Skip to content

Latest commit

 

History

History
127 lines (90 loc) · 5.63 KB

第 12 章 羽化 (Emergence) db992790cb7547a7849e7c4c8d1fde92.md

File metadata and controls

127 lines (90 loc) · 5.63 KB

第 12 章 羽化 (Emergence)

根據 Kent Beck 的說法,若遵循下列守則,一個設計就可以說是簡單設計

  • 執行完所有的測試
  • 沒有重覆的部份
  • 表達程式設計師的本意
  • 最小化類別和方法的數量

其中重要性由高到低排序。

執行完所有的測試

能在「所有的時間」都能通過「所有的測試」的系統,才是一種可測試系統。不能被測試的系統,代表無法被驗證,無法被驗證的系統,也就不應該進行部署

為了讓我們的系統能被測試,我們自然會走向小型、單一用途的類別。類別若能遵守單一職責原則 (SRP),測試就是一件簡單的事。

高耦合的程式碼,會造成寫測試程式的困難,所以當我們越常寫測試程式,我們就越會使用像相依性反向 (DIP) 之類的原則、相依性注入 (DI)介面或抽象概念等工具,來幫助我們最小化耦合度。

守則告訴我們要有測試程式,並持續地執行測試,我們遵守這個守則,會讓我們的系統保有物件導向的目標,讓程式有低耦合和高凝聚度。所以撰寫測試程式,最終會帶來更好的設計

簡單設計守則 2 ~ 4:程式重構

一旦我們有了測試程式,就能保持程式和類別的整潔,就不會有「整理程式碼會破壞程式碼」的恐懼。

在整個整理、重構程式碼的過程中,可以應該與良好軟體設計有關的所有知識,例如增加凝聚力、降低耦合度、分離關注點、模組化系統關注點、替函式和類別瘦身、選擇良好的名稱等。這些應用也都是在實踐最後三條簡單設計準則:消除重覆、具有表達力及最小化類別和方法的數量。

禁止重複

重複的程式碼代表了額外的工作、額外的風險及額外不必要的複雜性。想要有一個簡潔的系統,就要用「消除重複」的意願。

public void scaleToOneDimension(
    float desiredDimension,
    float imageDimension
) {
    if (Math.abs(desiredDimension - imageDimension) < errorThreshold)
        return;    

		float scalingFactor = desiredDimension / imageDimension;    
        
		scalingFactor = (float)(Math.floor(scalingFactor * 100) * 0.01f);   

		RenderedOp newImage = ImageUtilities.getScaledImage(
        image,
        scalingFactor,
        scalingFactor
    );
    image.dispose();
    System.gc();
    image = newImage;
}

public synchronized void rotate(int degrees) {
    RenderedOp newImage = ImageUtilities.getRotatedImage(
        image,
        degrees
    );
    image.dispose();
    System.gc();
    image = newImage;
}

為了讓這個系統更整潔,我們可以移除 scaleToOneDimension 跟 rotate 裡面少量的重複程式碼

public void scaleToOneDimension(
    float desiredDimension,
    float imageDimension
) {
    if (Math.abs(desiredDimension - imageDimension) <
        errorThreshold)
        return;    

		float scalingFactor = desiredDimension / imageDimension; 
		
		scalingFactor = (float)(Math.floor(scalingFactor * 100) * 0.01f);
        
		**replaceImage(
        ImageUtilities.getScaledImage(
            image,
            scalingFactor,
            scalingFactor
        )
    );**
}

public synchronized void rotate(int degrees) {
		**replaceImage(
        ImageUtilities.getRotatedImage(
            image,
            degrees
        )
    )**
}

private void replaceImage(RenderedOp newImage) {
    image.dispose();
    System.gc();
    image = newImage;
}

當我們從非常小的層級開始提取重複程式碼時,就會開始意識到這違及了單一職責原責 (SRP),就能夠進一步重構,把新提取的方法移到另一個類別,提升它的可見性,進一步在不同地方使用這些程式碼。

另外作者也提到,在移除高層級的重複程式碼時,樣版方法模式 (Template Method Pattern) 是常用的方法,這邊就不多做說明了。

具表達力

要寫出讓我們瞭解的程式碼是一件簡單的事,因為我們在寫的當下對問題有一定的暸解,但其他維護的人並不一定有同樣程度的認知。

一個軟體專案大多數成本是花在長期維護上。所以當你把程式碼寫得越清楚,程式碼就越有表達力,其他人就能花越少時間來修改,也就降低了維護的成本。

有很多方法能讓程式碼具有表達力,例如使用良好的名稱、讓函式或類別簡短、使用標準命名法 (例如用標準模式的名稱,Command 或 Visitor)

良好的單元測試也具有豐富的表達力,測試的主要目標之一是"用範例學",所以閱讀測試應該能夠快速理解某個類別是在做甚麼的。

最重要的是,要嘗試去讓自己的程式碼易於閱讀,簡而言之,就只是用心照顧你的程式

最小化類別及方法的數量

為了努力讓類別和方法簡短,我們可能會產生太多微型的類別跟方法,所以這個守則是建議我們要盡可能讓類別跟方法的數量少。

有時候大量的類別跟方法都是無意義的教條產生的,舉例來說,某個程式碼撰寫標準要求替所有的類別建立一個介面,或是要求成員變數 (field) 跟 行為 (behavior) 要分別寫在不同的類別。盡量必免這樣的寫法。

我們最終的目標是希望,在讓類別及方法的數量少的同時,也能讓系統保持簡單,但最小化類別及方法的數量不是最重要的事,前面說的測試、消除重複以及表達力才是更重要的。