《敏捷軟體開發(原則、模式與實踐)》第四章到第八章筆記

elevenxl發表於2008-02-26
第4章 測試

            烈火驗真金,逆境磨意志。
                        ——盧修斯.塞尼加(公元前4-公元65)

1 測試驅動的開發方法
    測試驅動的方法對於我們正在編寫的軟體的設計有積極的影響:
    (1) 程式中的每一項功能都有測試來驗證它的操作的正確性。
    (2) 首先編寫測試可以迫使我們使用不同的觀察點,可以設計出便於呼叫的軟體
    (3) 通過首先編寫測試,可以迫使自己把程式設計為可測試的,迫使我們解除軟體中的耦合(force us to decouple the software)
    (4) 測試可以作為一種無價的文件形式
    我們相信,只要按照測試所暗示的結構去編寫程式,就能通過測試,這種方法成為有意圖的程式設計(intentional programming)。
    測試促使模組之間隔離和解耦合
   
2 驗收測試
    對於驗證工具來說,單元測試是必要的,但是不夠充分。單元測試用來驗證系統的小的組成單元應該按照所期望的工作方式,但是它們沒有驗證系統作為一個整體時工作的正確性。單元測試是用來驗證系統中個別機制的白盒測試(white-box tests)。驗收測試是用來驗證系統滿足客戶需要的黑盒測試(black-box tests)。
    驗收測試由不瞭解系統內部機制的人編寫。客戶可以直接或者和一些技術人員(比如QA等)一起來編寫驗收測試。驗收測試是程式,因此是可以執行的。然而,通常使用專為應用程式的客戶建立的指令碼語言來編寫驗收測試。  在這裡不能由系統程式碼編寫人員來編寫驗收測試!
    驗收測試是關於一項特性(feature)的最終文件。  單元測試作為可編譯、執行的有關係統內部結構的文件,而驗收測試是有關係統特性的可編譯、執行的文件。
    此外,首先編寫驗收測試的行為對於系統的架構方面具有深遠的影響。為了使系統具有可測試行,就必須要在很高的系統架構層面對系統進行解耦合。
    驗收測試可以促使你在大的方面做出優良的系統架構決策。
    建立一個驗收測試框架(framework)看起來是件困難的任務。然而,如果僅僅建立框架中對單個迭代包含的特性進行驗收測試所需要的那部分,就會發現並不困難。你還會發現所花費的努力是值得的。  在這裡可以看到,每次迭代都需要進行驗收測試,而且這樣的工作量也不會太大。
    我們還沒有編寫任何程式碼,也沒有進行任何設計。這是開始考慮驗收測試的的最好時機,揭示意圖程式設計再一次成為有用的工具。我們應該以我們認為驗收測試應該的樣子去編寫它們,然後可以構建指令碼語言,並根據指令碼語言的結構來構造所將要開發的系統。   (這一點是非常值得我們在專案開發過程中借鑑的,當沒有開始對系統進行設計和程式碼編寫的時候就優先對系統進行驗收測試的考慮和用指令碼語言來構造驗收測試,這樣就可以迫使我們去讓我們即將設計和開發的系統按照我們所構想的驗證測試來實現程式碼的編寫,對於設計和架構都是非常有幫助的!)
    為使系統驗證便於編寫並易於改變。把它們放置在一個配置管理工具中,並且把它們儲存起來以便於隨時可以執行它們。因此,採用簡單的文字檔案來編寫驗收測試應該比較合理。

3 結論
    單元測試和驗收測試都是一種文件形式,那樣的文件是可以編譯和執行的;因此,它是準確和可靠的。此外,編寫測試所使用的語言是明確的,並且它們的觀看者使這些語言非常易讀。程式設計師能夠閱讀單元測試,因此單元測試是使用程式設計師程式設計的語言編寫的。客戶能夠閱讀驗收測試,因為驗收測試是使用客戶自己設計的語言編寫的。
    也許,測試最重要的好處就是它對架構和設計的影響(也的確如此)。為了使一個模組或者應用程式具有可測試性,就必須要對它進行解耦合。越是具有可測試性,耦合關係就越弱。全面地考慮驗收測試和單元測試的行為對軟體的結構具有深遠的正面影響。



第5章 重構
   
            大千世界中,惟一缺乏的就是人類的注意力。
                            ——凱文.凱利(“新經濟”的首席預言家之一),連線雜誌

    在Martin Fowler的名著《重構》一書中,他把重構(Refactoring)定以為:“……在不改變程式碼外在行為的前提下對程式碼做出修改,以改進程式碼的內部結構的過程。”
    每個軟體模組都有三項職責。第一個職責是它執行起來所完成的功能。這也是該模組得以完成的原因。第二個職責是它要應對變化。幾乎所有的模組在它們的生命周其中都要變化。開發者有責任保證這種改變應該儘可能地簡單。第三個職責是要和閱讀它的人進行溝通。對該模組不熟悉的開發人員應該能夠比較容易地閱讀並理解它。

1 素數產生程式:一個簡單的重構示例
    例子略

2 結論
    重構後的程式讀起來比一開始要好得多,程式工作得也更好一些。程式可以變得更容易被理解,且程式結構的各部分之間互相隔離,這也使它更容易更改。
    重構的目的,是為了每天清潔你的程式碼。我們想通過最小的努力就能夠對我們的系統進行擴充套件和修改,要想具有這種能力,最重要的就是要保持程式碼的清潔。



第6章 一次程式設計實踐

        設計和程式設計都是人的活動,忘記了這一點,將會失去一切。
                                ——Bjarne Stroustrup, 1991   
   
    在本章中作者給出了一次程式設計實踐,一個保齡球的程式,用一個例項給出了結隊程式設計的詳細情景,比較經典!
   




第二部分 敏捷設計

    在敏捷團隊中,全域性檢視和軟體一起演化。在每次迭代中,團隊改進系統設計,使設計儘可能適合於當前系統。團隊不會花費許多實踐去預測未來的需求和需要,也不會試圖在今天就構建一些基礎結構去製成那些他們認為明天才會需要的特性。他們更願意關注當前的系統結構,並使它儘可能地好。

    拙劣的設計症狀定義:
    僵化性(Rigidity):設計難於改變
    脆弱性(Fragility):設計易於遭到破壞
    牢固性(Immobility):設計難於重用
    粘滯性(Viscosity):難以做正確的事情
    不必要的複雜性(Needless Complexity):過分設計
    不必要的重複(Neddless Repetition):濫用滑鼠
    晦澀性(Opacity):混亂的表達

    一些物件導向設計原則:
    單一職責原則(The Single Responsibility Principle,簡稱SRP)
    開放-封閉原則(The Open-Close Principle,簡稱OCP)
    Liskov替換原則(The Liskov Substitution Principle,簡稱LSP)
    依賴倒置原則(The Dependency Inversion Principle,簡稱DIP)
    介面隔離原則(The Interface Segregation Interface,簡稱ISP)

    敏捷團隊應用這些原則來除去臭味。當沒有臭味時,他們不會應用這些原則。僅僅因為是一個原則就無條件的去遵守它的做法是錯誤的。這些於原則不是可以隨意在系統中到處噴灑的香水。過分遵循這些原則會導致不必要的複雜性(Needless Complexity)的設計臭味。

第7章 什麼是敏捷設計

        “在按照我的理解方式審查了軟體開發的生命週期後,我得出一個結論:實際上滿足工程設計標準的惟一軟體文件,就是原始碼清單。”
                                                                                            ——Jack Reeves

1 設計的臭味——腐化軟體的氣味
(1)僵化性 僵化性是指難以對軟體進行改動,即使簡單的改動。如果單一的改動會導致有依賴關係的模組中俄連鎖改動,那麼設計就是僵化的。必須要改動的模組越多,設計就越僵化。
(2)脆弱性 脆弱性是指,在進行一個改動時,程式的許多地方就可能出現問題。常常是,出現新問題的地方與改動的地方並沒有概念上的關聯。要修正這些問題就會引出更多的問題,從而使開發團隊就像一隻不停追逐自己尾巴的狗。   呵呵,這個形容真的很貼切
(3)牢固性 牢固性是指,設計中包含了對其他系統有用的部分,但是要把這些部分從系統中分離出來所需要的努力和風險是巨大的。這是一件令人遺憾的事,但是確實非常常見的事情。
(4)粘滯性 粘滯性有兩種表現形式:軟體的粘滯性和環境的粘滯性。
        當那些可以保持系統設計的方法比那些生硬手法更難應用的時候,就表明設計具有高的粘滯性。
        當開發環境遲鈍、低效時,就會產生環境的粘滯性。
        無論專案具有哪種粘滯性,都很難保持專案中的軟體設計。我們希望建立易於保持設計的系統和專案環境。
(5)不必要的複雜性 如果設計中包含有當前沒有用的組成部分,它就包含不必要的複雜性。
(6)不必要的重複 剪下和貼上也許是有用的文字編輯操作,但是它們卻是災難性的程式碼編輯操作。
    當同樣的程式碼以稍微不同的形式一再出現時謬表示開發人員忽視了抽象。對於它們來說,發現所有的重複並通過適當的抽象去消除它們的做法可能沒有高的優先順序別,但是這樣做非常有助於使系統更加易於理解和維護。
(7)晦澀性 晦澀性是指模組難以理解。
        為防止這種情況的發生,開發人員必須要站在程式碼閱讀者的位置,共同努力對它們的程式碼進行重構,這樣程式碼的閱讀者就可以理解程式碼。可以被其他人進行評審。

    大作數軟體專案中最不穩定的東西就是需求。需求處在一個持續變動的狀態之中。這是我們作為開發人員必須得接受的事實!我們生存在一個需求不斷變化的世界中,我們的工作是要保證我們的軟體能夠經受得住那些變化。如果我們軟體的設計由於需求變化而退化,那麼我們就不是敏捷的。
    敏捷團隊不是在一開始設計該模組時就試圖預測程式將如何變化。相反,他們是以最簡單的方法編寫的。直到需求最終確實變化時,他們才修改模組的設計,使之對該種變化保持彈性。
    簡單而言,敏捷開發人員知道要做什麼,是因為:
    (1)他們遵循敏捷實踐去開發問題;
    (2)他們應用設計原則去診斷問題;
    (3)他們應用適當的設計模式去解決問題。
    軟體開發的這三個方面間的互相作用就是設計。

    敏捷開發人員致力於保持設計儘可能地適當、乾淨。設計必須要保持乾淨、簡單,並且由於原始碼是設計最重要的表示,所以它同樣要保持乾淨。職業特性要求我們,作為軟體開發人員,不能忍受程式碼腐化。

2 結論
    敏捷設計是一個過程,不是一個事件。它是一個持續的應用原則、模式以及實踐來改進軟體的結構和可讀性的過程。它致力於保持系統設計在任何實踐都儘可能得簡單、乾淨以富有表現力。



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

        只有佛自己應當擔負起公佈玄妙祕密的職責。
                            ——E.Cobham Brewer, 1810-1897
                                        《英語典故字典》,1898

    這條原則曾經在Tom DeMaro和Meilir Page-Jones的著作中描述過,並稱之為內聚性(cohesion)。他們把內聚性定義為:一個模組的組成元素之間的功能相關性。

1 單一職責原則   
    就一個類而言,應該僅有一個引起它變化的原因。
    每一個職責都是變化的一個軸線(an axis of change)。當需求變化時,該變化會反映為類的職責的變化,如果一個類承擔了多餘一個的職責,那麼引起它變化的原因就會有多個。 如果一個類承擔的職責過多,就等於把這些職責耦合在了一起。一個值得的變化可能會削弱或者一直這個類完成其他職責的能力。這種耦合會導致脆弱的(fragile)設計,當變化發生時,設計會遭受到意想不到的破壞。
    在SRP中,我們把職責定義為“變化的原因”(a reason for change)。如果你能夠想到多餘一個的動機去改變一個類,那麼就具有多於一個的職責。

2 結論
    SRP是所有原則中最簡單的之一,也是最難正確運用的之一。我們會自然地把職責結合在一起。軟體設計真正要做的許多內容,就是發現職責並把那些職責互相分離。事實上,我們將要論述的其餘原則都會以這樣或那樣的方式回到這個問題上。

相關文章