天天看點

靈活軟體開發學習筆記前言第I部分 靈活開發第II部分 靈活設計第III部分 薪水支付案例研究

靈活軟體開發學習筆記

邱志軍 于2008年初

賽爾軟體有限公司(籌)

前言

靈活開發(AD)是一種面臨快速變化的需求快速軟體開發的能力。擷取靈活性的三要素:

1.         提供必要的紀律和回報的實踐

2.         保持軟體靈活、可維護的設計原則

3.         針對特定問題平衡設計原則的設計模式

第I部分 靈活開發

       人與人之間的互動是複雜的,并且其效果從來都難以預期,但卻是工作中最為重要的方面。原則、模式、實踐都很重要,但是使它們發揮作用的是人。人不是“插入即相容的程式設計裝置”,必須建構起有合作精神、自組織的團隊。

第一章     靈活實踐

失控的過程膨脹源于對項目失敗的恐懼,在這種恐懼之下過程變得越來越龐大!!!

第一節 靈活聯盟

2001年靈活聯盟釋出靈活聯盟宣言,聲明其價值觀和原則。宣言的具體内容如下:

個體和互動勝過過程和工具

1.         人是獲得成功的最為重要的因素

2.         合作、溝通以及互動能力遠比單純的程式設計能力更為重要

3.         使用過多龐大、笨重的工具就像缺少工具一樣,都是不好的

4.         團隊的建構比環境的建構重要的多

可以工作的軟體勝過面面俱到的文檔

1.         過多的文檔比更少的文檔更糟

2.         編寫并維護一份系統原理和結構方面的文檔

3.         給新的團隊成員傳遞知,最好的兩份文檔是代碼和團隊

4.         直到迫切需要并意義重大時,才編制文檔

客戶合作勝過合同談判

1.         成功的項目需要有序、頻繁的客戶回報

2.         指明了需求、進度以及項目成本的合同存在根本的缺陷

響應變化勝于遵循計劃

1.         響應變化的能力常常決定一個軟體項目的成敗

2.         計劃不能考慮的太遠

3.         較好的計劃政策:為下兩周做詳細的計劃,為下三個月做粗略的計劃,再以後就做極為粗糙的計劃

第二節 原則

從上述價值觀中引出下面12條原則,它們是靈活實踐差別于重型過程的特征所在。

我們最優先要做的是盡早的、持續的傳遞有價值的軟體使客戶滿意

1.         初期傳遞的系統功能越少,最終傳遞的系統的品質就越高

2.         靈活實踐會盡早的、經常的進行互動

即使到了開發的後期,也歡迎改變需求

1.         靈活過程利用變化來為客戶創造競争優勢

2.         靈活團隊會非常努力保持軟體結構的靈活性,以應對需求的變化

經常性的傳遞可以工作的軟體,傳遞的間隔可以從幾周到幾個月,傳遞的時間間隔越短越好

1.         不贊成傳遞大量的文檔或計劃

2.         我們關注的目标是傳遞滿足客戶需求的軟體

在整個項目開發期間,業務人員和開發人員必須天天都在一起工作

為了以靈活的方式進行項目的開發,客戶、開發人員和相關涉衆之間必須進行有意義的、頻繁的互動。

圍繞被激勵起來的個人來建構項目

1.         給他們提供所需的環境和支援,并且信任他們能夠完成工作

2.         在靈活項目中,人被認為是項目取得成功的最重要的因素

團隊中最有效果并富有效率的傳遞資訊的方式,就是面對面的交談

1.         靈活項目中,首要的溝通方式就是交談

2.         靈活團隊不需要書面的規範、計劃或者設計

工作的軟體是首要的進度度量标準

1.         靈活項目通過度量目前軟體滿足客戶需求的數量來度量開發進度

2.         隻有當30%的必修功能完成時,才可以确定進度完成了30%

靈活過程提倡可持續的開發速度

1.         責任人、開發者和使用者應該能夠保持一個長期、恒定的開發速度

2.         靈活項目不是50米短跑,而是馬拉松長跑

3.         跑得過快會導緻團隊精力耗盡,出現短期行為以緻崩潰

不斷的關注優秀的技能和好的設計會增強靈活能力

1.         高的産品品質是擷取高的開發速度的關鍵

2.         保持軟體盡可能的簡潔、健壯是快速軟體開發的途徑

簡單——使未完成的工作最大化的藝術——是根本的

1.         靈活團隊不會建構華而不實的系統,更願意采用和目标一緻的最簡單的方法

2.         在今天以最高的品質完成最簡單的工作

最好的構架、需求和設計出自于自組織的團隊

1.         靈活團隊是自組織的團隊

2.         靈活團隊的成員共同來解決項目中所有方面的問題

每隔一段時間,團隊會檢討如何更有效的工作

1.         靈活團隊會不斷的對團隊組織方式、規則、規範、關系等進行調整

2.         為保持團隊的靈活性,團隊必須随時間一起變化

第三節 結論

       靈活軟體開發的原則和價值觀構成了一個可以幫助團隊打破過程膨脹循環的方法,這個方法關注的是可以達到團隊目标的一些簡單技術。已經有許多靈活過程可供選擇,包括:SCRUM、FDD、ADP以及XP。

第二章     極限程式設計概述

作為開發人員,我們應該記住,極限程式設計并非唯一選擇。

第一節 極限程式設計實踐

       極限程式設計是靈活方法中最著名的一個,由一系列簡單卻互相依賴的實踐組成。

客戶作為團隊成員

1.         客戶和開發人員一起緊密的工作,彼此了解對方面臨的問題,并共同解決這些問題

2.         XP團隊中的客戶是指定義産品特性并排列這些特性優先級的個人或團體

3.         最好情況是客戶和開發人員在一起工作

使用者素材

1.         進行項目計劃必修知道項目需求有關的内容,但無需知道太多,做到能夠估算即可

2.         需求的特定細節會随着時間而改變

3.         在XP中,我們和客戶反複溝通以擷取對需求細節的了解,但不捕獲那些細節

4.         使用者素材是需求談話的助記符,可以根據它的優先級和估算代價安排時間

短傳遞周期

       XP每兩周傳遞一次可以工作的軟體,實作涉衆的一些需求。

疊代計劃

1.         疊代計劃是一次較小的傳遞,可能會被加入産品中,也可能不會

2.         由客戶依據開發人員的預算而選擇的使用者素材組成

3.         開發人員依據以前疊代完成的工作量為本次疊代設定預算

4.         一旦疊代開始,客戶就同意不修改使用者素材的定義和優先級

釋出計劃

1.         釋出計劃是一次較大的傳遞,通常會被加入産品中

2.         由客戶依據開發人員的預算而選擇的、排好優先級的使用者素材組成

3.         開發人員依據以前釋出完成的工作量為本次疊代設定預算

4.         釋出計劃不是一成不變的,使用者随時可以改變計劃的内容

驗收測試

1.         可以用客戶指定的驗收測試的測試的形式捕獲使用者素材的細節

2.         驗收測試用能夠使他們自動、反複運作的腳本語言編寫

3.         驗收測試驗證系統按照使用者指定的行為運轉

4.         一旦一項測試通過則加入驗收測試集,并絕不允許該測試再次失敗

結對程式設計

1.         所有的産品代碼都是有結對的程式員使用同一台計算機共同完成

2.         兩人頻繁互換角色

3.         結對關系每天至少改變一次

4.         結對程式設計催進知識在團隊中的傳播

5.         結對非但不會降低團隊效率,而且會大大減少缺陷率

測試驅動的開發方法

1.         編寫所有産品代碼的目的是使失敗的單元測試通過

2.         編寫測試用例和代碼之間的更疊速度是很快的,基本上幾分鐘左右

3.         測試用例集和代碼一起發展起來

4.         為使測試用例通過而編寫代碼時,編出的代碼是可測試的、松耦合的代碼

集體所有權

1.         結對程式設計中的每一對都具有簽出任何子產品,并對其修改的權利

2.         集體所有權并不意味着不需要專業知識

持續內建

1.         XP團隊使用非阻塞式的源代碼控制工具

2.         結對人員會在一項任務上工作1-2小時

可持續的開發速度

1.         軟體項目不是全速的短跑,而是馬拉松長跑

2.         XP的規則是不允許團隊加班工作,版本釋出前一個星期除外

開放的工作空間

1.         在充滿積極讨論的屋子裡工作,生産效率非但不會降低,反而會成倍的提高

2.         團隊在一個開發的環境中一起工作

計劃遊戲

1.         計劃遊戲的本質是劃分業務人員和開發人員的職責

2.         業務人員決定特性的重要性

3.         開發人員決定實作一個特性所花費的代價

簡單的設計

1.         XP團隊使他們的設計盡可能簡單、具有表現力

2.         僅僅關注本次疊代中的使用者素材

3.         以盡可能簡單的方式實作使用者素材,迫切需要基礎結構時才引入基礎結構

考慮能夠工作的最簡單的事情

1.         盡可能尋早能夠實作目前使用者素材的最簡單的設計

2.         能夠用平面檔案,就不用資料庫

3.         能夠用Socket就不用ORB或RMI

4.         能夠不用多線程就不用多線程

你将不需要他

隻有在有證據,或有明顯的迹象表明引入基礎結構比繼續等待合算時,才引入基礎結構

有且僅有一次

1.         極限程式設計者不能容忍備援的代碼

2.         消除重複最好的方法就是抽象

重構

1.         代碼往往會腐化

2.         XP團隊通過經常性的重構來扭轉這種退化

3.         重構之後要測試通過

4.         重構是持續進行的,持續保持幹淨、簡單并具有表現力的代碼

隐喻

1.         隐喻是将系統聯系在一起的全局視圖

2.         隐喻通常可以歸納成一個名字系統

第二節 結論

極限程式設計是一組簡單、具體的實踐,這些實踐組合在一起形成一個靈活開發過程。

第三章     計劃

當你能夠度量你所說的,并且能夠用數字去表達它時,就表示你了解了它;若你不能度量它,不能用數字去表達它,那麼說明你的知識是匮乏的,不能令人滿意。

第一節 初始探索

1.         項目開始時,開發人員和客戶會盡量确定出所有真正重要的使用者素材

2.         開發人員共同對這些素材進行估算,估算出相對的“點數”

探究、分解和速度

1.         過大或過小的素材都是難以估算的

2.         當分割或合并一個素材時,應該對其重新進行估算

3.         估算沒有給出使用者素材的絕對大小,需要引入一個稱為速度的因子

4.         随着項目進展,對速度的度量會越來越準确

5.         項目開始時,通過稱為探究的原型化過程了解速度

第二節 釋出計劃

1.         如果知道了開發速度,客戶對每個素材的成本就能夠有所了解

2.         客戶也知道每個素材的商業價值和優先級别

3.         讓業務人員來選擇那些會給他們帶來最大利益的素材

4.         開發人員同客戶對項目的首次釋出時間達成一緻,通常在2-4個月後

5.         當速度變得更準确時,可以再對釋出計劃進行調整

第三節 疊代計劃

1.         開發人員和客戶共同決定疊代規模,一般為兩周

2.         疊代期使用者素材的實作順序屬于技術決策範疇

3.         一旦疊代開始,使用者就不能改變疊代期内需實作的素材

4.         即使沒有完成所有的使用者素材,也應該在計劃指定日期結束疊代

5.         為疊代做計劃采用的速度,就是前一次疊代測算出的速度

6.         這樣的速度回報有利于保持計劃同實際狀況同步

第四節 任務計劃

1.         開發人員把素材分解為開發任務

2.         開發人員在客戶的幫助下,盡可能列出所有的任務

3.         一個任務就是開發人員能夠在4-16小時内實作的一些功能

4.         可以在活動挂圖、白闆等友善的媒介上列出任務

5.         開發人員可以簽訂任意類型的任務

6.         開發人員以上次疊代完成的任務點數簽訂本次的任務點數

7.         任務的選擇、配置設定過程是一個動态調整的過程

疊代的中點

1.         當疊代進行到一半時,會召開一次會議

2.         在此刻,半數的素材點數應該完成,否則重新配置設定任務和職責

第五節 疊代

1.         每兩周本次疊代結束,下次疊代開始

2.         每次疊代結束,給客戶示範可以運作的程式,收集回報

3.         客戶為下次疊代提供新的素材

4.         客戶可以經常看到項目的進展,度量開發速度

第六節 結論

1.         通過一次次的疊代、釋出,項目進入一種可以預測的、舒适的開發節奏

2.         開發人員基于自己的估算,自己度量開發速度,制定合理的計劃

3.         管理人員從每次疊代中擷取資料,控制和管理項目

第四章     測試

編寫單元測試是一種驗證行為,更是一種設計行為,更是一種編寫文檔的行為。此外,編寫單元測試避免了相當數量的回報循環,尤其是功能驗證方面的回報循環。

第一節 測試驅動的開發方法

如果我們能夠在設計程式前事先設計測試方案,情況會怎樣?

如果我們能夠做到:除非缺乏某個功能将導緻測試失敗,否則拒絕在程式中實作該功能,情況會怎麼樣?

如果我們能夠做到:除非缺乏某行代碼将導緻測試失敗,否則拒絕在程式中增加哪怕一行代碼,情況會怎麼樣?

如果首先編寫失敗的測試以表明需要某項功能,然後再逐漸的增加哪項功能使測試通過,情況會怎麼樣?

測試驅動的開發方法帶來的好處有:

l         程式中的每一項功能都有測試來驗證它操作的正确性

l         首先編寫測試,可以迫使我們使用不同的觀查點

l         首先編寫測試,可以迫使我們把程式編寫成可測試的

l         首先編寫的測試,可以作為一種無價的文檔形式

一個測試優先設計的範例

按照便于我們閱讀的方式編寫測試,然後按照測試所暗示的結構去編寫程式,這稱為有意圖的程式設計。在實作之前,現在測試中陳述你的意圖,使你的意圖盡可能簡單、易讀。

1.         最初的測試指出了一個好的解決問題的方法

2.         測試在非常早的階段,闡明了一個重要的設計問題

3.         首先編寫測試的行為就是在各種設計決策中辨識的行為

測試促使子產品之間隔離

       在編寫産品代碼之前,先編寫測試常常會暴露出程式中應該被解藕的區域。對類進行單元測試時,往往要同時測試與其協作的類,這時就需要隔離其協作類進行單獨的測試。可以通過MOCK OBJECT模拟對象模式,在類及其協作者間插入接口,并建立實作這些接口的測試樁(TEST STUB)。

意外獲得的解耦合

1.         通過插入接口,我們獲得了實作類的互換能力

2.         這種互換即是為了測試,也是為了應用的擴充性

3.         在編寫代碼前編寫測試改善了設計

第二節 驗收測試

       作為驗證工具來說,單元測試是必須的,但不夠充分。單元測試用來驗證系統的小的組成單元按照所期望的方式工作,但它沒有驗證系統作為一個整體的正确性。

1.         驗收測試由不了解系統内部機制的人編寫

2.         驗收測試是關于一項特性的最終的文檔

3.         編寫驗收測試的行為對系統的構架方面具有深遠的影響

驗收測試示例

我們應該以我們認為驗收測試應該的樣子去編寫驗收測試,然後構造腳本語言,并根據腳本語言的結構來構造系統。

意外獲得的構架

第三節 結論

1.         測試套件運作得越簡單,就會越頻繁的運作它們

2.         單元測試、驗收測試都是一種文檔形式,而且是可以編譯執行的

3.         測試最重要的好處就是它對構架和設計的影響

第五章     重構

大千世界中,唯一缺乏的就是人類的注意力。

Martin Fowler将重構定義為:在不改變代碼外在行為的前提下對代碼做出修改,以改進代碼内部結構的過程。為什麼要改變軟體子產品的内部結構呢?這要從軟體子產品的三項職責說起:

1.         第一個職責是它運作起來所完成的功能

2.         第二個職責是它要應對變化

3.         第三個職責是要和閱讀它的人進行溝通

第一節     一個重構示例(略)

第二節     結論

程式變得更易了解,是以也更易更改,且程式結構的各部分之間互相隔離,這也使它更易更改。重構就好比用餐後對廚房的清潔工作,重構的目的是每天清潔你的代碼。

第六章     一次程式設計實踐

設計和程式設計都是人的活動,忘記這一點,将會失去一切。

第II部分 靈活設計

       在靈活團隊中,全局視圖和軟體一起演化。在每次疊代時,團隊改進系統設計,使設計盡可能适合于目前系統。

拙劣設計的症狀

l         僵化性(Regidity):設計難以改變

l         脆弱性(Fragility):設計易于遭到破壞

l         牢固性(Immobility):設計難以重用

l         粘滞性(Viscosity):難以做正确的事

l         不必要的複雜性(Needless Complexity):過分設計

l         不必要的重複(Needless Repetition):濫用滑鼠

l         晦澀性(Opacity):混亂的表達

原則

       面向對象設計的基本原則:

l         單一職責原則(SRP)

l         開放——封閉原則

l         Liskov替換原則

l         依賴倒置原則

l         接口隔離原則

臭味和原則

       設計中的臭味是一種症狀,是可以主觀進行度量的。這些臭味常常是出于違反了這些原則中的一個或多個而導緻的,靈活團隊應用這些原則去除異味。

第七章     什麼是靈活設計

在按照我的了解方式審查了軟體開發的生命周期後,我得出一個結論:實際上滿足工程設計标準的唯一軟體文檔,就是源代碼清單。

                                                                                               ­———Jack Reeves

第一節     軟體出了什麼錯

剛開始時,你能夠獲得系統的清晰視圖,接着事情開始變遭,軟體像一片壞面包一樣開始腐化。最後,即使僅僅進行最簡單的更改,也需要花費巨大的努力。管理人員開始強烈的要求進行重新設計,然而這樣的重新設計很少成功。

第二節     設計的臭味——腐化軟體的氣味

當軟體出現下面任何一種氣味時,就表明軟體正在腐化。

l         僵化性(Regidity):很難對系統進行改動,因為每個改動都會迫使對系統其他部分的其他改動

l         脆弱性(Fragility):對系統的改動會導緻系統中和改動的地方在概念上無關的許多地方出現問題

l         牢固性(Immobility):很難解開系統的糾結,使之成為一些可以在其他系統中重用的元件

l         粘滞性(Viscosity):做正确的事情要比做錯誤的事情困難

l         不必要的複雜性(Needless Complexity):設計中包含不具有直接好處的基礎結構

l         不必要的重複(Needless Repetition):設計中包含重複的結構,而該重複的結構本可以使用單一的抽象進行統一

l         晦澀性(Opacity):很難閱讀、了解,沒有很好的表現出意圖。

什麼激發了軟體的腐化

       在非靈活的環境中,由于需求沒有按照初始設計預見的方式進行變化,進而導緻設計的退化。然而,我們不能因為設計的退化而責怪需求的變化。我們必須設法找到一種方法,使得設計對于這種變化就有彈性,并且應用一些實踐來防止設計腐化。

靈活團隊不允許軟體腐化

       靈活團隊依靠變化擷取活力。他們更願意保持系統設計盡可能的簡單、幹淨,并使用單元測試、驗收測試做為支援。

第三節     “Copy”程式

靈活開發人員知道要做什麼,是因為:

1)        他們遵循實踐去發現問題

2)        他們應用設計原則去診斷問題

3)        他們應用适當的設計模式去解決問題

軟體開發的這三個方面的互相作用就是設計。

第四節     保持盡可能好的設計

靈活開發人員緻力于保持設計盡可能的适當、幹淨。他們從來不說“稍後我們會回來修正他們”。設計必須要保持幹淨、簡單,并且由于源代碼是設計最重要的表示,是以它同樣要保持幹淨。

第五節     結論

靈活設計是一個過程,不是一個事件,它是一個持續的應用原則、模式以及實踐來改進軟體的結構和可讀性的過程。它緻力于保持系統設計在任何時候都盡可能簡單、幹淨以及富有表現力。

第八章     單一職責原則(SRP)

隻有佛自己應當擔負起公布玄妙秘密的職責。

                                                                                                            ——E Brewer

單一職責原則,最初也被稱為内聚性——一個子產品的組成元素之間的功能相關性。

第二節     單一職責原則

就一個類而言,應該僅有一個引起它變化的原因。每一個職責都是變化的一個軸線,當需求變化時,該變化會反映為類的職責的變化。如果一個類承擔的職責過多,就等于把這些職責耦合在一起,這種耦合會導緻脆弱的設計。通常對于單一職責的違反可能會導緻一些嚴重的問題。

什麼是職責?

       在SRP中,我們把職責定義為變化的原因,如果你能夠想到多于一個的動機去改變一個類,那麼這個類就有了多于一個的職責。

分離耦合的職責

       常常會有一些和硬體或者作業系統有關的原因,迫使我們把不願耦合的東西耦合在一起。然而,對于其他部分來說,通過分離他們的接口我們已經解耦了概念。

持久化(違反SRP範例)

第二節     結論

SRP是是以原則中最簡單的,也是最難以正确應用的,實際上其餘原則都是以這樣或那樣的方式回答SRP要解決的問題。

第九章     開發——閉合原則(OCP)

任何系統在其生命周期中都會發生變化,如果我們期望開發出的系統不會在第一版後就抛棄,尤其需要注意這一點。

第一節 開發——閉合原則(OCP)

軟體實體應該是可以擴充的,但是不可以修改。如果程式中的一處改動就會産生連鎖反應,那麼設計就有僵化性的臭味,依舊OCP我們應當對代碼進行重構。

第二節 描述

遵循開閉原則設計的子產品具有以下兩種特征:

1.         對于擴充是開放的:這意味着子產品的行為是可以擴充的

2.         對于更改是封閉的:對子產品擴充時,不必改變子產品的源代碼或二進制代碼。

第三節     關鍵是抽象

子產品可以操作一個抽象體,由于子產品依賴一個固定的抽象體,是以它對于更改可以是封閉的,通過從這個抽象類派生,也可以擴充此子產品的行為。

Shape應用程式(略)

預測變化和貼切的結構

如果我們預測到這種抽變化,就可以設計一個抽象來隔離它。但是,這導緻了一個麻煩的結果,一般而言,無論子產品設計的多麼封閉,都存在一些無法對之封閉的變化。這樣我們就得預測變化的發生,并且一直等到變化發生時才采取行動。

放置吊鈎

       過去我們常常通過放置吊鈎的方式預先隔離變化,然而放置吊鈎常常是錯誤的。我們不希望背着許多不需要的抽象,直到真正需要時才放置進去。

l         隻受一次愚弄:我們最初編寫代碼時,假設變化不會發生,當變化發生時,我們就建立抽象來隔離以後發生的同類變化

l         刺激變化:我們希望在開放後不久就知道可能發生的變化,是以我們需要去刺激變化

使用抽象擷取顯式封閉

第四節     結論

在許多方面,OCP都是面向對象設計的核心所在,遵循這個設計可以帶來面向對象技術所聲稱的諸多好處。

第十章     Liskov替換原則(LSP)

OCP背後的主要機制是抽象和多态,在JAVA中這主要是通過繼承來實作。但是,是什麼設計規則支配着這種特殊繼承用法呢?

——Liskov替換原則

第一節     Liskov替換原則

對LSP可以做如下解釋:子類型必須能夠替換掉他們的基類型。

第二節     一個違反LSP的簡單例子

對于LSP的違反常常會導緻以明顯違反OCP的方式使用RTTI。

第三節     更微妙的違規

我們經常說繼承是一個IS -A關系,這種關系有時會導緻一些問題。如果新建立的派生類會導緻改變基類,這常常意味着設計存在缺陷。

真正的問題

       新建立的子類違反了父類的不變性,進而違反了LSP原則。

有效性并非本質屬性

       LSP讓我們得出一個非常重要的結論:一個模型,如果孤立的看,并不具有真正意義上的有效性。模型的有效性隻能通過它的客戶程式來展現,客戶程式往往對子產品作出一定的合理假設,有效性建立在對這種假設的預測之上。

IS_A是關于行為的

       從行為方式的角度來看,對象的行為方式才是軟體真正所關注的問題。LSP清楚的指明,OOD中IS_A關系是關于行為的,行為方式是可以合理假設的,是客戶程式所依賴的。

基于契約設計

       怎樣才能知道客戶的真正需求呢?有一項技術可以使這些合理的需求明确化——DBC基于契約的設計。使用DBC,類的編寫者顯式的規定對該類的契約,契約通過為每個方法聲明前置條件和後置條件指定。按照Mayer所述,派生類的前置條件和後置條件規則是:在重寫基類的方法時,隻能使用相等或更弱的前置條件,隻能使用相等或更強的後置條件。

在單元測試中指定契約

第四節     一個實際的例子(略)

第五節     用提取公共部分的方法代替繼承

第六節     結論

OCP是OOD中許多說法的核心,LSP是是OCP成為可能的主要原則之一。

第十一章 依賴倒置原則

       絕不能再讓國家的重大利益依賴于那些會動搖人類薄弱意志的衆多可能性。

——Thomas Noon

第一節 依賴倒置原則(DIP)

l         高層子產品不應該依賴底層子產品,兩者都應該依賴于抽象

l         抽象不應該依賴于細節,細節應該依賴于抽象

倒置,倒置的到底是什麼呢?許多傳統的軟體開發方法,如面向結構的分析和設計,總是傾向于建立一些高層子產品依賴底層子產品、政策依賴于細節的軟體結構。而一個面向對象的程式,其軟體結構的依賴關系顯然是倒置了。

高層子產品包含了一個應用程式中重要的政策選擇和業務模型,無論如何高層子產品都不應該依賴底層子產品,否則底層子產品的改動勢必影響高層子產品。我們更希望重用的是高層的政策設定子產品,DIP原則是架構設計的核心原則。

第二節     階層化

Booch曾經說過:所有結構良好的面向對象構架都具有清晰的層次定義,每個層都通過一個定義良好的、受控的接口向外提供一組内聚的服務。然而,對這句話了解不當的話,很容易就做出違反DIP原則的設計。其實,更為合理的設計應該是:每個較高層次都為它所需要的服務聲明一個抽象接口,較低的層次實作了這些接口,每個高層類都通過抽象接口使用下一層,這樣高層子產品就不依賴底層子產品。這樣,倒置不僅是依賴關系的倒置,也是接口所有權的倒置,當應用DIP原則時,我們發現往往是他們的客戶擁有抽象接口,而他們的服務者則從這些抽象接口派生。

倒置的接口所有權

       底層子產品實作了在高層子產品中聲明的并被高層子產品調用的抽象接口——這就是Hollywood原則。通過這種倒置的接口所有權,我們建立了一個更靈、更持久、更易改變的結構。

依賴于抽象

       依賴倒置的簡單啟發式規則:依賴于抽象,該規則建議不要依賴于具體類——也就是說程式中所有依賴關系都應終止于抽象類或接口。依據這個啟發式規則,我們可以得出如下結論:

1.         任何變量都不應該持有一個指向具體類的指針或引用

2.         任何類都不應該從具體類派生

3.         任何方法都不應該複寫它的任何基類中的已經實作的方法

第三節 一個簡單的例子

依賴倒置可以應用在任何存在一個類向另一個類發送消息的地方。違反DIP原則,高層政策自動依賴于底層子產品,抽象就自動的依賴于具體細節。

找出潛在的抽象

       什麼是高層政策呢?它是應用背後的抽象,是那些不随具體細節改變而改變的真理,是系統内部的系統——隐喻。通常,遵循DIP原則的設計中的依賴隻是名字上的概念,有時抽象的接口沒有所有者,在JAVA中這樣的接口一般放在單獨的一個包中。

第四節 熔爐示例(略)

第五節     結論

使用傳統的過程化程式設計所建立的依賴關系結構,政策是依賴于細節的。面向對像的程式設計倒置了這種依賴。依賴倒置原則是實作面向對象技術承諾的好處的基本底層機制,對于建立可重用的架構是必須的。

第十二章 接口隔離原則(ISP)

       接口隔離原則用來處理FAT接口的缺點,如果一個類的接口不是内聚的,就表示該類具有胖的接口。客戶程式看到的應該是多個具有内聚接口的抽象基類。

第一節     接口污染

在繼承層次中解決接口依賴問題,往往會導緻基類中的接口變胖——最終違反接口隔離原則。

第二節     分離客戶就是分離接口

既然客戶程式是分離的,那麼接口也應該保持分離。因為,客戶程式對他們使用的接口有作用力。

客戶對接口施加的反作用力

第三節     接口隔離原則(ISP)

不應該強迫客戶依賴于他們不用的方法,否則就容易導緻所有客戶程式間不必要的耦合。我們希望盡量避免這種耦合,是以我們希望分離接口。

第四節     類接口和對象接口

一個對象的客戶不是必須通過該對象的接口去通路它,也可以通過委托或對象的基類去通路它。

使用委托分離接口

使用多重繼承分離接口

第五節     ATM使用者界面的例子(略)

第六節     結論

胖類會導緻它們的客戶程式之間産生不正常的并且有害的耦合關系。通過把胖類的接口分解為多個特定于客戶程式的接口,這就解除了客戶程式和他們沒有用到的方法間的依賴關系,并使客戶程式之間互不依賴。

第III部分 薪水支付案例研究

第十三章 COMMAND模式和ACTIVE OBJECT模式

沒有人天生就有指令他人的權利。——Denis

       COMMAND模式是最簡單、最優雅的模式之一,然而這種簡單性具有欺騙性。COMMAND模式僅有帶有一個方法的接口組成,該模式橫過了一個非常有趣的界限。

第一節 簡單的COMMAND

       通過對指令概念的封裝,該模式解除了系統的邏輯互聯關系同實際連接配接的實體裝置之間的耦合。

第二節 事務操作

       另一個COMMAND模式的常見用法是建立和執行事務操作。

實體上解耦

       通過建立和執行事務操作,解除了擷取使用者資料的代碼、驗證并操作資料的代碼以及業務對象本省之間的耦合關系。

時間上解耦

       通過建立事務對象,我們可以将事務置于一個清單中,以後在進行事務的相關處理,達到時間上解耦的目的。

第三節 UNDO撤銷

       如果Command派生類知道do方法可以知道它所執行的方法的細節,那麼undo方法就可以取消這些操作,并把狀态恢複到以前的狀态。記住,這時do方法一般要保留一定的操作狀态資訊,以供undo方法使用。

第四節 ACTIVE OBJECT模式

       活動對象模式是實作多線程控制的一項古老技術,通常通過一個指令連結清單來實作。

第五節 結論

       Command模式的簡單性掩蓋了其對功能性,Command模式可以應用于多種不同的美妙用途,範圍設計資料庫事務操作,多線程核心,裝置控制以及GUI的do/undo管理。

第十四章 模闆方法和政策模式:繼承和委托

繼承關系蘊含的意義是非常深遠的,使用繼承我們可以基于差異程式設計。然而,繼承非常容易被過度使用,并且過度使用的代價非常高。是以,我們推薦優先使用組合,而不是類繼承。

本章介紹了模闆方法模式和政策模式,他們解決的問題類似,不過模闆方法模式使用繼承解決問題,而政策模式使用的則是委托。

第一節 模闆方法模式