《Head First設計模式》讀書筆記
前言:本文是記錄我在閱讀《Head First設計模式》這本書時,做得相關筆記,相關示例程式碼地址:design-patterns。由於本書不是將設計原則和設計模式分開講述的,而是在講一個設計模式之前,會引出一個設計原則,我在做筆記時進行了分類,可以看該文的目錄,需要學習設計模式詳情建議查看原書,希望本文對你了解設計模式有所幫助。
第一章要點
- 知道面向對象(OO)基礎,並不足以讓你設計出良好的面向對象(OO)系統
- 良好的OO設計必須具備可用復用、可擴充、可維護三個熱性
- 模式可以讓我們建造出具有良好的OO設計品質的系統
- 模式被認為是歷經驗證的OO設計經驗
- 模式不是程式碼,而是針對設計問題的通用解決方案,你把它們應用到特定的應用中
- 模式不是被發明,而是被實現
- 大多數的模式和原則,都著眼於軟體變化的主題
- 大多數的模式都允許系統局部改變獨立於其他部分
- 我們通常把系統中,會變化的部分抽出來封裝
- 模式讓開發人員之間有共享的語言,最大化溝通的價值
第二章要點
- 觀察者模式定義了對象之間一對多的關係
- 主題(也就是可觀察者)用一個共同的介面來更新觀察者
- 觀察者和可觀察者之間用松耦合方式結合(loosecoupling),可觀察者不知道觀察者的細節,只知道觀察者實現了觀察者介面
- 使用此模式時,你可從被觀察者處推(push)或拉(pull)數據(然而,推的方式被認為更「正確」)
- 有多個觀察者時,不可以依賴特定的通知順序
- Java有多種觀察者模式的實現,包括了通用的java.util.Observable
- 要注意java.util.Observable實現上所帶來的一些問題。
- 如果有必要的話,可以實現自己的Observable,這並不難,不要害怕
- Swing大量使用觀察者模式,許多GUI框架也是如此
第三章知識要點
- 繼承屬於擴展形式之一,但不見得是達到彈性設計的最佳方式。
- 在我們的設計中,應該允許行為可以被擴展,而無須修改現有的程式碼。
- 組合和委託可用於在運行時動態地加上新的行為。
- 除了繼承,裝飾者模式也可以讓我們擴展行為。
- 裝飾者模式意味著一群裝飾者類,這些類用來包裝具體組件
- 裝飾者類反映出被裝飾的組件類型(事實上,他們具有相同的類型,都經過介面或繼承實現)
- 裝飾者可以在被裝飾者的行為前面與/或後面加上自己的行為,甚至將被裝飾者的行為整個取代掉,而達到特定的目的。
- 你可以用無數個裝飾者包裝一個組件。
- 裝飾者一般對組件的客戶是透明的,除非客戶程式依賴於組件的具體類型。
- 裝飾者會導致設計中出現許多小對象,如果過度使用,會讓程式變得很複雜。
第四章知識要點
- 所有的工廠都是用來封裝對象的創建
- 簡單工廠,雖然不是真正的設計模式,但仍不失為一個簡單的方法,可以將客戶程式從具體類中解耦
- 工廠方法使用繼承:把對象的創建委託給了子類,子類實現工廠方法來創建對象
- 抽象工廠使用對象組合:對象的創建被實現在工廠介面所暴露出來的方法中
- 所有工廠模式都通過減少應用程式和具體類之間的依賴促進松耦合
- 工廠方法允許類將實例化延遲到子類進行
- 抽象工廠創建相關的對象家族,而不需要依賴他們的具體類
- 依賴倒置原則,指導我們避免依賴具體類型,而要盡量依賴抽象
- 工廠是很有威力的技巧,幫助我們針對抽象編程,而不要針對具體類編程
第五章知識要點
- 單例模式確保程式中一個類最多只有一個實例
- 單例模式也提供訪問這個實例的全局點
- 在Java中 實現單例模式需要私有的構造器、一個靜態變數和一個靜態方法
- 確定在性能和資源上的限制,然後小心地選擇合適的方案來實現單例,以解決多執行緒問題(我們必須認定所有的程式都是多執行緒的)
- 小心,你如果使用多個類載入器,可能會導致單例失效而產生多個實例
- 如果不是採用的jdk1.5或以上版本,雙重檢查加鎖實現會失效
- 如果是使用的jdk1.2或之前的版本,你必須建立單例註冊表,以免垃圾收集器將單例回收
第六章知識要點
- 命令模式將發出請求的對象和執行請求的對象解耦
- 在被解耦的兩者之間是通過命令對象進行溝通的。命令對象封裝了接收者和一個或一組動作
- 調用者通過調用命令對象的execute()發出請求,這會使得接收者的動作被調用
- 調用者可以接受命令當作參數,甚至在運行時動態地進行
- 命令可以支援撤銷,做法是實現一個undo()方法來回到execute()方法執行之前的狀態
- 宏命令是命令的一種簡單的延申,允許調用多個命令。宏方法也支援撤銷
- 實際操作時,很常見使用「聰明」的命令對象,也就是直接實現了請求,而不是將工作委託給接收者
- 命令也可以用來實現日誌和事務系統
第七章知識要點
- 當需要使用一個現有的類而其並不符合你的需要時,就使用適配器
- 當需要簡化並統一一個很大的介面或者一群複雜的介面時,使用外觀
- 適配器改變介面以符合客戶的期望
- 外觀將客戶從一個複雜的子系統中解耦
- 實現一個適配器可能需要一番功夫,也可能不費功夫,視目標介面的大小與複雜度而定
- 實現一個外觀,需要將子系統組合到外觀中,然後將工作委託給子系統執行
- 適配器模式有兩種形式:對象適配器和類適配器。類適配器需要用到多繼承
- 你可以為一個子系統實現一個以上的外觀
- 適配器將一個對象包裝起來以改變其介面;裝飾者將一個對象包裝以增加新的行為和責任;而外觀將一群對象」包裝「起來以簡化其介面
第八章知識要點
- 「模板方法」定義了演算法的步驟,把這些步驟的實現延遲到子類
- 模板方法模式為我們提供了一種程式碼復用的重要技巧
- 模板方法的抽象類可以定義具體方法,抽象方法和鉤子
- 抽象方法由子類實現
- 鉤子是一種方法,它在抽象類中不做事,或者只做默認的事情,子類可以去選擇要不要覆蓋它
- 為了防止子類改變模板方法中的演算法,可以將模板方法聲明為final
- 好萊塢原則告訴我們,將決策權放在高層模組中,以便決定如何以及何時調用底層模組
- 你將在真實世界程式碼中看到模板方法的許多變體,不要期望他們全都是一眼就可以被你認出的
- 策略模式和模板方法模式都封裝演算法,一個用組合,一個用繼承
- 工廠方法是模板方法的一個特殊版本
第九章知識要點
- 迭代器允許訪問聚合的元素,而不需要暴露它的內部結構
- 迭代器將遍歷聚合的工作封裝進一個對象中
- 當使用迭代器的時候,我們依賴聚合提供遍歷
- 迭代器提供了一個通用的介面,讓我們遍歷聚合的項,當我們編碼使用集合的項時,就可以使用多態機制
- 我們應該努力讓一個類只分配一個責任
- 組合模式提供一個結構,可同時包含個別對象和組合對象
- 組合模式允許客戶對個別對象以及組合對象一視同仁
- 組合結構內的任意對象稱為組件,組件可以是組合,也可以是葉子節點
第十章知識要點
- 狀態模式允許一個對象基於內部狀態而擁有不同的行為
- 和程式狀態機(PSM)不同,狀態模式用類代表狀態
- Context會將行為委託給當前狀態對象
- 通過將每個狀態封裝進一個類,我們把以後需要做的任何改變格局變化了
- 狀態模式和策略模式有相同的類圖,但他們的意圖不一樣
- 策略模式通常會用行為或演算法來配置Context類
- 狀態模式允許Context隨著狀態的改變而改變行為
- 狀態轉換可以由State類或Context類控制
- 使用狀態模式通常會導致設計類的數目大量增加
- 狀態類可以被多個Context實例共享
第十一章知識要點
- 代理模式為另一個對象提供代表,以便控制客戶對對象的訪問,管理訪問的方式有多種
- 遠程代理管理客戶和遠程對象之間的交互
- 虛擬代理控制訪問實例化開銷大的對象
- 保護代理基於調用者控制對對象方法的訪問
- 代理模式有多種變體,例如:快取代理、同步代理、防火牆代理和寫入時複製代理
- 防火牆代理:控制網路資源的訪問,保護主題免於「壞客戶」的侵害
- 智慧引用代理:當主題被引用時,進行額外的動作,例如計算一個對象被引用的次數
- 快取代理:為開銷大的運算結果提供暫時的存儲,它允許多個客戶共享結果,以減少計算和網路延遲
- 同步代理:在多執行緒情況下為主題提供安全的訪問
- 複雜隱藏代理:用來隱藏一個類的複雜集合的複雜度,並進行訪問控制。有時候也稱為外觀代理。複雜隱藏代理和外觀模式是不一樣的,因為代理控制訪問,而外觀模式只提供另一組介面
- 寫入時複製代理:用來控制對象的複製,方法是延遲對象的複製,直到客戶真的需要為止,這是虛擬代理的變體。
- 代理在結構上類似裝飾者,但是目的不同
- 裝飾者模式為對象加上行為,而代理則是控制訪問
- Java內置的代理支援,可以根據需要建立動態代理,並將所有調用分配到所選的處理器
- 就和其它的包裝者一樣,代理會造成你得設計中類得數目增加
設計原則
- 把會變化的部分取出並封裝起來,以便以後可以輕易地擴充此部分,而不影響不需要變化的其他部分
- 針對介面編程,而不是針對實現編程
- 多用組合,少用繼承
- 為了交互對象之間的松耦合設計而努力
- 開放-關閉原則:類應該對擴展開放,對修改關閉
- 依賴倒置原則:要依賴抽象,不要依賴具體類。不能讓高層組件依賴底層組件。
- 幾個指導方針幫助實現該原則:
- 變數不可以持有具體類的引用。如果使用new,就會持有具體類的引用,你可以改用工廠來避開這樣的做法
- 不要讓類派生自具體類。如果派生自具體類,你就會依賴具體類,請派生自一個抽象(介面或抽象類)。
- 不要覆蓋基類中已實現的方法。如果覆蓋基類已實現的方法,那麼你得基類就不是一個真正適合被繼承的抽象,基類中已實現的方法,應該由所有的子類共享。
- 幾個指導方針幫助實現該原則:
- 最少知道原則:只和你的密友談話
- 幾個指導方針幫助實現該原則:在該對象的方法內,我們只應該調用屬於以下範圍的方法:
- 該對象本身
- 被當作方法的參數而傳遞進來的對象
- 此方法所創建或實例化的任何對象
- 對象的任何組件:把」組件「想像成被任何實例變數所引用的任何對象,換句話說,把這想像成」有一個(HAS-A)「關係
- 幾個指導方針幫助實現該原則:在該對象的方法內,我們只應該調用屬於以下範圍的方法:
- 好萊塢原則:別調用(打電話給)我們,我們會調用(打電話給)你
- 換句話說就是:高層組件對待底層組件的方式就是「別調用我們,我們會調用你」
- 底層組件絕對不可以直接調用高層組件
- 單一責任:一個類應該只有一個引起變化的原因
- 內聚:用來度量一個類或模組緊密地達到單一目的或責任
- 當一個模組或一個類被設計成只支援一組相關的功能時,我們說它具有高內聚,反之,當被設計成支援一組不相關的功能時,我們說它具有低內聚
設計模式
- 策略模式:定義了演算法族,分別封裝起來,讓他們之間可以相互替換,此模式讓演算法的變化獨立於使用演算法的客戶
- 觀察者模式: 定義了對象之間一對多的依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新
- 裝飾者模式:動態地將責任附加到對象上,若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案
- 工廠方法模式:定義了一個創建對象的介面,但由子類決定要實例化的類是哪一個。工廠方法讓類把實例化推遲到子類
- 抽象工廠模式:提供一個介面,用於創建相關或依賴對象的家族,而不需要明確指定具體類
- 單例模式:確保一個類只有一個實例,並提供一個全局訪問點
- 命令模式:將「請求」封裝成對象,以便使用不同的請求、隊列或者日誌參數化其他對象。命令模式也可以支援撤銷的操作
- 適配器模式:將一個類得介面,轉換成客戶期望的另一個介面。適配器讓原本介面不兼容的類可以合作無間
- 外觀模式:提供了一個統一的介面,用來訪問子系統中的一群介面。外觀定義了一個高層介面,讓子系統更容易使用
- 模板方法:在一個方法中定義一個演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變演算法結構的情況下,重新定義演算法的某些步驟。
- 迭代器模式:提供一種方法順序訪問一個聚合對象中的各個元素,而又不暴露其內部的表示
- 組合模式:允許你將對象組合成樹形結構來表現「整體/部分」層次結構。組合能讓客戶以一致的方式處理個別對象以及對象組合。
- 狀態模式:允許對象在內部狀態改變時改變它的行為,對象看起來好像改變了它的類
- 代理模式:為另一個對象提供一個替身或佔位符以訪問這個對象