摘要:設計模式是軟件開發人員在軟件開發過程中面臨的一般問題的解決方案。設計模式的類型共有種設計模式。工廠模式工廠模式最常用的設計模式之一。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。
該文建議配合 design-patterns-for-humans 中文版 一起看。推薦閱讀
超全的設計模式簡介(45種)
design-patterns-for-humans 中文版(github 倉庫永久更新)
MongoDB 資源、庫、工具、應用程序精選列表中文版
有哪些鮮為人知,但是很有意思的網站?
一份攻城獅筆記
每天搜集 Github 上優秀的項目
一些有趣的民間故事
超好用的谷歌瀏覽器、Sublime Text、Phpstorm、油猴插件合集
設計模式(Design pattern)代表了最佳的實踐,通常被有經驗的面向對象的軟件開發人員所采用。設計模式是軟件開發人員在軟件開發過程中面臨的一般問題的解決方案。這些解決方案是眾多軟件開發人員經過相當長的一段時間的試驗和錯誤總結出來的。設計模式的類型
設計模式是一套被反復使用的、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式于己于他人于系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石一樣。項目中合理地運用設計模式可以完美地解決很多問題,每種模式在現實中都有相應的原理來與之對應,每種模式都描述了一個在我們周圍不斷重復發生的問題,以及該問題的核心解決方案,這也是設計模式能被廣泛應用的原因。
共有 23 種設計模式。這些模式可以分為三大類:
創建型模式(Creational Patterns)- 這些設計模式提供了一種在創建對象的同時隱藏創建邏輯的方式,而不是使用 new 運算符直接實例化對象。這使得程序在判斷針對某個給定實例需要創建哪些對象時更加靈活。
工廠模式(Factory Pattern)
抽象工廠模式(Abstract Factory Pattern)
單例模式(Singleton Pattern)
建造者模式(Builder Pattern)
原型模式(Prototype Pattern)
對象池模式 *(Pool)
多例模式 *(Multiton)
靜態工廠模式 *(Static Factory)
結構型模式(Structural Patterns)- 這些設計模式關注類和對象的組合。繼承的概念被用來組合接口和定義組合對象獲得新功能的方式。
適配器模式(Adapter Pattern)
橋接模式(Bridge Pattern)
過濾器模式(Filter、Criteria Pattern)
組合模式(Composite Pattern)
裝飾器模式(Decorator Pattern)
外觀模式(Facade Pattern)
享元模式(Flyweight Pattern)
代理模式(Proxy Pattern)
數據映射模式 *(Data Mapper)
依賴注入模式 *(Dependency Injection)
門面模式 *(Facade)
流接口模式 *(Fluent Interface)
注冊模式 *(Registry)
行為型模式(Behavioral Patterns)- 這些設計模式特別關注對象之間的通信。
責任鏈模式(Chain of Responsibility Pattern)
命令模式(Command Pattern)
解釋器模式(Interpreter Pattern)
迭代器模式(Iterator Pattern)
中介者模式(Mediator Pattern)
備忘錄模式(Memento Pattern)
觀察者模式(Observer Pattern)
狀態模式(State Pattern)
空對象模式(Null Object Pattern)
策略模式(Strategy Pattern)
模板模式(Template Pattern)
訪問者模式(Visitor Pattern)
規格模式 *(Specification)
訪問者模式 *(Visitor)
J2EE 設計模式 - 這些設計模式特別關注表示層。這些模式是由 Sun Java Center 鑒定的。
MVC模式(MVC Pattern)
業務代表模式(Business Delegate Pattern)
組合實體模式(Composite Entity Pattern)
數據訪問對象模式(Data Access Object Pattern)
前端控制器模式(Front Controller Pattern)
攔截過濾器模式(Intercepting Filter Pattern)
服務定位器模式(Service Locator Pattern)
傳輸對象模式(Transfer Object Pattern)
委托模式 *(Delegation)
資源庫模式 *(Repository)
下面用一個圖片來整體描述一下設計模式之間的關系:
設計模式的六大原則 1、開閉原則(Open Close Principle)開閉原則的意思是:對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。簡言之,是為了使程序的擴展性好,易于維護和升級。想要達到這樣的效果,我們需要使用接口和抽象類,后面的具體設計中我們會提到這點。
2、里氏代換原則(Liskov Substitution Principle)里氏代換原則是面向對象設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。LSP 是繼承復用的基石,只有當派生類可以替換掉基類,且軟件單位的功能不受到影響時,基類才能真正被復用,而派生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關系就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規范。
3、依賴倒轉原則(Dependence Inversion Principle)這個原則是開閉原則的基礎,具體內容:針對接口編程,依賴于抽象而不依賴于具體。
4、接口隔離原則(Interface Segregation Principle)這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。它還有另外一個意思是:降低類之間的耦合度。由此可見,其實設計模式就是從大型軟件架構出發、便于升級和維護的軟件設計思想,它強調降低依賴,降低耦合。
5、迪米特法則,又稱最少知道原則(Demeter Principle)最少知道原則是指:一個實體應當盡量少地與其他實體之間發生相互作用,使得系統功能模塊相對獨立。
6、合成復用原則(Composite Reuse Principle)合成復用原則是指:盡量使用合成 / 聚合的方式,而不是使用繼承。
工廠模式工廠模式(Factory Pattern)最常用的設計模式之一。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。介紹
在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,并且是通過使用一個共同的接口來指向新創建的對象。
意圖: 定義一個創建對象的接口,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創建過程延遲到子類進行。
主要解決: 主要解決接口選擇的問題。
何時使用: 我們明確地計劃不同條件下創建不同實例時。
如何解決: 讓其子類實現工廠接口,返回的也是一個抽象的產品。
關鍵代碼: 創建過程在其子類執行。
應用實例:
您需要一輛汽車,可以直接從工廠里面提貨,而不用去管這輛汽車是怎么做出來的,以及這個汽車里面的具體實現。
Hibernate 換數據庫只需換方言和驅動就可以。
優點:
一個調用者想創建一個對象,只要知道其名稱就可以了。
擴展性高,如果想增加一個產品,只要擴展一個工廠類就可以。
屏蔽產品的具體實現,調用者只關心產品的接口。
缺點: 每次增加一個產品時,都需要增加一個具體類和對象實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的復雜度,同時也增加了系統具體類的依賴。這并不是什么好事。
使用場景:
日志記錄器:記錄可能記錄到本地硬盤、系統事件、遠程服務器等,用戶可以選擇記錄日志到什么地方。
數據庫訪問,當用戶不知道最后系統采用哪一類數據庫,以及數據庫可能有變化時。
設計一個連接服務器的框架,需要三個協議,"POP3"、"IMAP"、"HTTP",可以把這三個作為產品類,共同實現一個接口。
注意事項: 作為一種創建類模式,在任何需要生成復雜對象的地方,都可以使用工廠方法模式。有一點需要注意的地方就是復雜對象適合使用工廠模式,而簡單對象,特別是只需要通過 new 就可以完成創建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的復雜度。
抽象工廠模式抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠創建其他工廠。該超級工廠又稱為其他工廠的工廠。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。介紹
在抽象工廠模式中,接口是負責創建一個相關對象的工廠,不需要顯式指定它們的類。每個生成的工廠都能按照工廠模式提供對象。
意圖: 提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
主要解決: 主要解決接口選擇的問題。
何時使用: 系統的產品有多于一個的產品族,而系統只消費其中某一族的產品。
如何解決: 在一個產品族里面,定義多個產品。
關鍵代碼: 在一個工廠里聚合多個同類產品。
應用實例: 工作了,為了參加一些聚會,肯定有兩套或多套衣服吧,比如說有商務裝(成套,一系列具體產品)、時尚裝(成套,一系列具體產品),甚至對于一個家庭來說,可能有商務女裝、商務男裝、時尚女裝、時尚男裝,這些也都是成套的,即一系列具體產品。假設一種情況(現實中是不存在的,要不然,沒法進入共產主義了,但有利于說明抽象工廠模式),在您的家中,某一個衣柜(具體工廠)只能存放某一種這樣的衣服(成套,一系列具體產品),每次拿這種成套的衣服時也自然要從這個衣柜中取出了。用 OO 的思想去理解,所有的衣柜(具體工廠)都是衣柜類的(抽象工廠)某一個,而每一件成套的衣服又包括具體的上衣(某一具體產品),褲子(某一具體產品),這些具體的上衣其實也都是上衣(抽象產品),具體的褲子也都是褲子(另一個抽象產品)。
優點: 當一個產品族中的多個對象被設計成一起工作時,它能保證客戶端始終只使用同一個產品族中的對象。
缺點: 產品族擴展非常困難,要增加一個系列的某一產品,既要在抽象的 Creator 里加代碼,又要在具體的里面加代碼。
使用場景:
QQ 換皮膚,一整套一起換。
生成不同操作系統的程序。
注意事項: 產品族難擴展,產品等級易擴展。
單例模式單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。
這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
注意:
1、單例類只能有一個實例。
2、單例類必須自己創建自己的唯一實例。
3、單例類必須給所有其他對象提供這一實例。
介紹意圖: 保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
主要解決: 一個全局使用的類頻繁地創建與銷毀。
何時使用: 當您想控制實例數目,節省系統資源的時候。
如何解決: 判斷系統是否已經有這個單例,如果有則返回,如果沒有則創建。
關鍵代碼: 構造函數是私有的。
應用實例:
一個班級只有一個班主任。
Windows 是多進程多線程的,在操作一個文件的時候,就不可避免地出現多個進程或線程同時操作一個文件的現象,所以所有文件的處理必須通過唯一的實例來進行。
一些設備管理器常常設計為單例模式,比如一個電腦有兩臺打印機,在輸出的時候就要處理不能兩臺打印機打印同一個文件。
優點:
在內存里只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷毀實例(比如管理學院首頁頁面緩存)。
避免對資源的多重占用(比如寫文件操作)。
缺點: 沒有接口,不能繼承,與單一職責原則沖突,一個類應該只關心內部邏輯,而不關心外面怎么樣來實例化。
使用場景:
要求生產唯一序列號。
WEB 中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。
創建的一個對象需要消耗的資源過多,比如 I/O 與數據庫的連接等。
注意事項:getInstance() 方法中需要使用同步鎖 synchronized (Singleton.class) 防止多線程同時進入造成 instance 被多次實例化。
建造者模式建造者模式(Builder Pattern)使用多個簡單的對象一步一步構建成一個復雜的對象。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。介紹
一個 Builder 類會一步一步構造最終的對象。該 Builder 類是獨立于其他對象的。
意圖: 將一個復雜的構建與其表示相分離,使得同樣的構建過程可以創建不同的表示。
主要解決: 主要解決在軟件系統中,有時候面臨著 "一個復雜對象" 的創建工作,其通常由各個部分的子對象用一定的算法構成;由于需求的變化,這個復雜對象的各個部分經常面臨著劇烈的變化,但是將它們組合在一起的算法卻相對穩定。
何時使用: 一些基本部件不會變,而其組合經常變化的時候。
如何解決: 將變與不變分離開。
關鍵代碼: 建造者:創建和提供實例,導演:管理建造出來的實例的依賴關系。
應用實例:
去肯德基,漢堡、可樂、薯條、炸雞翅等是不變的,而其組合是經常變化的,生成出所謂的 "套餐"。
JAVA 中的 StringBuilder。
優點:
建造者獨立,易擴展。
便于控制細節風險。
缺點:
產品必須有共同點,范圍有限制。
如內部變化復雜,會有很多的建造類。
使用場景:
需要生成的對象具有復雜的內部結構。
需要生成的對象內部屬性本身相互依賴。
注意事項: 與工廠模式的區別是:建造者模式更加關注與零件裝配的順序。
原型模式原型模式(Prototype Pattern)是用于創建重復的對象,同時又能保證性能。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。介紹
這種模式是實現了一個原型接口,該接口用于創建當前對象的克隆。當直接創建對象的代價比較大時,則采用這種模式。例如,一個對象需要在一個高代價的數據庫操作之后被創建。我們可以緩存該對象,在下一個請求時返回它的克隆,在需要的時候更新數據庫,以此來減少數據庫調用。
意圖: 用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。
主要解決: 在運行期建立和刪除原型。
何時使用:
當一個系統應該獨立于它的產品創建,構成和表示時。
當要實例化的類是在運行時刻指定時,例如,通過動態裝載。
為了避免創建一個與產品類層次平行的工廠類層次時。
當一個類的實例只能有幾個不同狀態組合中的一種時。建立相應數目的原型并克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。
如何解決: 利用已有的一個原型對象,快速地生成和原型對象一樣的實例。
關鍵代碼:
實現克隆操作,在 JAVA 繼承 Cloneable,重寫 clone(),在 .NET 中可以使用 Object 類的 MemberwiseClone() 方法來實現對象的淺拷貝或通過序列化的方式來實現深拷貝。
原型模式同樣用于隔離類對象的使用者和具體類型(易變類)之間的耦合關系,它同樣要求這些 "易變類" 擁有穩定的接口。
應用實例:
細胞分裂。
JAVA 中的 Object clone() 方法。
優點:
性能提高。
逃避構造函數的約束。
缺點:
配備克隆方法需要對類的功能進行通盤考慮,這對于全新的類不是很難,但對于已有的類不一定很容易,特別當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。
必須實現 Cloneable 接口。
使用場景:
資源優化場景。
類初始化需要消化非常多的資源,這個資源包括數據. 硬件資源等。
性能和安全要求的場景。
通過 new 產生一個對象需要非常繁瑣的數據準備或訪問權限,則可以使用原型模式。
一個對象多個修改者的場景。
一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用。
在實際項目中,原型模式很少多帶帶出現,一般是和工廠方法模式一起出現,通過 clone 的方法創建一個對象,然后由工廠方法提供給調用者。原型模式已經與 Java 融為渾然一體,大家可以隨手拿來使用。
注意事項: 與通過對一個類進行實例化來構造新對象不同的是,原型模式是通過拷貝一個現有對象生成新對象的。淺拷貝實現 Cloneable,重寫,深拷貝是通過實現 Serializable 讀取二進制流。
對象池模式對象池(也稱為資源池)被用來管理對象緩存。對象池是一組已經初始化過且可以直接使用的對象集合,用戶在使用對象時可以從對象池中獲取對象,對其進行操作處理,并在不需要時歸還給對象池而非銷毀它。示例代碼 Pool.php
若對象初始化、實例化的代價高,且需要經常實例化,但每次實例化的數量較少的情況下,使用對象池可以獲得顯著的性能提升。常見的使用對象池模式的技術包括線程池、數據庫連接池、任務隊列池、圖片資源對象池等。
當然,如果要實例化的對象較小,不需要多少資源開銷,就沒有必要使用對象池模式了,這非但不會提升性能,反而浪費內存空間,甚至降低性能。
class = $class; } public function get() { if (count($this->instances) > 0) { return array_pop($this->instances); } return new $this->class(); } public function dispose($instance) { $this->instances[] = $instance; } }Processor.php
pool = $pool; } public function process($image) { if ($this->processing++ < $this->maxProcesses) { $this->createWorker($image); } else { $this->pushToWaitingQueue($image); } } private function createWorker($image) { $worker = $this->pool->get(); $worker->run($image, array($this, "processDone")); } public function processDone($worker) { $this->processing--; $this->pool->dispose($worker); if (count($this->waitingQueue) > 0) { $this->createWorker($this->popFromWaitingQueue()); } } private function pushToWaitingQueue($image) { $this->waitingQueue[] = $image; } private function popFromWaitingQueue() { return array_pop($this->waitingQueue); } }Worker.php
多例模式多例模式和單例模式類似,但可以返回多個實例。比如我們有多個數據庫連接,MySQL、SQLite、Postgres,又或者我們有多個日志記錄器,分別用于記錄調試信息和錯誤信息,這些都可以使用多例模式實現。示例代碼 Multiton.php靜態工廠模式與簡單工廠類似,該模式用于創建一組相關或依賴的對象,不同之處在于靜態工廠模式使用一個靜態方法來創建所有類型的對象,該靜態方法通常是 factory 或 build。示例代碼 StaticFactory.phpFormatterInterface.phpFormatString.php
適配器模式適配器模式(Adapter Pattern)是作為兩個不兼容的接口之間的橋梁。這種類型的設計模式屬于結構型模式,它結合了兩個獨立接口的功能。介紹
這種模式涉及到一個單一的類,該類負責加入獨立的或不兼容的接口功能。舉個真實的例子,讀卡器是作為內存卡和筆記本之間的適配器。您將內存卡插入讀卡器,再將讀卡器插入筆記本,這樣就可以通過筆記本來讀取內存卡。意圖: 將一個類的接口轉換成客戶希望的另外一個接口。適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。
主要解決: 主要解決在軟件系統中,常常要將一些 "現存的對象" 放到新的環境中,而新環境要求的接口是現對象不能滿足的。
何時使用:
系統需要使用現有的類,而此類的接口不符合系統的需要。
想要建立一個可以重復使用的類,用于與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作,這些源類不一定有一致的接口。
通過接口轉換,將一個類插入另一個類系中。(比如老虎和飛禽,現在多了一個飛虎,在不增加實體的需求下,增加一個適配器,在里面包容一個虎對象,實現飛的接口。)
如何解決: 繼承或依賴(推薦)。
關鍵代碼: 適配器繼承或依賴已有的對象,實現想要的目標接口。
應用實例:
美國電器 110V,中國 220V,就要有一個適配器將 110V 轉化為 220V。
JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,則要將以前系統的 Enumeration 接口轉化為 Iterator 接口,這時就需要適配器模式。
在 LINUX 上運行 WINDOWS 程序。 4. JAVA 中的 jdbc。
優點:
可以讓任何兩個沒有關聯的類一起運行。
提高了類的復用。
增加了類的透明度。
靈活性好。
缺點:
過多地使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是 A 接口,其實內部被適配成了 B 接口的實現,一個系統如果太多出現這種情況,無異于一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統進行重構。
由于 JAVA 至多繼承一個類,所以至多只能適配一個適配者類,而且目標類必須是抽象類。
使用場景: 有動機地修改一個正常運行的系統的接口,這時應該考慮使用適配器模式。
注意事項: 適配器不是在詳細設計時添加的,而是解決正在服役的項目的問題。
橋接模式橋接(Bridge)是用于把抽象化與實現化解耦,使得二者可以獨立變化。這種類型的設計模式屬于結構型模式,它通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦。介紹
這種模式涉及到一個作為橋接的接口,使得實體類的功能獨立于接口實現類。這兩種類型的類可被結構化改變而互不影響。意圖: 將抽象部分與實現部分分離,使它們都可以獨立的變化。
主要解決: 在有多種可能會變化的情況下,用繼承會造成類爆炸問題,擴展起來不靈活。
何時使用: 實現系統可能有多個角度分類,每一種角度都可能變化。
如何解決: 把這種多角度分類分離出來,讓它們獨立變化,減少它們之間耦合。
關鍵代碼: 抽象類依賴實現類。
應用實例:
豬八戒從天蓬元帥轉世投胎到豬,轉世投胎的機制將塵世劃分為兩個等級,即:靈魂和肉體,前者相當于抽象化,后者相當于實現化。生靈通過功能的委派,調用肉體對象的功能,使得生靈可以動態地選擇。
墻上的開關,可以看到的開關是抽象的,不用管里面具體怎么實現的。
優點:
抽象和實現的分離。
優秀的擴展能力。
實現細節對客戶透明。
缺點: 橋接模式的引入會增加系統的理解與設計難度,由于聚合關聯關系建立在抽象層,要求開發者針對抽象進行設計與編程。
使用場景:
如果一個系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態的繼承聯系,通過橋接模式可以使它們在抽象層建立一個關聯關系。
對于那些不希望使用繼承或因為多層次繼承導致系統類的個數急劇增加的系統,橋接模式尤為適用。
一個類存在兩個獨立變化的維度,且這兩個維度都需要進行擴展。
注意事項: 對于兩個獨立變化的維度,使用橋接模式再適合不過了。
過濾器模式過濾器模式(Filter Pattern)或標準模式(Criteria Pattern)是一種設計模式,這種模式允許開發人員使用不同的標準來過濾一組對象,通過邏輯運算以解耦的方式把它們連接起來。這種類型的設計模式屬于結構型模式,它結合多個標準來獲得單一標準。組合模式組合模式(Composite Pattern),又叫部分整體模式,是用于把一組相似的對象當作一個單一的對象。組合模式依據樹形結構來組合對象,用來表示部分以及整體層次。這種類型的設計模式屬于結構型模式,它創建了對象組的樹形結構。介紹
這種模式創建了一個包含自己對象組的類。該類提供了修改相同對象組的方式。意圖: 將對象組合成樹形結構以表示 "部分 - 整體" 的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。
主要解決: 它在我們樹型結構的問題中,模糊了簡單元素和復雜元素的概念,客戶程序可以向處理簡單元素一樣來處理復雜元素,從而使得客戶程序與復雜元素的內部結構解耦。
何時使用:
您想表示對象的部分 - 整體層次結構(樹形結構)。
您希望用戶忽略組合對象與單個對象的不同,用戶將統一地使用組合結構中的所有對象。
如何解決: 樹枝和葉子實現統一接口,樹枝內部組合該接口。
關鍵代碼: 樹枝內部組合該接口,并且含有內部屬性 List,里面放 Component。
應用實例:
算術表達式包括操作數. 操作符和另一個操作數,其中,另一個操作符也可以是操作數. 操作符和另一個操作數。
在 JAVA AWT 和 SWING 中,對于 Button 和 Checkbox 是樹葉,Container 是樹枝。
優點:
高層模塊調用簡單。
節點自由增加。
缺點: 在使用組合模式時,其葉子和樹枝的聲明都是實現類,而不是接口,違反了依賴倒置原則。
使用場景: 部分. 整體場景,如樹形菜單,文件. 文件夾的管理。
注意事項: 定義時為具體類。
裝飾器模式裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬于結構型模式,它是作為現有的類的一個包裝。介紹
這種模式創建了一個裝飾類,用來包裝原有的類,并在保持類方法簽名完整性的前提下,提供了額外的功能。意圖: 動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更為靈活。
主要解決: 一般的,我們為了擴展一個類經常使用繼承方式實現,由于繼承為類引入靜態特征,并且隨著擴展功能的增多,子類會很膨脹。
何時使用: 在不想增加很多子類的情況下擴展類。
如何解決: 將具體功能職責劃分,同時繼承裝飾者模式。
關鍵代碼:
Component 類充當抽象角色,不應該具體實現。
修飾類引用和繼承 Component 類,具體擴展類重寫父類方法。
應用實例:
孫悟空有 72 變,當他變成 "廟宇" 后,他的根本還是一只猴子,但是他又有了廟宇的功能。
不論一幅畫有沒有畫框都可以掛在墻上,但是通常都是有畫框的,并且實際上是畫框被掛在墻上。在掛在墻上之前,畫可以被蒙上玻璃,裝到框子里;這時畫、玻璃和畫框形成了一個物體。
優點: 裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴展一個實現類的功能。
缺點: 多層裝飾比較復雜。
使用場景:
擴展一個類的功能。
動態增加功能,動態撤銷。
注意事項: 可代替繼承。
外觀模式外觀模式(Facade Pattern)隱藏系統的復雜性,并向客戶端提供了一個客戶端可以訪問系統的接口。這種類型的設計模式屬于結構型模式,它向現有的系統添加一個接口,來隱藏系統的復雜性。介紹
這種模式涉及到一個單一的類,該類提供了客戶端請求的簡化方法和對現有系統類方法的委托調用。意圖: 為子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
主要解決: 降低訪問復雜系統的內部子系統時的復雜度,簡化客戶端與之的接口。
何時使用:
客戶端不需要知道系統內部的復雜聯系,整個系統只需提供一個 "接待員" 即可。
定義系統的入口。
如何解決: 客戶端不與系統耦合,外觀類與系統耦合。
關鍵代碼: 在客戶端和復雜系統之間再加一層,這一層將調用順序. 依賴關系等處理好。
應用實例:
去醫院看病,可能要去掛號、門診、劃價、取藥,讓患者或患者家屬覺得很復雜,如果有提供接待人員,只讓接待人員來處理,就很方便。
JAVA 的三層開發模式。
優點:
減少系統相互依賴。
提高靈活性。
提高了安全性。
缺點: 不符合開閉原則,如果要改東西很麻煩,繼承重寫都不合適。
使用場景:
為復雜的模塊或子系統提供外界訪問的模塊。
子系統相對獨立。
預防低水平人員帶來的風險。
注意事項: 在層次化結構中,可以使用外觀模式定義系統中每一層的入口。
享元模式享元模式(Flyweight Pattern)主要用于減少創建對象的數量,以減少內存占用和提高性能。這種類型的設計模式屬于結構型模式,它提供了減少對象數量從而改善應用所需的對象結構的方式。介紹
享元模式嘗試重用現有的同類對象,如果未找到匹配的對象,則創建新對象。我們將通過創建 5 個對象來畫出 20 個分布于不同位置的圓來演示這種模式。由于只有 5 種可用的顏色,所以 color 屬性被用來檢查現有的 Circle 對象。意圖: 運用共享技術有效地支持大量細粒度的對象。
主要解決: 在有大量對象時,有可能會造成內存溢出,我們把其中共同的部分抽象出來,如果有相同的業務請求,直接返回在內存中已有的對象,避免重新創建。
何時使用:
系統中有大量對象。
這些對象消耗大量內存。
這些對象的狀態大部分可以外部化。
這些對象可以按照內蘊狀態分為很多組,當把外蘊對象從對象中剔除出來時,每一組對象都可以用一個對象來代替。
系統不依賴于這些對象身份,這些對象是不可分辨的。
如何解決: 用唯一標識碼判斷,如果在內存中有,則返回這個唯一標識碼所標識的對象。
關鍵代碼: 用 HashMap 存儲這些對象。
應用實例:
JAVA 中的 String,如果有則返回,如果沒有則創建一個字符串保存在字符串緩存池里面。2. 數據庫的數據池。
優點: 大大減少對象的創建,降低系統的內存,使效率提高。
缺點: 提高了系統的復雜度,需要分離出外部狀態和內部狀態,而且外部狀態具有固有化的性質,不應該隨著內部狀態的變化而變化,否則會造成系統的混亂。
使用場景:
系統有大量相似對象。
需要緩沖池的場景。
注意事項:
注意劃分外部狀態和內部狀態,否則可能會引起線程安全問題。
這些類必須有一個工廠對象加以控制。
代理模式在代理模式(Proxy Pattern)中,一個類代表另一個類的功能。這種類型的設計模式屬于結構型模式。介紹
在代理模式中,我們創建具有現有對象的對象,以便向外界提供功能接口。意圖: 為其他對象提供一種代理以控制對這個對象的訪問。
主要解決: 在直接訪問對象時帶來的問題,比如說:要訪問的對象在遠程的機器上。在面向對象系統中,有些對象由于某些原因(比如對象創建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層。
何時使用: 想在訪問一個類時做一些控制。
如何解決: 增加中間層。
關鍵代碼: 實現與被代理類組合。
應用實例:
Windows 里面的快捷方式。
豬八戒去找高翠蘭結果是孫悟空變的,可以這樣理解:把高翠蘭的外貌抽象出來,高翠蘭本人和孫悟空都實現了這個接口,豬八戒訪問高翠蘭的時候看不出來這個是孫悟空,所以說孫悟空是高翠蘭代理類。
買火車票不一定在火車站買,也可以去代售點。
一張支票或銀行存單是賬戶中資金的代理。支票在市場交易中用來代替現金,并提供對簽發人賬號上資金的控制。
spring aop。
優點:
職責清晰。
高擴展性。
智能化。
缺點:
由于在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。
實現代理模式需要額外的工作,有些代理模式的實現非常復雜。
使用場景: 按職責來劃分,通常有以下使用場景:
遠程代理。
虛擬代理。
Copy-on-Write 代理。
保護(Protect or Access)代理。
Cache 代理。
防火墻(Firewall)代理。
同步化(Synchronization)代理。
智能引用(Smart Reference)代理。
注意事項:
和適配器模式的區別:適配器模式主要改變所考慮對象的接口,而代理模式不能改變所代理類的接口。
和裝飾器模式的區別:裝飾器模式為了增強功能,而代理模式是為了加以控制。
數據映射模式在了解數據映射模式之前,先了解下數據映射,它是在持久化數據存儲層(通常是關系型數據庫)和駐于內存的數據表現層之間進行雙向數據傳輸的數據訪問層。依賴注入模式
數據映射模式的目的是讓持久化數據存儲層、駐于內存的數據表現層、以及數據映射本身三者相互獨立、互不依賴。這個數據訪問層由一個或多個映射器(或者數據訪問對象)組成,用于實現數據傳輸。通用的數據訪問層可以處理不同的實體類型,而專用的則處理一個或幾個。
數據映射模式的核心在于它的數據模型遵循單一職責原則(Single Responsibility Principle), 這也是和 Active Record 模式的不同之處。最典型的數據映射模式例子就是數據庫 ORM 模型 (Object Relational Mapper)。
準確來說該模式是個架構模式。依賴注入(Dependency Injection)是控制反轉(Inversion of Control)的一種實現方式。門面模式
我們先來看看什么是控制反轉。
當調用者需要被調用者的協助時,在傳統的程序設計過程中,通常由調用者來創建被調用者的實例,但在這里,創建被調用者的工作不再由調用者來完成,而是將被調用者的創建移到調用者的外部,從而反轉被調用者的創建,消除了調用者對被調用者創建的控制,因此稱為控制反轉。
要實現控制反轉,通常的解決方案是將創建被調用者實例的工作交由 IoC 容器來完成,然后在調用者中注入被調用者(通過構造器/方法注入實現),這樣我們就實現了調用者與被調用者的解耦,該過程被稱為依賴注入。
依賴注入不是目的,它是一系列工具和手段,最終的目的是幫助我們開發出松散耦合(loose coupled)、可維護、可測試的代碼和程序。這條原則的做法是大家熟知的面向接口,或者說是面向抽象編程。門面模式(Facade)又稱外觀模式,用于為子系統中的一組接口提供一個一致的界面。門面模式定義了一個高層接口,這個接口使得子系統更加容易使用:引入門面角色之后,用戶只需要直接與門面角色交互,用戶與子系統之間的復雜關系由門面角色來實現,從而降低了系統的耦合度。示例代碼 Facade.phpbios = $bios; $this->os = $os; } /** * turn on the system */ public function turnOn() { $this->bios->execute(); $this->bios->waitForKeyPress(); $this->bios->launch($this->os); } /** * turn off the system */ public function turnOff() { $this->os->halt(); $this->bios->powerDown(); } }OsInterface.phpBiosInterface.php流接口模式在軟件工程中,流接口(Fluent Interface)是指實現一種面向對象的、能提高代碼可讀性的 API 的方法,其目的就是可以編寫具有自然語言一樣可讀性的代碼,我們對這種代碼編寫方式還有一個通俗的稱呼 —— 方法鏈。示例代碼 Sql.php
Laravel 中流接口模式有著廣泛使用,比如查詢構建器,郵件等等。fields = $fields; return $this; } /** * 添加 FROM 子句 * * @param string $table * @param string $alias * * @return SQL */ public function from($table, $alias) { $this->from[] = $table . " AS " . $alias; return $this; } /** * 添加 WHERE 條件 * * @param string $condition * * @return SQL */ public function where($condition) { $this->where[] = $condition; return $this; } /** * 生成查詢語句 * * @return string */ public function getQuery() { return "SELECT " . implode(",", $this->fields) . " FROM " . implode(",", $this->from) . " WHERE " . implode(" AND ", $this->where); } }注冊模式注冊模式(Registry)也叫做注冊樹模式,注冊器模式。注冊模式為應用中經常使用的對象創建一個中央存儲器來存放這些對象 —— 通常通過一個只包含靜態方法的抽象類來實現(或者通過單例模式)。示例代碼 Registry.php
責任鏈模式顧名思義,責任鏈模式(Chain of Responsibility Pattern)為請求創建了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬于行為型模式。介紹
在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求,那么它會把相同的請求傳給下一個接收者,依此類推。意圖: 避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,并且沿著這條鏈傳遞請求,直到有對象處理它為止。
主要解決: 職責鏈上的處理者負責處理請求,客戶只需要將請求發送到職責鏈上即可,無須關心請求的處理細節和請求的傳遞,所以職責鏈將請求的發送者和請求的處理者解耦了。
何時使用: 在處理消息的時候以過濾很多道。
如何解決: 攔截的類都實現統一接口。
關鍵代碼: Handler 里面聚合它自己,在 HandlerRequest 里判斷是否合適,如果沒達到條件則向下傳遞,向誰傳遞之前 set 進去。
應用實例:
紅樓夢中的 "擊鼓傳花"。
JS 中的事件冒泡。
JAVA WEB 中 Apache Tomcat 對 Encoding 的處理,Struts2 的攔截器,jsp servlet 的 Filter。
優點:
降低耦合度。它將請求的發送者和接收者解耦。
簡化了對象。使得對象不需要知道鏈的結構。
增強給對象指派職責的靈活性。通過改變鏈內的成員或者調動它們的次序,允許動態地新增或者刪除責任。
增加新的請求處理類很方便。
缺點:
不能保證請求一定被接收。
系統性能將受到一定影響,而且在進行代碼調試時不太方便,可能會造成循環調用。
可能不容易觀察運行時的特征,有礙于除錯。
使用場景:
有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定。
在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。
可動態指定一組對象處理請求。
注意事項: 在 JAVA WEB 中遇到很多應用。
命令模式命令模式(Command Pattern)是一種數據驅動的設計模式,它屬于行為型模式。請求以命令的形式包裹在對象中,并傳給調用對象。調用對象尋找可以處理該命令的合適的對象,并把該命令傳給相應的對象,該對象執行命令。介紹意圖: 將一個請求封裝成一個對象,從而使您可以用不同的請求對客戶進行參數化。
主要解決: 在軟件系統中,行為請求者與行為實現者通常是一種緊耦合的關系,但某些場合,比如需要對行為進行記錄、撤銷或重做、事務等處理時,這種無法抵御變化的緊耦合的設計就不太合適。
何時使用: 在某些場合,比如要對行為進行 "記錄、撤銷 / 重做、事務" 等處理,這種無法抵御變化的緊耦合是不合適的。在這種情況下,如何將 "行為請求者" 與 "行為實現者" 解耦?將一組行為抽象為對象,可以實現二者之間的松耦合。
如何解決: 通過調用者調用接受者執行命令,順序:調用者→接受者→命令。
關鍵代碼: 定義三個角色:
received 真正的命令執行對象
Command
invoker 使用命令對象的入口
應用實例: struts 1 中的 action 核心控制器 ActionServlet 只有一個,相當于 Invoker,而模型層的類會隨著不同的應用有不同的模型類,相當于具體的 Command。
優點:
降低了系統耦合度。
新的命令可以很容易添加到系統中去。
缺點: 使用命令模式可能會導致某些系統有過多的具體命令類。
使用場景: 認為是命令的地方都可以使用命令模式,比如:
GUI 中每一個按鈕都是一條命令。
模擬 CMD。
注意事項: 系統需要支持命令的撤銷 (Undo) 操作和恢復 (Redo) 操作,也可以考慮使用命令模式,見命令模式的擴展。
解釋器模式解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式,它屬于行為型模式。這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。介紹意圖: 給定一個語言,定義它的文法表示,并定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子。
主要解決: 對于一些固定文法構建一個解釋句子的解釋器。
何時使用: 如果一種特定類型的問題發生的頻率足夠高,那么可能就值得將該問題的各個實例表述為一個簡單語言中的句子。這樣就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題。
如何解決: 構建語法樹,定義終結符與非終結符。
關鍵代碼: 構建環境類,包含解釋器之外的一些全局信息,一般是 HashMap。
應用實例: 編譯器、運算表達式計算。
優點:
可擴展性比較好,靈活。
增加了新的解釋表達式的方式。
易于實現簡單文法。
缺點:
可利用場景比較少。
對于復雜的文法比較難維護。
解釋器模式會引起類膨脹。
解釋器模式采用遞歸調用方法。
使用場景:
可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹。
一些重復出現的問題可以用一種簡單的語言來進行表達。
一個簡單語法需要解釋的場景。
注意事項: 可利用場景比較少,JAVA 中如果碰到可以用 expression4J 代替。
迭代器模式迭代器模式(Iterator Pattern)是 Java 和 .Net 編程環境中非常常用的設計模式。這種模式用于順序訪問集合對象的元素,不需要知道集合對象的底層表示。介紹
迭代器模式屬于行為型模式。意圖: 提供一種方法順序訪問一個聚合對象中各個元素, 而又無須暴露該對象的內部表示。
主要解決: 不同的方式來遍歷整個整合對象。
何時使用: 遍歷一個聚合對象。
如何解決: 把在元素之間游走的責任交給迭代器,而不是聚合對象。
關鍵代碼: 定義接口:hasNext, next。
應用實例: JAVA 中的 iterator。
優點:
它支持以不同的方式遍歷一個聚合對象。
迭代器簡化了聚合類。
在同一個聚合上可以有多個遍歷。
在迭代器模式中,增加新的聚合類和迭代器類都很方便,無須修改原有代碼。
缺點: 由于迭代器模式將存儲數據和遍歷數據的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,這在一定程度上增加了系統的復雜性。
使用場景:
訪問一個聚合對象的內容而無須暴露它的內部表示。
需要為聚合對象提供多種遍歷方式。
為遍歷不同的聚合結構提供一個統一的接口。
注意事項: 迭代器模式就是分離了集合對象的遍歷行為,抽象出一個迭代器類來負責,這樣既可以做到不暴露集合的內部結構,又可讓外部代碼透明地訪問集合內部的數據。
中介者模式中介者模式(Mediator Pattern)是用來降低多個對象和類之間的通信復雜性。這種模式提供了一個中介類,該類通常處理不同類之間的通信,并支持松耦合,使代碼易于維護。中介者模式屬于行為型模式。介紹意圖: 用一個中介對象來封裝一系列的對象交互,中介者使各對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。
主要解決: 對象與對象之間存在大量的關聯關系,這樣勢必會導致系統的結構變得很復雜,同時若一個對象發生改變,我們也需要跟蹤與之相關聯的對象,同時做出相應的處理。
何時使用: 多個類相互耦合,形成了網狀結構。
如何解決: 將上述網狀結構分離為星型結構。
關鍵代碼: 對象 Colleague 之間的通信封裝到一個類中多帶帶處理。
應用實例:
中國加入 WTO 之前是各個國家相互貿易,結構復雜,現在是各個國家通過 WTO 來互相貿易。
機場調度系統。
MVC 框架,其中 C(控制器)就是 M(模型)和 V(視圖)的中介者。
優點:
降低了類的復雜度,將一對多轉化成了一對一。
各個類之間的解耦。
符合迪米特原則。
缺點: 中介者會龐大,變得復雜難以維護。
使用場景:
系統中對象之間存在比較復雜的引用關系,導致它們之間的依賴關系結構混亂而且難以復用該對象。
想通過一個中間類來封裝多個類中的行為,而又不想生成太多的子類。
注意事項: 不應當在職責混亂的時候使用。
備忘錄模式備忘錄模式(Memento Pattern)保存一個對象的某個狀態,以便在適當的時候恢復對象。備忘錄模式屬于行為型模式。
介紹意圖: 在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態。
主要解決: 所謂備忘錄模式就是在不破壞封裝的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態,這樣可以在以后將對象恢復到原先保存的狀態。
何時使用: 很多時候我們總是需要記錄一個對象的內部狀態,這樣做的目的就是為了允許用戶取消不確定或者錯誤的操作,能夠恢復到他原先的狀態,使得他有 "后悔藥" 可吃。
如何解決: 通過一個備忘錄類專門存儲對象狀態。
關鍵代碼: 客戶不與備忘錄類耦合,與備忘錄管理類耦合。
應用實例:
后悔藥。
打游戲時的存檔。
Windows 里的 ctri + z。
IE 中的后退。
數據庫的事務管理。
優點:
給用戶提供了一種可以恢復狀態的機制,可以使用戶能夠比較方便地回到某個歷史的狀態。
實現了信息的封裝,使得用戶不需要關心狀態的保存細節。
缺點: 消耗資源。如果類的成員變量過多,勢必會占用比較大的資源,而且每一次保存都會消耗一定的內存。
使用場景:
需要保存 / 恢復數據的相關狀態場景。
提供一個可回滾的操作。
注意事項:
為了符合迪米特原則,還要增加一個管理備忘錄的類。
為了節約內存,可使用原型模式 + 備忘錄模式。
觀察者模式當對象間存在一對多關系時,則使用觀察者模式(Observer Pattern)。比如,當一個對象被修改時,則會自動通知它的依賴對象。觀察者模式屬于行為型模式。介紹意圖: 定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并被自動更新。
主要解決: 一個對象狀態改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協作。
何時使用: 一個對象(目標對象)的狀態發生改變,所有的依賴對象(觀察者對象)都將得到通知,進行廣播通知。
如何解決: 使用面向對象技術,可以將這種依賴關系弱化。
關鍵代碼: 在抽象類里有一個 ArrayList 存放觀察者們。
應用實例:
拍賣的時候,拍賣師觀察最高標價,然后通知給其他競價者競價。
西游記里面悟空請求菩薩降服紅孩兒,菩薩灑了一地水招來一個老烏龜,這個烏龜就是觀察者,他觀察菩薩灑水這個動作。
優點:
觀察者和被觀察者是抽象耦合的。
建立一套觸發機制。
缺點:
如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。
觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎么發生變化的,而僅僅只是知道觀察目標發生了變化。
使用場景:
一個抽象模型有兩個方面,其中一個方面依賴于另一個方面。將這些方面封裝在獨立的對象中使它們可以各自獨立地改變和復用。
一個對象的改變將導致其他一個或多個對象也發生改變,而不知道具體有多少對象將發生改變,
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/31253.html
摘要:設計模式是軟件開發人員在軟件開發過程中面臨的一般問題的解決方案。設計模式的類型共有種設計模式。工廠模式工廠模式最常用的設計模式之一。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。 該文建議配合 design-patterns-for-humans 中文版 一起看。 推薦閱讀 超全的設計模式簡介(45種) design-patterns-for-humans 中文版...
摘要:參考配置添加,告訴程序在執行前要調用的服務或者也可以在修改配置文件,告訴程序在執行前要調用的服務參考鏈接 showImg(https://segmentfault.com/img/bVbt7t1?w=2880&h=2608); 推薦閱讀 Tideways、xhprof 和 xhgui 打造 PHP 非侵入式監控平臺 超全的設計模式簡介(45種) design-patterns-for...
摘要:參考配置添加,告訴程序在執行前要調用的服務或者也可以在修改配置文件,告訴程序在執行前要調用的服務參考鏈接 showImg(https://segmentfault.com/img/bVbt7t1?w=2880&h=2608); 推薦閱讀 Tideways、xhprof 和 xhgui 打造 PHP 非侵入式監控平臺 超全的設計模式簡介(45種) design-patterns-for...
閱讀 3920·2021-11-24 10:46
閱讀 1816·2021-11-16 11:44
閱讀 2289·2021-09-22 16:02
閱讀 1401·2019-08-30 15:55
閱讀 1131·2019-08-30 12:46
閱讀 566·2019-08-28 18:31
閱讀 2762·2019-08-26 18:38
閱讀 1094·2019-08-23 16:51