## Kent Beck 的實作模式 Bear --- ## Chapter 4 動機 --- ## 軟體成本 = 初始成本 + 維護成本 --- ## 維護成本 = 理解成本 + 修改成本 ## + 測試成本 + 部署成本 --- ## 在初期的開發中投入更多精力,來減少維護的需要? --- ## Kent Beck 的策略 --- ## 注重程式設計師之間的溝通 減少理解程式碼的代價 --- ## 記錄自己的實作模式,並嚴格地遵守它 --- ## 模式給人們和經濟的影響 --- ## Chapter 5 類別 --- ## 類別 (Class) 這些資料應該放在一起,還有這些邏輯也是。 --- ## 資料會變,邏輯不變 學會如何用類別來包裝邏輯和如何表達邏輯的變化。 --- ## 繼承 把多個類別放進繼承體系可以縮減程式碼的數量。 --- ## 簡單的超類別名稱 (Simple Superclass Name) 位於繼承體系上的根類別,應該有個簡單的名字,用以描繪它的隱喻。 --- ## 命名 在所有的命名中,類別的命名是最重要的。 --- ## 簡短與表達力之間存在張力 類別名稱應該簡單扼要,但有時候需要好幾個單字才能足夠精確。 --- ## 隱喻 替計算邏輯找個強有力的隱喻。 讓名稱變成一張張「關係、連接和暗示」的大綱。 --- ## 交談 當嘗試把物件的用途解釋給別人聽時,就得尋找具有表達力和感染力的名稱。 --- ## 重要的類別 盡量用一個單字來為它命名。 --- ## 適當的子類別名稱 (Qualified Subclass Name) 子類別的名稱應該表達出與超類別間的相似性和差異性。 --- ## 子類別的命名 要描述子類別像什麼, 還要說明它們之間的區別是什麼。 --- ## 簡短與表達力間的選擇 由於子類別在交談中並不頻繁,所以可以「犧牲簡明」。 --- ## 例外 如果只是利用繼承來實作貢獻機制,則這樣的子類別也該用簡單的命名。 --- ## 多層繼承體系的命名 是個難題,一般來說要進行重構,改用委派。 若還是存在,則要思考閱讀者需要知道什麼資訊。 --- ## 抽象介面 (Abstract Interface) 界面與實作分離。 --- ## 介面的成本 需要學習它、理解它、替它寫文件、除錯、組織它,甚至替它命名。 --- ## 經濟的考量 只要一開始設計正確,軟體就不需要進行任何改動? --- ## 軟體變更清單 程式設計師沒有弄清需求、客戶改變了想法, 唯一沒有提到的是「正當的變更」。 --- ## Interface --- ## 多重繼承 一個類別可以實作多個 interface。 --- ## 介面的命名 如果 interface 叫 File,那實作類別只好叫 ActualFile, ConcreteFile 或 FileImpl。 可以給 interface 加上 I 字首。例如:IFile。 --- ## 抽象類別 (Abstract Class) 用抽象類別來表現可能會產生變化的抽象界面。 --- ## Interface vs Abstract Class 取捨的關鍵,介面會如何變化及實作類別是否需要同時支援多個介面。 --- ## 使用 Interface 作抽象類別 當修改時,所有的實作類別都必須進行修改。 容易導致現有的設計癱瘓,需借助有版本的 interface。 --- ## 使用 Abstract Class 作抽象類別 局限在「實作類別必須對其忠心不二」。 如果需要從另一個角度看待同一個實作類別,則只能讓它實作 interface。 --- ## 兩者並非互斥 可以提供介面,再提供超類別表達一種實作。 在這種情況下,變數宣告應使用介面類型的參考, 這樣將來的維護者就可以根據需要替換。 --- ## 有版本的 interface (Versioned Interface) 引入 sub-interface,進而安全地對 interface 進行拓展。 --- ```java interface Command { void run(); } ``` --- ```java interface ReversibleCommand extends Command { void undo(); } ``` --- ```java public void undoIfReversible(Command recent) { if (recent instanceof ReversibleCommand) { ReversibleCommand downcasted = (ReversibleCommand) recent; downcasted.undo(); } } ``` --- ## 使用 instanceof 會降低靈活性,也是個醜陋的解決方案。 但真的遇到這種尷尬局面時,知道該如何修改總是好的。 --- ## 值物件 (Value Object) 這種物件扮演的角色如同數值一樣。 --- ## 函數式 永遠不會改變任何狀態,只是建立新的值。 --- ## 程序式 如果面對的情景總在不斷變化,則狀態式比較適合。 --- ```java class Transaction { int value; Transaction(int value, Account credit, Account debit) { this.value = value; credit.addCredit(this); debit.addDebit(this); } int getValue() { return value; } } ``` --- ## 效能 使用值物件,最大的反對意見是建立臨時物件。 但考慮整條成本,大部分的情況,都不是效能瓶頸。 --- ## 邊界 對於「狀態需要變化」與「物件不能變化」的邊界難以劃清。 大部分是值物件,但又不純粹是值物件的風格是最糟糕的。 --- ## 物件、函數式和程序式 將「使用狀態的可變物件」與「像數值一樣不可變的物件」 搭配一起,能讓程式表達地更好。 --- ## 特殊化 (Specialization) 描繪相關計算的相似性和差異性。 --- ## 簡單的變化 abc 與 def 是不同的。 但計算字串長度的演算法相同。 --- ## 大部分的程式 「相同的邏輯處理不同的資料」和「不同的邏輯處理相同的資料」。 --- ## 複雜的變化 邏輯完全不同。 例如符號積分程式與數學排版程式。 --- ## 子類別 (Subclass) 用一個子類別表現一個維度上的變化。 --- ## 繼承 有些人發現繼承做的就是共用實作的萬靈丹。 --- ## 繼承的局限 - 這張牌只能打一次織。 - 要理解子類別,需先理解超類別。 - 對超類別的修改充滿風險。 --- ## 平行的繼承 「這個」繼承體系中的子類別都需要「那個」繼承體系的某個子類別。 這是特別糟糕的繼承用法。 --- ## 關鍵 把超類別的邏輯徹底劃分,直到每個方法只做一件事。 但當子類別修改超類別方法時,需檢查兩者隱晦的耦合。 --- ## 子類別的局限 沒辦法表現不斷變化的邏輯,因建立物件時已決定。 如果需要表達邏輯會隨時變化,則條件陳述式或委派更適合。 --- ## ʕ •ᴥ•ʔ:Thank you
返回文章