由于簡書字數(shù)文章限制,刪減了一部分,可以前往
github
查看全文 超全的設(shè)計模式簡介(45種)心墅,該文建議配合 design-patterns-for-humans 中文版 一起看枷遂。
推薦閱讀
- 超全的設(shè)計模式簡介(45種)
- design-patterns-for-humans 中文版(github 倉庫永久更新)
- MongoDB 資源卵渴、庫瓣赂、工具榆骚、應(yīng)用程序精選列表中文版
- 有哪些鮮為人知片拍,但是很有意思的網(wǎng)站煌集?
- 一份攻城獅筆記
- 每天搜集 Github 上優(yōu)秀的項目
- 一些有趣的民間故事
- 超好用的谷歌瀏覽器、Sublime Text捌省、Phpstorm苫纤、油猴插件合集
設(shè)計模式(Design pattern)代表了最佳的實踐,通常被有經(jīng)驗的面向?qū)ο蟮能浖_發(fā)人員所采用。設(shè)計模式是軟件開發(fā)人員在軟件開發(fā)過程中面臨的一般問題的解決方案卷拘。這些解決方案是眾多軟件開發(fā)人員經(jīng)過相當(dāng)長的一段時間的試驗和錯誤總結(jié)出來的喊废。
設(shè)計模式是一套被反復(fù)使用的、多數(shù)人知曉的栗弟、經(jīng)過分類編目的污筷、代碼設(shè)計經(jīng)驗的總結(jié)。使用設(shè)計模式是為了重用代碼乍赫、讓代碼更容易被他人理解瓣蛀、保證代碼可靠性。 毫無疑問雷厂,設(shè)計模式于己于他人于系統(tǒng)都是多贏的惋增,設(shè)計模式使代碼編制真正工程化,設(shè)計模式是軟件工程的基石改鲫,如同大廈的一塊塊磚石一樣诈皿。項目中合理地運用設(shè)計模式可以完美地解決很多問題,每種模式在現(xiàn)實中都有相應(yīng)的原理來與之對應(yīng)像棘,每種模式都描述了一個在我們周圍不斷重復(fù)發(fā)生的問題稽亏,以及該問題的核心解決方案,這也是設(shè)計模式能被廣泛應(yīng)用的原因缕题。
設(shè)計模式的類型
共有 23 種設(shè)計模式措左。這些模式可以分為三大類:
- 創(chuàng)建型模式(Creational Patterns)- 這些設(shè)計模式提供了一種在創(chuàng)建對象的同時隱藏創(chuàng)建邏輯的方式,而不是使用 new 運算符直接實例化對象避除。這使得程序在判斷針對某個給定實例需要創(chuàng)建哪些對象時更加靈活怎披。
-
結(jié)構(gòu)型模式(Structural Patterns)- 這些設(shè)計模式關(guān)注類和對象的組合。繼承的概念被用來組合接口和定義組合對象獲得新功能的方式瓶摆。
- 適配器模式(Adapter Pattern)
- 橋接模式(Bridge Pattern)
- 過濾器模式(Filter凉逛、Criteria Pattern)
- 組合模式(Composite Pattern)
- 裝飾器模式(Decorator Pattern)
- 外觀模式(Facade Pattern)
- 享元模式(Flyweight Pattern)
- 代理模式(Proxy Pattern)
- 數(shù)據(jù)映射模式 *(Data Mapper)
- 依賴注入模式 *(Dependency Injection)
- 門面模式 *(Facade)
- 流接口模式 *(Fluent Interface)
- 注冊模式 *(Registry)
-
行為型模式(Behavioral Patterns)- 這些設(shè)計模式特別關(guān)注對象之間的通信。
- 責(zé)任鏈模式(Chain of Responsibility Pattern)
- 命令模式(Command Pattern)
- 解釋器模式(Interpreter Pattern)
- 迭代器模式(Iterator Pattern)
- 中介者模式(Mediator Pattern)
- 備忘錄模式(Memento Pattern)
- 觀察者模式(Observer Pattern)
- 狀態(tài)模式(State Pattern)
- 空對象模式(Null Object Pattern)
- 策略模式(Strategy Pattern)
- 模板模式(Template Pattern)
- 訪問者模式(Visitor Pattern)
- 規(guī)格模式 *(Specification)
- 訪問者模式 *(Visitor)
-
J2EE 設(shè)計模式 - 這些設(shè)計模式特別關(guān)注表示層群井。這些模式是由 Sun Java Center 鑒定的状飞。
- MVC模式(MVC Pattern)
- 業(yè)務(wù)代表模式(Business Delegate Pattern)
- 組合實體模式(Composite Entity Pattern)
- 數(shù)據(jù)訪問對象模式(Data Access Object Pattern)
- 前端控制器模式(Front Controller Pattern)
- 攔截過濾器模式(Intercepting Filter Pattern)
- 服務(wù)定位器模式(Service Locator Pattern)
- 傳輸對象模式(Transfer Object Pattern)
- 委托模式 *(Delegation)
- 資源庫模式 *(Repository)
下面用一個圖片來整體描述一下設(shè)計模式之間的關(guān)系:
設(shè)計模式的六大原則
1、開閉原則(Open Close Principle)
開閉原則的意思是:對擴展開放书斜,對修改關(guān)閉诬辈。在程序需要進行拓展的時候,不能去修改原有的代碼荐吉,實現(xiàn)一個熱插拔的效果焙糟。簡言之,是為了使程序的擴展性好样屠,易于維護和升級穿撮。想要達到這樣的效果缺脉,我們需要使用接口和抽象類,后面的具體設(shè)計中我們會提到這點悦穿。
2攻礼、里氏代換原則(Liskov Substitution Principle)
里氏代換原則是面向?qū)ο笤O(shè)計的基本原則之一。 里氏代換原則中說栗柒,任何基類可以出現(xiàn)的地方礁扮,子類一定可以出現(xiàn)。LSP 是繼承復(fù)用的基石瞬沦,只有當(dāng)派生類可以替換掉基類深员,且軟件單位的功能不受到影響時,基類才能真正被復(fù)用蛙埂,而派生類也能夠在基類的基礎(chǔ)上增加新的行為倦畅。里氏代換原則是對開閉原則的補充。實現(xiàn)開閉原則的關(guān)鍵步驟就是抽象化绣的,而基類與子類的繼承關(guān)系就是抽象化的具體實現(xiàn)叠赐,所以里氏代換原則是對實現(xiàn)抽象化的具體步驟的規(guī)范。
3屡江、依賴倒轉(zhuǎn)原則(Dependence Inversion Principle)
這個原則是開閉原則的基礎(chǔ)芭概,具體內(nèi)容:針對接口編程,依賴于抽象而不依賴于具體惩嘉。
4罢洲、接口隔離原則(Interface Segregation Principle)
這個原則的意思是:使用多個隔離的接口,比使用單個接口要好文黎。它還有另外一個意思是:降低類之間的耦合度惹苗。由此可見,其實設(shè)計模式就是從大型軟件架構(gòu)出發(fā)耸峭、便于升級和維護的軟件設(shè)計思想桩蓉,它強調(diào)降低依賴,降低耦合劳闹。
5院究、迪米特法則,又稱最少知道原則(Demeter Principle)
最少知道原則是指:一個實體應(yīng)當(dāng)盡量少地與其他實體之間發(fā)生相互作用本涕,使得系統(tǒng)功能模塊相對獨立业汰。
6、合成復(fù)用原則(Composite Reuse Principle)
合成復(fù)用原則是指:盡量使用合成 / 聚合的方式菩颖,而不是使用繼承样漆。
工廠模式
工廠模式(Factory Pattern)最常用的設(shè)計模式之一。這種類型的設(shè)計模式屬于創(chuàng)建型模式位他,它提供了一種創(chuàng)建對象的最佳方式氛濒。
在工廠模式中产场,我們在創(chuàng)建對象時不會對客戶端暴露創(chuàng)建邏輯鹅髓,并且是通過使用一個共同的接口來指向新創(chuàng)建的對象舞竿。
介紹
意圖: 定義一個創(chuàng)建對象的接口,讓其子類自己決定實例化哪一個工廠類窿冯,工廠模式使其創(chuàng)建過程延遲到子類進行骗奖。
主要解決: 主要解決接口選擇的問題。
何時使用: 我們明確地計劃不同條件下創(chuàng)建不同實例時醒串。
如何解決: 讓其子類實現(xiàn)工廠接口执桌,返回的也是一個抽象的產(chǎn)品。
關(guān)鍵代碼: 創(chuàng)建過程在其子類執(zhí)行芜赌。
應(yīng)用實例:
- 您需要一輛汽車仰挣,可以直接從工廠里面提貨,而不用去管這輛汽車是怎么做出來的缠沈,以及這個汽車里面的具體實現(xiàn)膘壶。
- Hibernate 換數(shù)據(jù)庫只需換方言和驅(qū)動就可以。
優(yōu)點:
- 一個調(diào)用者想創(chuàng)建一個對象洲愤,只要知道其名稱就可以了颓芭。
- 擴展性高,如果想增加一個產(chǎn)品柬赐,只要擴展一個工廠類就可以亡问。
- 屏蔽產(chǎn)品的具體實現(xiàn),調(diào)用者只關(guān)心產(chǎn)品的接口肛宋。
缺點: 每次增加一個產(chǎn)品時州藕,都需要增加一個具體類和對象實現(xiàn)工廠,使得系統(tǒng)中類的個數(shù)成倍增加酝陈,在一定程度上增加了系統(tǒng)的復(fù)雜度慎框,同時也增加了系統(tǒng)具體類的依賴。這并不是什么好事后添。
使用場景:
- 日志記錄器:記錄可能記錄到本地硬盤笨枯、系統(tǒng)事件、遠程服務(wù)器等遇西,用戶可以選擇記錄日志到什么地方馅精。
- 數(shù)據(jù)庫訪問,當(dāng)用戶不知道最后系統(tǒng)采用哪一類數(shù)據(jù)庫粱檀,以及數(shù)據(jù)庫可能有變化時洲敢。
- 設(shè)計一個連接服務(wù)器的框架,需要三個協(xié)議茄蚯,"POP3"压彭、"IMAP"睦优、"HTTP",可以把這三個作為產(chǎn)品類壮不,共同實現(xiàn)一個接口汗盘。
注意事項: 作為一種創(chuàng)建類模式,在任何需要生成復(fù)雜對象的地方询一,都可以使用工廠方法模式隐孽。有一點需要注意的地方就是復(fù)雜對象適合使用工廠模式,而簡單對象健蕊,特別是只需要通過 new 就可以完成創(chuàng)建的對象菱阵,無需使用工廠模式。如果使用工廠模式缩功,就需要引入一個工廠類晴及,會增加系統(tǒng)的復(fù)雜度。
抽象工廠模式
抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠創(chuàng)建其他工廠嫡锌。該超級工廠又稱為其他工廠的工廠虑稼。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式世舰。
在抽象工廠模式中动雹,接口是負責(zé)創(chuàng)建一個相關(guān)對象的工廠,不需要顯式指定它們的類跟压。每個生成的工廠都能按照工廠模式提供對象胰蝠。
介紹
意圖: 提供一個創(chuàng)建一系列相關(guān)或相互依賴對象的接口,而無需指定它們具體的類震蒋。
主要解決: 主要解決接口選擇的問題茸塞。
何時使用: 系統(tǒng)的產(chǎn)品有多于一個的產(chǎn)品族,而系統(tǒng)只消費其中某一族的產(chǎn)品查剖。
如何解決: 在一個產(chǎn)品族里面钾虐,定義多個產(chǎn)品。
關(guān)鍵代碼: 在一個工廠里聚合多個同類產(chǎn)品笋庄。
應(yīng)用實例: 工作了效扫,為了參加一些聚會,肯定有兩套或多套衣服吧直砂,比如說有商務(wù)裝(成套菌仁,一系列具體產(chǎn)品)、時尚裝(成套静暂,一系列具體產(chǎn)品)济丘,甚至對于一個家庭來說,可能有商務(wù)女裝、商務(wù)男裝摹迷、時尚女裝疟赊、時尚男裝,這些也都是成套的峡碉,即一系列具體產(chǎn)品近哟。假設(shè)一種情況(現(xiàn)實中是不存在的,要不然异赫,沒法進入共產(chǎn)主義了椅挣,但有利于說明抽象工廠模式)头岔,在您的家中塔拳,某一個衣柜(具體工廠)只能存放某一種這樣的衣服(成套,一系列具體產(chǎn)品)峡竣,每次拿這種成套的衣服時也自然要從這個衣柜中取出了靠抑。用 OO 的思想去理解,所有的衣柜(具體工廠)都是衣柜類的(抽象工廠)某一個适掰,而每一件成套的衣服又包括具體的上衣(某一具體產(chǎn)品)颂碧,褲子(某一具體產(chǎn)品),這些具體的上衣其實也都是上衣(抽象產(chǎn)品)类浪,具體的褲子也都是褲子(另一個抽象產(chǎn)品)载城。
優(yōu)點: 當(dāng)一個產(chǎn)品族中的多個對象被設(shè)計成一起工作時,它能保證客戶端始終只使用同一個產(chǎn)品族中的對象费就。
缺點: 產(chǎn)品族擴展非常困難诉瓦,要增加一個系列的某一產(chǎn)品,既要在抽象的 Creator 里加代碼力细,又要在具體的里面加代碼睬澡。
使用場景:
- QQ 換皮膚,一整套一起換眠蚂。
- 生成不同操作系統(tǒng)的程序煞聪。
注意事項: 產(chǎn)品族難擴展,產(chǎn)品等級易擴展逝慧。
單例模式
單例模式(Singleton Pattern)是 Java 中最簡單的設(shè)計模式之一昔脯。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式笛臣。
這種模式涉及到一個單一的類云稚,該類負責(zé)創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建捐祠。這個類提供了一種訪問其唯一的對象的方式碱鳞,可以直接訪問,不需要實例化該類的對象踱蛀。
注意:
- 1窿给、單例類只能有一個實例贵白。
- 2、單例類必須自己創(chuàng)建自己的唯一實例崩泡。
- 3禁荒、單例類必須給所有其他對象提供這一實例。
介紹
意圖: 保證一個類僅有一個實例角撞,并提供一個訪問它的全局訪問點呛伴。
主要解決: 一個全局使用的類頻繁地創(chuàng)建與銷毀。
何時使用: 當(dāng)您想控制實例數(shù)目谒所,節(jié)省系統(tǒng)資源的時候葱轩。
如何解決: 判斷系統(tǒng)是否已經(jīng)有這個單例,如果有則返回祝迂,如果沒有則創(chuàng)建撵颊。
關(guān)鍵代碼: 構(gòu)造函數(shù)是私有的。
應(yīng)用實例:
- 一個班級只有一個班主任尖淘。
- Windows 是多進程多線程的奕锌,在操作一個文件的時候,就不可避免地出現(xiàn)多個進程或線程同時操作一個文件的現(xiàn)象村生,所以所有文件的處理必須通過唯一的實例來進行惊暴。
- 一些設(shè)備管理器常常設(shè)計為單例模式,比如一個電腦有兩臺打印機趁桃,在輸出的時候就要處理不能兩臺打印機打印同一個文件辽话。
優(yōu)點:
- 在內(nèi)存里只有一個實例,減少了內(nèi)存的開銷镇辉,尤其是頻繁的創(chuàng)建和銷毀實例(比如管理學(xué)院首頁頁面緩存)屡穗。
- 避免對資源的多重占用(比如寫文件操作)。
缺點: 沒有接口忽肛,不能繼承村砂,與單一職責(zé)原則沖突,一個類應(yīng)該只關(guān)心內(nèi)部邏輯屹逛,而不關(guān)心外面怎么樣來實例化础废。
使用場景:
- 要求生產(chǎn)唯一序列號。
- WEB 中的計數(shù)器罕模,不用每次刷新都在數(shù)據(jù)庫里加一次评腺,用單例先緩存起來。
- 創(chuàng)建的一個對象需要消耗的資源過多淑掌,比如 I/O 與數(shù)據(jù)庫的連接等蒿讥。
注意事項:getInstance() 方法中需要使用同步鎖 synchronized (Singleton.class) 防止多線程同時進入造成 instance 被多次實例化。
建造者模式
建造者模式(Builder Pattern)使用多個簡單的對象一步一步構(gòu)建成一個復(fù)雜的對象。這種類型的設(shè)計模式屬于創(chuàng)建型模式芋绸,它提供了一種創(chuàng)建對象的最佳方式媒殉。
一個 Builder 類會一步一步構(gòu)造最終的對象。該 Builder 類是獨立于其他對象的摔敛。
介紹
意圖: 將一個復(fù)雜的構(gòu)建與其表示相分離廷蓉,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
主要解決: 主要解決在軟件系統(tǒng)中马昙,有時候面臨著 "一個復(fù)雜對象" 的創(chuàng)建工作桃犬,其通常由各個部分的子對象用一定的算法構(gòu)成;由于需求的變化行楞,這個復(fù)雜對象的各個部分經(jīng)常面臨著劇烈的變化攒暇,但是將它們組合在一起的算法卻相對穩(wěn)定。
何時使用: 一些基本部件不會變敢伸,而其組合經(jīng)常變化的時候扯饶。
如何解決: 將變與不變分離開恒削。
關(guān)鍵代碼: 建造者:創(chuàng)建和提供實例池颈,導(dǎo)演:管理建造出來的實例的依賴關(guān)系。
應(yīng)用實例:
- 去肯德基钓丰,漢堡躯砰、可樂、薯條携丁、炸雞翅等是不變的琢歇,而其組合是經(jīng)常變化的,生成出所謂的 "套餐"梦鉴。
- JAVA 中的 StringBuilder李茫。
優(yōu)點:
- 建造者獨立,易擴展肥橙。
- 便于控制細節(jié)風(fēng)險魄宏。
缺點:
- 產(chǎn)品必須有共同點,范圍有限制存筏。
- 如內(nèi)部變化復(fù)雜宠互,會有很多的建造類。
使用場景:
- 需要生成的對象具有復(fù)雜的內(nèi)部結(jié)構(gòu)椭坚。
- 需要生成的對象內(nèi)部屬性本身相互依賴予跌。
注意事項: 與工廠模式的區(qū)別是:建造者模式更加關(guān)注與零件裝配的順序。
原型模式
原型模式(Prototype Pattern)是用于創(chuàng)建重復(fù)的對象善茎,同時又能保證性能券册。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
這種模式是實現(xiàn)了一個原型接口烁焙,該接口用于創(chuàng)建當(dāng)前對象的克隆略吨。當(dāng)直接創(chuàng)建對象的代價比較大時,則采用這種模式考阱。例如翠忠,一個對象需要在一個高代價的數(shù)據(jù)庫操作之后被創(chuàng)建。我們可以緩存該對象乞榨,在下一個請求時返回它的克隆秽之,在需要的時候更新數(shù)據(jù)庫,以此來減少數(shù)據(jù)庫調(diào)用吃既。
介紹
意圖: 用原型實例指定創(chuàng)建對象的種類考榨,并且通過拷貝這些原型創(chuàng)建新的對象。
主要解決: 在運行期建立和刪除原型鹦倚。
何時使用:
- 當(dāng)一個系統(tǒng)應(yīng)該獨立于它的產(chǎn)品創(chuàng)建河质,構(gòu)成和表示時。
- 當(dāng)要實例化的類是在運行時刻指定時震叙,例如掀鹅,通過動態(tài)裝載。
- 為了避免創(chuàng)建一個與產(chǎn)品類層次平行的工廠類層次時媒楼。
- 當(dāng)一個類的實例只能有幾個不同狀態(tài)組合中的一種時乐尊。建立相應(yīng)數(shù)目的原型并克隆它們可能比每次用合適的狀態(tài)手工實例化該類更方便一些。
如何解決: 利用已有的一個原型對象划址,快速地生成和原型對象一樣的實例扔嵌。
關(guān)鍵代碼:
- 實現(xiàn)克隆操作,在 JAVA 繼承 Cloneable夺颤,重寫 clone()痢缎,在 .NET 中可以使用 Object 類的 MemberwiseClone() 方法來實現(xiàn)對象的淺拷貝或通過序列化的方式來實現(xiàn)深拷貝。
- 原型模式同樣用于隔離類對象的使用者和具體類型(易變類)之間的耦合關(guān)系世澜,它同樣要求這些 "易變類" 擁有穩(wěn)定的接口独旷。
應(yīng)用實例:
- 細胞分裂。
- JAVA 中的 Object clone() 方法宜狐。
優(yōu)點:
- 性能提高势告。
- 逃避構(gòu)造函數(shù)的約束。
缺點:
- 配備克隆方法需要對類的功能進行通盤考慮抚恒,這對于全新的類不是很難咱台,但對于已有的類不一定很容易,特別當(dāng)一個類引用不支持串行化的間接對象俭驮,或者引用含有循環(huán)結(jié)構(gòu)的時候回溺。
- 必須實現(xiàn) Cloneable 接口春贸。
使用場景:
- 資源優(yōu)化場景。
- 類初始化需要消化非常多的資源遗遵,這個資源包括數(shù)據(jù). 硬件資源等萍恕。
- 性能和安全要求的場景。
- 通過 new 產(chǎn)生一個對象需要非常繁瑣的數(shù)據(jù)準備或訪問權(quán)限车要,則可以使用原型模式允粤。
- 一個對象多個修改者的場景。
- 一個對象需要提供給其他對象訪問翼岁,而且各個調(diào)用者可能都需要修改其值時类垫,可以考慮使用原型模式拷貝多個對象供調(diào)用者使用。
- 在實際項目中琅坡,原型模式很少單獨出現(xiàn)悉患,一般是和工廠方法模式一起出現(xiàn),通過 clone 的方法創(chuàng)建一個對象榆俺,然后由工廠方法提供給調(diào)用者售躁。原型模式已經(jīng)與 Java 融為渾然一體,大家可以隨手拿來使用茴晋。
注意事項: 與通過對一個類進行實例化來構(gòu)造新對象不同的是陪捷,原型模式是通過拷貝一個現(xiàn)有對象生成新對象的。淺拷貝實現(xiàn) Cloneable晃跺,重寫揩局,深拷貝是通過實現(xiàn) Serializable 讀取二進制流。
對象池模式
對象池(也稱為資源池)被用來管理對象緩存掀虎。對象池是一組已經(jīng)初始化過且可以直接使用的對象集合,用戶在使用對象時可以從對象池中獲取對象付枫,對其進行操作處理烹玉,并在不需要時歸還給對象池而非銷毀它。
若對象初始化阐滩、實例化的代價高二打,且需要經(jīng)常實例化,但每次實例化的數(shù)量較少的情況下掂榔,使用對象池可以獲得顯著的性能提升继效。常見的使用對象池模式的技術(shù)包括線程池、數(shù)據(jù)庫連接池装获、任務(wù)隊列池瑞信、圖片資源對象池等。
當(dāng)然穴豫,如果要實例化的對象較小凡简,不需要多少資源開銷逼友,就沒有必要使用對象池模式了,這非但不會提升性能秤涩,反而浪費內(nèi)存空間帜乞,甚至降低性能。
示例代碼
Pool.php
<?php
namespace DesignPatterns\Creational\Pool;
class Pool
{
private $instances = array();
private $class;
public function __construct($class)
{
$this->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
<?php
namespace DesignPatterns\Creational\Pool;
class Processor
{
private $pool;
private $processing = 0;
private $maxProcesses = 3;
private $waitingQueue = [];
public function __construct(Pool $pool)
{
$this->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
<?php
namespace DesignPatterns\Creational\Pool;
class Worker
{
public function __construct()
{
// let's say that constuctor does really expensive work...
// for example creates "thread"
}
public function run($image, array $callback)
{
// do something with $image...
// and when it's done, execute callback
call_user_func($callback, $this);
}
}
多例模式
多例模式和單例模式類似筐眷,但可以返回多個實例黎烈。比如我們有多個數(shù)據(jù)庫連接,MySQL匀谣、SQLite怨喘、Postgres,又或者我們有多個日志記錄器振定,分別用于記錄調(diào)試信息和錯誤信息必怜,這些都可以使用多例模式實現(xiàn)。
示例代碼
Multiton.php
<?php
namespace DesignPatterns\Creational\Multiton;
/**
* Multiton類
*/
class Multiton
{
/**
*
* 第一個實例
*/
const INSTANCE_1 = '1';
/**
*
* 第二個實例
*/
const INSTANCE_2 = '2';
/**
* 實例數(shù)組
*
* @var array
*/
private static $instances = array();
/**
* 構(gòu)造函數(shù)是私有的后频,不能從外部進行實例化
*
*/
private function __construct()
{
}
/**
* 通過指定名稱返回實例(使用到該實例的時候才會實例化)
*
* @param string $instanceName
*
* @return Multiton
*/
public static function getInstance($instanceName)
{
if (!array_key_exists($instanceName, self::$instances)) {
self::$instances[$instanceName] = new self();
}
return self::$instances[$instanceName];
}
/**
* 防止實例從外部被克隆
*
* @return void
*/
private function __clone()
{
}
/**
* 防止實例從外部反序列化
*
* @return void
*/
private function __wakeup()
{
}
}
靜態(tài)工廠模式
與簡單工廠類似梳庆,該模式用于創(chuàng)建一組相關(guān)或依賴的對象,不同之處在于靜態(tài)工廠模式使用一個靜態(tài)方法來創(chuàng)建所有類型的對象卑惜,該靜態(tài)方法通常是 factory 或 build膏执。
示例代碼
StaticFactory.php
<?php
namespace DesignPatterns\Creational\StaticFactory;
class StaticFactory
{
/**
* 通過傳入?yún)?shù)創(chuàng)建相應(yīng)對象實例
*
* @param string $type
*
* @static
*
* @throws \InvalidArgumentException
* @return FormatterInterface
*/
public static function factory($type)
{
$className = __NAMESPACE__ . '\Format' . ucfirst($type);
if (!class_exists($className)) {
throw new \InvalidArgumentException('Missing format class.');
}
return new $className();
}
}
FormatterInterface.php
<?php
namespace DesignPatterns\Creational\StaticFactory;
/**
* FormatterInterface接口
*/
interface FormatterInterface
{
}
FormatString.php
<?php
namespace DesignPatterns\Creational\StaticFactory;
/**
* FormatNumber類
*/
class FormatNumber implements FormatterInterface
{
}
適配器模式
適配器模式(Adapter Pattern)是作為兩個不兼容的接口之間的橋梁。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式露久,它結(jié)合了兩個獨立接口的功能更米。
這種模式涉及到一個單一的類,該類負責(zé)加入獨立的或不兼容的接口功能毫痕。舉個真實的例子征峦,讀卡器是作為內(nèi)存卡和筆記本之間的適配器。您將內(nèi)存卡插入讀卡器消请,再將讀卡器插入筆記本栏笆,這樣就可以通過筆記本來讀取內(nèi)存卡。
介紹
意圖: 將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口臊泰。適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作蛉加。
主要解決: 主要解決在軟件系統(tǒng)中,常常要將一些 "現(xiàn)存的對象" 放到新的環(huán)境中缸逃,而新環(huán)境要求的接口是現(xiàn)對象不能滿足的针饥。
何時使用:
- 系統(tǒng)需要使用現(xiàn)有的類,而此類的接口不符合系統(tǒng)的需要需频。
- 想要建立一個可以重復(fù)使用的類丁眼,用于與一些彼此之間沒有太大關(guān)聯(lián)的一些類,包括一些可能在將來引進的類一起工作贺辰,這些源類不一定有一致的接口户盯。
- 通過接口轉(zhuǎn)換嵌施,將一個類插入另一個類系中。(比如老虎和飛禽莽鸭,現(xiàn)在多了一個飛虎吗伤,在不增加實體的需求下,增加一個適配器硫眨,在里面包容一個虎對象足淆,實現(xiàn)飛的接口。)
如何解決: 繼承或依賴(推薦)礁阁。
關(guān)鍵代碼: 適配器繼承或依賴已有的對象巧号,實現(xiàn)想要的目標接口。
應(yīng)用實例:
- 美國電器 110V姥闭,中國 220V丹鸿,就要有一個適配器將 110V 轉(zhuǎn)化為 220V。
- JAVA JDK 1.1 提供了 Enumeration 接口棚品,而在 1.2 中提供了 Iterator 接口靠欢,想要使用 1.2 的 JDK,則要將以前系統(tǒng)的 Enumeration 接口轉(zhuǎn)化為 Iterator 接口铜跑,這時就需要適配器模式门怪。
- 在 LINUX 上運行 WINDOWS 程序。 4. JAVA 中的 jdbc锅纺。
優(yōu)點:
- 可以讓任何兩個沒有關(guān)聯(lián)的類一起運行掷空。
- 提高了類的復(fù)用。
- 增加了類的透明度囤锉。
- 靈活性好坦弟。
缺點:
- 過多地使用適配器,會讓系統(tǒng)非常零亂嚼锄,不易整體進行把握减拭。比如,明明看到調(diào)用的是 A 接口区丑,其實內(nèi)部被適配成了 B 接口的實現(xiàn),一個系統(tǒng)如果太多出現(xiàn)這種情況修陡,無異于一場災(zāi)難沧侥。因此如果不是很有必要,可以不使用適配器魄鸦,而是直接對系統(tǒng)進行重構(gòu)宴杀。
- 由于 JAVA 至多繼承一個類,所以至多只能適配一個適配者類拾因,而且目標類必須是抽象類旺罢。
使用場景: 有動機地修改一個正常運行的系統(tǒng)的接口旷余,這時應(yīng)該考慮使用適配器模式。
注意事項: 適配器不是在詳細設(shè)計時添加的扁达,而是解決正在服役的項目的問題正卧。
橋接模式
橋接(Bridge)是用于把抽象化與實現(xiàn)化解耦,使得二者可以獨立變化跪解。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式炉旷,它通過提供抽象化和實現(xiàn)化之間的橋接結(jié)構(gòu),來實現(xiàn)二者的解耦叉讥。
這種模式涉及到一個作為橋接的接口窘行,使得實體類的功能獨立于接口實現(xiàn)類。這兩種類型的類可被結(jié)構(gòu)化改變而互不影響图仓。
介紹
意圖: 將抽象部分與實現(xiàn)部分分離罐盔,使它們都可以獨立的變化。
主要解決: 在有多種可能會變化的情況下救崔,用繼承會造成類爆炸問題惶看,擴展起來不靈活。
何時使用: 實現(xiàn)系統(tǒng)可能有多個角度分類帚豪,每一種角度都可能變化碳竟。
如何解決: 把這種多角度分類分離出來,讓它們獨立變化狸臣,減少它們之間耦合莹桅。
關(guān)鍵代碼: 抽象類依賴實現(xiàn)類。
應(yīng)用實例:
- 豬八戒從天蓬元帥轉(zhuǎn)世投胎到豬烛亦,轉(zhuǎn)世投胎的機制將塵世劃分為兩個等級诈泼,即:靈魂和肉體,前者相當(dāng)于抽象化煤禽,后者相當(dāng)于實現(xiàn)化铐达。生靈通過功能的委派,調(diào)用肉體對象的功能檬果,使得生靈可以動態(tài)地選擇瓮孙。
- 墻上的開關(guān),可以看到的開關(guān)是抽象的选脊,不用管里面具體怎么實現(xiàn)的杭抠。
優(yōu)點:
- 抽象和實現(xiàn)的分離。
- 優(yōu)秀的擴展能力恳啥。
- 實現(xiàn)細節(jié)對客戶透明偏灿。
缺點: 橋接模式的引入會增加系統(tǒng)的理解與設(shè)計難度,由于聚合關(guān)聯(lián)關(guān)系建立在抽象層钝的,要求開發(fā)者針對抽象進行設(shè)計與編程翁垂。
使用場景:
- 如果一個系統(tǒng)需要在構(gòu)件的抽象化角色和具體化角色之間增加更多的靈活性铆遭,避免在兩個層次之間建立靜態(tài)的繼承聯(lián)系,通過橋接模式可以使它們在抽象層建立一個關(guān)聯(lián)關(guān)系沿猜。
- 對于那些不希望使用繼承或因為多層次繼承導(dǎo)致系統(tǒng)類的個數(shù)急劇增加的系統(tǒng)枚荣,橋接模式尤為適用。
- 一個類存在兩個獨立變化的維度邢疙,且這兩個維度都需要進行擴展棍弄。
注意事項: 對于兩個獨立變化的維度,使用橋接模式再適合不過了疟游。
過濾器模式
過濾器模式(Filter Pattern)或標準模式(Criteria Pattern)是一種設(shè)計模式呼畸,這種模式允許開發(fā)人員使用不同的標準來過濾一組對象,通過邏輯運算以解耦的方式把它們連接起來颁虐。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式侥袜,它結(jié)合多個標準來獲得單一標準虾标。
組合模式
組合模式(Composite Pattern)萄传,又叫部分整體模式南吮,是用于把一組相似的對象當(dāng)作一個單一的對象。組合模式依據(jù)樹形結(jié)構(gòu)來組合對象笋籽,用來表示部分以及整體層次蹦漠。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式,它創(chuàng)建了對象組的樹形結(jié)構(gòu)车海。
這種模式創(chuàng)建了一個包含自己對象組的類笛园。該類提供了修改相同對象組的方式。
介紹
意圖: 將對象組合成樹形結(jié)構(gòu)以表示 "部分 - 整體" 的層次結(jié)構(gòu)侍芝。組合模式使得用戶對單個對象和組合對象的使用具有一致性研铆。
主要解決: 它在我們樹型結(jié)構(gòu)的問題中,模糊了簡單元素和復(fù)雜元素的概念州叠,客戶程序可以向處理簡單元素一樣來處理復(fù)雜元素棵红,從而使得客戶程序與復(fù)雜元素的內(nèi)部結(jié)構(gòu)解耦。
何時使用:
- 您想表示對象的部分 - 整體層次結(jié)構(gòu)(樹形結(jié)構(gòu))咧栗。
- 您希望用戶忽略組合對象與單個對象的不同逆甜,用戶將統(tǒng)一地使用組合結(jié)構(gòu)中的所有對象。
如何解決: 樹枝和葉子實現(xiàn)統(tǒng)一接口致板,樹枝內(nèi)部組合該接口忆绰。
關(guān)鍵代碼: 樹枝內(nèi)部組合該接口,并且含有內(nèi)部屬性 List可岂,里面放 Component。
應(yīng)用實例:
- 算術(shù)表達式包括操作數(shù). 操作符和另一個操作數(shù)翰灾,其中缕粹,另一個操作符也可以是操作數(shù). 操作符和另一個操作數(shù)稚茅。
- 在 JAVA AWT 和 SWING 中,對于 Button 和 Checkbox 是樹葉平斩,Container 是樹枝亚享。
優(yōu)點:
- 高層模塊調(diào)用簡單。
- 節(jié)點自由增加绘面。
缺點: 在使用組合模式時欺税,其葉子和樹枝的聲明都是實現(xiàn)類,而不是接口揭璃,違反了依賴倒置原則晚凿。
使用場景: 部分. 整體場景,如樹形菜單瘦馍,文件. 文件夾的管理歼秽。
注意事項: 定義時為具體類。
裝飾器模式
裝飾器模式(Decorator Pattern)允許向一個現(xiàn)有的對象添加新的功能情组,同時又不改變其結(jié)構(gòu)燥筷。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類的一個包裝院崇。
這種模式創(chuàng)建了一個裝飾類肆氓,用來包裝原有的類,并在保持類方法簽名完整性的前提下底瓣,提供了額外的功能谢揪。
介紹
意圖: 動態(tài)地給一個對象添加一些額外的職責(zé)。就增加功能來說濒持,裝飾器模式相比生成子類更為靈活键耕。
主要解決: 一般的,我們?yōu)榱藬U展一個類經(jīng)常使用繼承方式實現(xiàn)柑营,由于繼承為類引入靜態(tài)特征屈雄,并且隨著擴展功能的增多,子類會很膨脹官套。
何時使用: 在不想增加很多子類的情況下擴展類酒奶。
如何解決: 將具體功能職責(zé)劃分,同時繼承裝飾者模式奶赔。
關(guān)鍵代碼:
- Component 類充當(dāng)抽象角色惋嚎,不應(yīng)該具體實現(xiàn)。
- 修飾類引用和繼承 Component 類站刑,具體擴展類重寫父類方法另伍。
應(yīng)用實例:
- 孫悟空有 72 變,當(dāng)他變成 "廟宇" 后,他的根本還是一只猴子摆尝,但是他又有了廟宇的功能温艇。
- 不論一幅畫有沒有畫框都可以掛在墻上,但是通常都是有畫框的堕汞,并且實際上是畫框被掛在墻上勺爱。在掛在墻上之前,畫可以被蒙上玻璃讯检,裝到框子里琐鲁;這時畫、玻璃和畫框形成了一個物體人灼。
優(yōu)點: 裝飾類和被裝飾類可以獨立發(fā)展围段,不會相互耦合,裝飾模式是繼承的一個替代模式挡毅,裝飾模式可以動態(tài)擴展一個實現(xiàn)類的功能蒜撮。
缺點: 多層裝飾比較復(fù)雜。
使用場景:
- 擴展一個類的功能跪呈。
- 動態(tài)增加功能段磨,動態(tài)撤銷。
注意事項: 可代替繼承耗绿。
外觀模式
外觀模式(Facade Pattern)隱藏系統(tǒng)的復(fù)雜性苹支,并向客戶端提供了一個客戶端可以訪問系統(tǒng)的接口。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式误阻,它向現(xiàn)有的系統(tǒng)添加一個接口债蜜,來隱藏系統(tǒng)的復(fù)雜性。
這種模式涉及到一個單一的類究反,該類提供了客戶端請求的簡化方法和對現(xiàn)有系統(tǒng)類方法的委托調(diào)用寻定。
介紹
意圖: 為子系統(tǒng)中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口精耐,這個接口使得這一子系統(tǒng)更加容易使用狼速。
主要解決: 降低訪問復(fù)雜系統(tǒng)的內(nèi)部子系統(tǒng)時的復(fù)雜度,簡化客戶端與之的接口卦停。
何時使用:
- 客戶端不需要知道系統(tǒng)內(nèi)部的復(fù)雜聯(lián)系向胡,整個系統(tǒng)只需提供一個 "接待員" 即可。
- 定義系統(tǒng)的入口惊完。
如何解決: 客戶端不與系統(tǒng)耦合僵芹,外觀類與系統(tǒng)耦合。
關(guān)鍵代碼: 在客戶端和復(fù)雜系統(tǒng)之間再加一層小槐,這一層將調(diào)用順序. 依賴關(guān)系等處理好拇派。
應(yīng)用實例:
- 去醫(yī)院看病,可能要去掛號、門診攀痊、劃價桐腌、取藥,讓患者或患者家屬覺得很復(fù)雜苟径,如果有提供接待人員,只讓接待人員來處理躬审,就很方便棘街。
- JAVA 的三層開發(fā)模式。
優(yōu)點:
- 減少系統(tǒng)相互依賴承边。
- 提高靈活性遭殉。
- 提高了安全性。
缺點: 不符合開閉原則博助,如果要改東西很麻煩险污,繼承重寫都不合適。
使用場景:
- 為復(fù)雜的模塊或子系統(tǒng)提供外界訪問的模塊富岳。
- 子系統(tǒng)相對獨立蛔糯。
- 預(yù)防低水平人員帶來的風(fēng)險。
注意事項: 在層次化結(jié)構(gòu)中窖式,可以使用外觀模式定義系統(tǒng)中每一層的入口蚁飒。
享元模式
享元模式(Flyweight Pattern)主要用于減少創(chuàng)建對象的數(shù)量,以減少內(nèi)存占用和提高性能萝喘。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式淮逻,它提供了減少對象數(shù)量從而改善應(yīng)用所需的對象結(jié)構(gòu)的方式。
享元模式嘗試重用現(xiàn)有的同類對象阁簸,如果未找到匹配的對象爬早,則創(chuàng)建新對象。我們將通過創(chuàng)建 5 個對象來畫出 20 個分布于不同位置的圓來演示這種模式启妹。由于只有 5 種可用的顏色筛严,所以 color 屬性被用來檢查現(xiàn)有的 Circle 對象。
介紹
意圖: 運用共享技術(shù)有效地支持大量細粒度的對象翅溺。
主要解決: 在有大量對象時脑漫,有可能會造成內(nèi)存溢出,我們把其中共同的部分抽象出來咙崎,如果有相同的業(yè)務(wù)請求优幸,直接返回在內(nèi)存中已有的對象,避免重新創(chuàng)建褪猛。
何時使用:
- 系統(tǒng)中有大量對象网杆。
- 這些對象消耗大量內(nèi)存。
- 這些對象的狀態(tài)大部分可以外部化。
- 這些對象可以按照內(nèi)蘊狀態(tài)分為很多組碳却,當(dāng)把外蘊對象從對象中剔除出來時队秩,每一組對象都可以用一個對象來代替。
- 系統(tǒng)不依賴于這些對象身份昼浦,這些對象是不可分辨的馍资。
如何解決: 用唯一標識碼判斷,如果在內(nèi)存中有关噪,則返回這個唯一標識碼所標識的對象鸟蟹。
關(guān)鍵代碼: 用 HashMap 存儲這些對象。
應(yīng)用實例:
- JAVA 中的 String使兔,如果有則返回建钥,如果沒有則創(chuàng)建一個字符串保存在字符串緩存池里面。2. 數(shù)據(jù)庫的數(shù)據(jù)池虐沥。
優(yōu)點: 大大減少對象的創(chuàng)建熊经,降低系統(tǒng)的內(nèi)存,使效率提高欲险。
缺點: 提高了系統(tǒng)的復(fù)雜度镐依,需要分離出外部狀態(tài)和內(nèi)部狀態(tài),而且外部狀態(tài)具有固有化的性質(zhì)盯荤,不應(yīng)該隨著內(nèi)部狀態(tài)的變化而變化馋吗,否則會造成系統(tǒng)的混亂。
使用場景:
- 系統(tǒng)有大量相似對象秋秤。
- 需要緩沖池的場景宏粤。
注意事項:
- 注意劃分外部狀態(tài)和內(nèi)部狀態(tài),否則可能會引起線程安全問題灼卢。
- 這些類必須有一個工廠對象加以控制绍哎。
代理模式
在代理模式(Proxy Pattern)中,一個類代表另一個類的功能鞋真。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式崇堰。
在代理模式中,我們創(chuàng)建具有現(xiàn)有對象的對象涩咖,以便向外界提供功能接口海诲。
介紹
意圖: 為其他對象提供一種代理以控制對這個對象的訪問。
主要解決: 在直接訪問對象時帶來的問題檩互,比如說:要訪問的對象在遠程的機器上特幔。在面向?qū)ο笙到y(tǒng)中,有些對象由于某些原因(比如對象創(chuàng)建開銷很大闸昨,或者某些操作需要安全控制蚯斯,或者需要進程外的訪問)薄风,直接訪問會給使用者或者系統(tǒng)結(jié)構(gòu)帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層拍嵌。
何時使用: 想在訪問一個類時做一些控制遭赂。
如何解決: 增加中間層。
關(guān)鍵代碼: 實現(xiàn)與被代理類組合横辆。
應(yīng)用實例:
- Windows 里面的快捷方式撇他。
- 豬八戒去找高翠蘭結(jié)果是孫悟空變的,可以這樣理解:把高翠蘭的外貌抽象出來龄糊,高翠蘭本人和孫悟空都實現(xiàn)了這個接口逆粹,豬八戒訪問高翠蘭的時候看不出來這個是孫悟空,所以說孫悟空是高翠蘭代理類炫惩。
- 買火車票不一定在火車站買,也可以去代售點阿浓。
- 一張支票或銀行存單是賬戶中資金的代理他嚷。支票在市場交易中用來代替現(xiàn)金,并提供對簽發(fā)人賬號上資金的控制芭毙。
- spring aop筋蓖。
優(yōu)點:
- 職責(zé)清晰。
- 高擴展性退敦。
- 智能化粘咖。
缺點:
- 由于在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢侈百。
- 實現(xiàn)代理模式需要額外的工作瓮下,有些代理模式的實現(xiàn)非常復(fù)雜。
使用場景: 按職責(zé)來劃分钝域,通常有以下使用場景:
- 遠程代理讽坏。
- 虛擬代理。
- Copy-on-Write 代理例证。
- 保護(Protect or Access)代理路呜。
- Cache 代理。
- 防火墻(Firewall)代理织咧。
- 同步化(Synchronization)代理胀葱。
- 智能引用(Smart Reference)代理。
注意事項:
- 和適配器模式的區(qū)別:適配器模式主要改變所考慮對象的接口笙蒙,而代理模式不能改變所代理類的接口抵屿。
- 和裝飾器模式的區(qū)別:裝飾器模式為了增強功能,而代理模式是為了加以控制手趣。
數(shù)據(jù)映射模式
在了解數(shù)據(jù)映射模式之前晌该,先了解下數(shù)據(jù)映射肥荔,它是在持久化數(shù)據(jù)存儲層(通常是關(guān)系型數(shù)據(jù)庫)和駐于內(nèi)存的數(shù)據(jù)表現(xiàn)層之間進行雙向數(shù)據(jù)傳輸?shù)臄?shù)據(jù)訪問層。
數(shù)據(jù)映射模式的目的是讓持久化數(shù)據(jù)存儲層朝群、駐于內(nèi)存的數(shù)據(jù)表現(xiàn)層燕耿、以及數(shù)據(jù)映射本身三者相互獨立、互不依賴姜胖。這個數(shù)據(jù)訪問層由一個或多個映射器(或者數(shù)據(jù)訪問對象)組成誉帅,用于實現(xiàn)數(shù)據(jù)傳輸。通用的數(shù)據(jù)訪問層可以處理不同的實體類型右莱,而專用的則處理一個或幾個蚜锨。
數(shù)據(jù)映射模式的核心在于它的數(shù)據(jù)模型遵循單一職責(zé)原則(Single Responsibility Principle), 這也是和 Active Record 模式的不同之處。最典型的數(shù)據(jù)映射模式例子就是數(shù)據(jù)庫 ORM 模型 (Object Relational Mapper)慢蜓。
準確來說該模式是個架構(gòu)模式亚再。
依賴注入模式
依賴注入(Dependency Injection)是控制反轉(zhuǎn)(Inversion of Control)的一種實現(xiàn)方式。
我們先來看看什么是控制反轉(zhuǎn)晨抡。
當(dāng)調(diào)用者需要被調(diào)用者的協(xié)助時氛悬,在傳統(tǒng)的程序設(shè)計過程中,通常由調(diào)用者來創(chuàng)建被調(diào)用者的實例耘柱,但在這里如捅,創(chuàng)建被調(diào)用者的工作不再由調(diào)用者來完成,而是將被調(diào)用者的創(chuàng)建移到調(diào)用者的外部调煎,從而反轉(zhuǎn)被調(diào)用者的創(chuàng)建镜遣,消除了調(diào)用者對被調(diào)用者創(chuàng)建的控制,因此稱為控制反轉(zhuǎn)士袄。
要實現(xiàn)控制反轉(zhuǎn)悲关,通常的解決方案是將創(chuàng)建被調(diào)用者實例的工作交由 IoC 容器來完成,然后在調(diào)用者中注入被調(diào)用者(通過構(gòu)造器/方法注入實現(xiàn))窖剑,這樣我們就實現(xiàn)了調(diào)用者與被調(diào)用者的解耦坚洽,該過程被稱為依賴注入。
依賴注入不是目的西土,它是一系列工具和手段讶舰,最終的目的是幫助我們開發(fā)出松散耦合(loose coupled)、可維護需了、可測試的代碼和程序跳昼。這條原則的做法是大家熟知的面向接口,或者說是面向抽象編程肋乍。
門面模式
門面模式(Facade)又稱外觀模式鹅颊,用于為子系統(tǒng)中的一組接口提供一個一致的界面。門面模式定義了一個高層接口墓造,這個接口使得子系統(tǒng)更加容易使用:引入門面角色之后堪伍,用戶只需要直接與門面角色交互锚烦,用戶與子系統(tǒng)之間的復(fù)雜關(guān)系由門面角色來實現(xiàn),從而降低了系統(tǒng)的耦合度帝雇。
示例代碼
Facade.php
<?php
namespace DesignPatterns\Structural\Facade;
/**
* 門面類
*/
class Facade
{
/**
* @var OsInterface
*/
protected $os;
/**
* @var BiosInterface
*/
protected $bios;
/**
* This is the perfect time to use a dependency injection container
* to create an instance of this class
*
* @param BiosInterface $bios
* @param OsInterface $os
*/
public function __construct(BiosInterface $bios, OsInterface $os)
{
$this->bios = $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.php
<?php
namespace DesignPatterns\Structural\Facade;
/**
* OsInterface接口
*/
interface OsInterface
{
/**
* halt the OS
*/
public function halt();
}
BiosInterface.php
<?php
namespace DesignPatterns\Structural\Facade;
/**
* BiosInterface接口
*/
interface BiosInterface
{
/**
* execute the BIOS
*/
public function execute();
/**
* wait for halt
*/
public function waitForKeyPress();
/**
* launches the OS
*
* @param OsInterface $os
*/
public function launch(OsInterface $os);
/**
* power down BIOS
*/
public function powerDown();
}
流接口模式
在軟件工程中涮俄,流接口(Fluent Interface)是指實現(xiàn)一種面向?qū)ο蟮摹⒛芴岣叽a可讀性的 API 的方法尸闸,其目的就是可以編寫具有自然語言一樣可讀性的代碼彻亲,我們對這種代碼編寫方式還有一個通俗的稱呼 —— 方法鏈。
Laravel 中流接口模式有著廣泛使用吮廉,比如查詢構(gòu)建器苞尝,郵件等等。
示例代碼
Sql.php
<?php
namespace DesignPatterns\Structural\FluentInterface;
/**
* SQL 類
*/
class Sql
{
/**
* @var array
*/
protected $fields = array();
/**
* @var array
*/
protected $from = array();
/**
* @var array
*/
protected $where = array();
/**
* 添加 select 字段
*
* @param array $fields
*
* @return SQL
*/
public function select(array $fields = array())
{
$this->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)也叫做注冊樹模式宦芦,注冊器模式宙址。注冊模式為應(yīng)用中經(jīng)常使用的對象創(chuàng)建一個中央存儲器來存放這些對象 —— 通常通過一個只包含靜態(tài)方法的抽象類來實現(xiàn)(或者通過單例模式)。
示例代碼
Registry.php
<?php
namespace DesignPatterns\Structural\Registry;
/**
* class Registry
*/
abstract class Registry
{
const LOGGER = 'logger';
/**
* @var array
*/
protected static $storedValues = array();
/**
* sets a value
*
* @param string $key
* @param mixed $value
*
* @static
* @return void
*/
public static function set($key, $value)
{
self::$storedValues[$key] = $value;
}
/**
* gets a value from the registry
*
* @param string $key
*
* @static
* @return mixed
*/
public static function get($key)
{
return self::$storedValues[$key];
}
// typically there would be methods to check if a key has already been registered and so on ...
}
責(zé)任鏈模式
顧名思義调卑,責(zé)任鏈模式(Chain of Responsibility Pattern)為請求創(chuàng)建了一個接收者對象的鏈曼氛。這種模式給予請求的類型,對請求的發(fā)送者和接收者進行解耦令野。這種類型的設(shè)計模式屬于行為型模式。
在這種模式中徽级,通常每個接收者都包含對另一個接收者的引用气破。如果一個對象不能處理該請求,那么它會把相同的請求傳給下一個接收者餐抢,依此類推现使。
介紹
意圖: 避免請求發(fā)送者與接收者耦合在一起,讓多個對象都有可能接收請求旷痕,將這些對象連接成一條鏈碳锈,并且沿著這條鏈傳遞請求,直到有對象處理它為止欺抗。
主要解決: 職責(zé)鏈上的處理者負責(zé)處理請求售碳,客戶只需要將請求發(fā)送到職責(zé)鏈上即可,無須關(guān)心請求的處理細節(jié)和請求的傳遞绞呈,所以職責(zé)鏈將請求的發(fā)送者和請求的處理者解耦了贸人。
何時使用: 在處理消息的時候以過濾很多道。
如何解決: 攔截的類都實現(xiàn)統(tǒng)一接口佃声。
關(guān)鍵代碼: Handler 里面聚合它自己艺智,在 HandlerRequest 里判斷是否合適,如果沒達到條件則向下傳遞圾亏,向誰傳遞之前 set 進去十拣。
應(yīng)用實例:
- 紅樓夢中的 "擊鼓傳花"封拧。
- JS 中的事件冒泡。
- JAVA WEB 中 Apache Tomcat 對 Encoding 的處理夭问,Struts2 的攔截器辑甜,jsp servlet 的 Filter琅束。
優(yōu)點:
- 降低耦合度。它將請求的發(fā)送者和接收者解耦。
- 簡化了對象扫尺。使得對象不需要知道鏈的結(jié)構(gòu)。
- 增強給對象指派職責(zé)的靈活性间聊。通過改變鏈內(nèi)的成員或者調(diào)動它們的次序笤虫,允許動態(tài)地新增或者刪除責(zé)任。
- 增加新的請求處理類很方便直撤。
缺點:
- 不能保證請求一定被接收非竿。
- 系統(tǒng)性能將受到一定影響,而且在進行代碼調(diào)試時不太方便谋竖,可能會造成循環(huán)調(diào)用红柱。
- 可能不容易觀察運行時的特征,有礙于除錯蓖乘。
使用場景:
- 有多個對象可以處理同一個請求锤悄,具體哪個對象處理該請求由運行時刻自動確定。
- 在不明確指定接收者的情況下嘉抒,向多個對象中的一個提交一個請求零聚。
- 可動態(tài)指定一組對象處理請求。
注意事項: 在 JAVA WEB 中遇到很多應(yīng)用些侍。
命令模式
命令模式(Command Pattern)是一種數(shù)據(jù)驅(qū)動的設(shè)計模式隶症,它屬于行為型模式。請求以命令的形式包裹在對象中岗宣,并傳給調(diào)用對象蚂会。調(diào)用對象尋找可以處理該命令的合適的對象,并把該命令傳給相應(yīng)的對象耗式,該對象執(zhí)行命令胁住。
介紹
意圖: 將一個請求封裝成一個對象,從而使您可以用不同的請求對客戶進行參數(shù)化纽什。
主要解決: 在軟件系統(tǒng)中措嵌,行為請求者與行為實現(xiàn)者通常是一種緊耦合的關(guān)系,但某些場合芦缰,比如需要對行為進行記錄企巢、撤銷或重做、事務(wù)等處理時让蕾,這種無法抵御變化的緊耦合的設(shè)計就不太合適浪规。
何時使用: 在某些場合或听,比如要對行為進行 "記錄、撤銷 / 重做笋婿、事務(wù)" 等處理誉裆,這種無法抵御變化的緊耦合是不合適的。在這種情況下缸濒,如何將 "行為請求者" 與 "行為實現(xiàn)者" 解耦足丢?將一組行為抽象為對象,可以實現(xiàn)二者之間的松耦合庇配。
如何解決: 通過調(diào)用者調(diào)用接受者執(zhí)行命令斩跌,順序:調(diào)用者→接受者→命令。
關(guān)鍵代碼: 定義三個角色:
- received 真正的命令執(zhí)行對象
- Command
- invoker 使用命令對象的入口
應(yīng)用實例: struts 1 中的 action 核心控制器 ActionServlet 只有一個捞慌,相當(dāng)于 Invoker耀鸦,而模型層的類會隨著不同的應(yīng)用有不同的模型類,相當(dāng)于具體的 Command啸澡。
優(yōu)點:
- 降低了系統(tǒng)耦合度袖订。
- 新的命令可以很容易添加到系統(tǒng)中去。
缺點: 使用命令模式可能會導(dǎo)致某些系統(tǒng)有過多的具體命令類嗅虏。
使用場景: 認為是命令的地方都可以使用命令模式洛姑,比如:
- GUI 中每一個按鈕都是一條命令。
- 模擬 CMD皮服。
注意事項: 系統(tǒng)需要支持命令的撤銷 (Undo) 操作和恢復(fù) (Redo) 操作吏口,也可以考慮使用命令模式,見命令模式的擴展冰更。
解釋器模式
解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式,它屬于行為型模式昂勒。這種模式實現(xiàn)了一個表達式接口蜀细,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析戈盈、符號處理引擎等奠衔。
介紹
意圖: 給定一個語言,定義它的文法表示塘娶,并定義一個解釋器归斤,這個解釋器使用該標識來解釋語言中的句子。
主要解決: 對于一些固定文法構(gòu)建一個解釋句子的解釋器刁岸。
何時使用: 如果一種特定類型的問題發(fā)生的頻率足夠高脏里,那么可能就值得將該問題的各個實例表述為一個簡單語言中的句子。這樣就可以構(gòu)建一個解釋器虹曙,該解釋器通過解釋這些句子來解決該問題迫横。
如何解決: 構(gòu)建語法樹番舆,定義終結(jié)符與非終結(jié)符。
關(guān)鍵代碼: 構(gòu)建環(huán)境類矾踱,包含解釋器之外的一些全局信息恨狈,一般是 HashMap。
應(yīng)用實例: 編譯器呛讲、運算表達式計算禾怠。
優(yōu)點:
- 可擴展性比較好,靈活贝搁。
- 增加了新的解釋表達式的方式吗氏。
- 易于實現(xiàn)簡單文法。
缺點:
- 可利用場景比較少徘公。
- 對于復(fù)雜的文法比較難維護牲证。
- 解釋器模式會引起類膨脹。
- 解釋器模式采用遞歸調(diào)用方法关面。
使用場景:
- 可以將一個需要解釋執(zhí)行的語言中的句子表示為一個抽象語法樹坦袍。
- 一些重復(fù)出現(xiàn)的問題可以用一種簡單的語言來進行表達。
- 一個簡單語法需要解釋的場景等太。
注意事項: 可利用場景比較少捂齐,JAVA 中如果碰到可以用 expression4J 代替。
迭代器模式
迭代器模式(Iterator Pattern)是 Java 和 .Net 編程環(huán)境中非常常用的設(shè)計模式缩抡。這種模式用于順序訪問集合對象的元素奠宜,不需要知道集合對象的底層表示。
迭代器模式屬于行為型模式瞻想。
介紹
意圖: 提供一種方法順序訪問一個聚合對象中各個元素, 而又無須暴露該對象的內(nèi)部表示压真。
主要解決: 不同的方式來遍歷整個整合對象。
何時使用: 遍歷一個聚合對象蘑险。
如何解決: 把在元素之間游走的責(zé)任交給迭代器滴肿,而不是聚合對象。
關(guān)鍵代碼: 定義接口:hasNext, next佃迄。
應(yīng)用實例: JAVA 中的 iterator泼差。
優(yōu)點:
- 它支持以不同的方式遍歷一個聚合對象。
- 迭代器簡化了聚合類呵俏。
- 在同一個聚合上可以有多個遍歷堆缘。
- 在迭代器模式中,增加新的聚合類和迭代器類都很方便普碎,無須修改原有代碼吼肥。
缺點: 由于迭代器模式將存儲數(shù)據(jù)和遍歷數(shù)據(jù)的職責(zé)分離,增加新的聚合類需要對應(yīng)增加新的迭代器類,類的個數(shù)成對增加潜沦,這在一定程度上增加了系統(tǒng)的復(fù)雜性萄涯。
使用場景:
- 訪問一個聚合對象的內(nèi)容而無須暴露它的內(nèi)部表示。
- 需要為聚合對象提供多種遍歷方式唆鸡。
- 為遍歷不同的聚合結(jié)構(gòu)提供一個統(tǒng)一的接口涝影。
注意事項: 迭代器模式就是分離了集合對象的遍歷行為,抽象出一個迭代器類來負責(zé)争占,這樣既可以做到不暴露集合的內(nèi)部結(jié)構(gòu)燃逻,又可讓外部代碼透明地訪問集合內(nèi)部的數(shù)據(jù)。
中介者模式
中介者模式(Mediator Pattern)是用來降低多個對象和類之間的通信復(fù)雜性臂痕。這種模式提供了一個中介類伯襟,該類通常處理不同類之間的通信,并支持松耦合握童,使代碼易于維護姆怪。中介者模式屬于行為型模式。
介紹
意圖: 用一個中介對象來封裝一系列的對象交互澡绩,中介者使各對象不需要顯式地相互引用稽揭,從而使其耦合松散,而且可以獨立地改變它們之間的交互肥卡。
主要解決: 對象與對象之間存在大量的關(guān)聯(lián)關(guān)系溪掀,這樣勢必會導(dǎo)致系統(tǒng)的結(jié)構(gòu)變得很復(fù)雜,同時若一個對象發(fā)生改變步鉴,我們也需要跟蹤與之相關(guān)聯(lián)的對象揪胃,同時做出相應(yīng)的處理。
何時使用: 多個類相互耦合氛琢,形成了網(wǎng)狀結(jié)構(gòu)喊递。
如何解決: 將上述網(wǎng)狀結(jié)構(gòu)分離為星型結(jié)構(gòu)。
關(guān)鍵代碼: 對象 Colleague 之間的通信封裝到一個類中單獨處理阳似。
應(yīng)用實例:
- 中國加入 WTO 之前是各個國家相互貿(mào)易册舞,結(jié)構(gòu)復(fù)雜,現(xiàn)在是各個國家通過 WTO 來互相貿(mào)易障般。
- 機場調(diào)度系統(tǒng)。
- MVC 框架盛杰,其中 C(控制器)就是 M(模型)和 V(視圖)的中介者挽荡。
優(yōu)點:
- 降低了類的復(fù)雜度,將一對多轉(zhuǎn)化成了一對一即供。
- 各個類之間的解耦定拟。
- 符合迪米特原則。
缺點: 中介者會龐大,變得復(fù)雜難以維護青自。
使用場景:
- 系統(tǒng)中對象之間存在比較復(fù)雜的引用關(guān)系株依,導(dǎo)致它們之間的依賴關(guān)系結(jié)構(gòu)混亂而且難以復(fù)用該對象。
- 想通過一個中間類來封裝多個類中的行為延窜,而又不想生成太多的子類恋腕。
注意事項: 不應(yīng)當(dāng)在職責(zé)混亂的時候使用。
備忘錄模式
備忘錄模式(Memento Pattern)保存一個對象的某個狀態(tài)逆瑞,以便在適當(dāng)?shù)臅r候恢復(fù)對象荠藤。備忘錄模式屬于行為型模式。
介紹
意圖: 在不破壞封裝性的前提下获高,捕獲一個對象的內(nèi)部狀態(tài)哈肖,并在該對象之外保存這個狀態(tài)。
主要解決: 所謂備忘錄模式就是在不破壞封裝的前提下念秧,捕獲一個對象的內(nèi)部狀態(tài)淤井,并在該對象之外保存這個狀態(tài),這樣可以在以后將對象恢復(fù)到原先保存的狀態(tài)摊趾。
何時使用: 很多時候我們總是需要記錄一個對象的內(nèi)部狀態(tài)币狠,這樣做的目的就是為了允許用戶取消不確定或者錯誤的操作,能夠恢復(fù)到他原先的狀態(tài)严就,使得他有 "后悔藥" 可吃总寻。
如何解決: 通過一個備忘錄類專門存儲對象狀態(tài)。
關(guān)鍵代碼: 客戶不與備忘錄類耦合梢为,與備忘錄管理類耦合渐行。
應(yīng)用實例:
- 后悔藥。
- 打游戲時的存檔铸董。
- Windows 里的 ctri + z祟印。
- IE 中的后退。
- 數(shù)據(jù)庫的事務(wù)管理粟害。
優(yōu)點:
- 給用戶提供了一種可以恢復(fù)狀態(tài)的機制蕴忆,可以使用戶能夠比較方便地回到某個歷史的狀態(tài)。
- 實現(xiàn)了信息的封裝悲幅,使得用戶不需要關(guān)心狀態(tài)的保存細節(jié)套鹅。
缺點: 消耗資源。如果類的成員變量過多汰具,勢必會占用比較大的資源卓鹿,而且每一次保存都會消耗一定的內(nèi)存。
使用場景:
- 需要保存 / 恢復(fù)數(shù)據(jù)的相關(guān)狀態(tài)場景留荔。
- 提供一個可回滾的操作吟孙。
注意事項:
- 為了符合迪米特原則,還要增加一個管理備忘錄的類。
- 為了節(jié)約內(nèi)存杰妓,可使用原型模式 + 備忘錄模式藻治。
觀察者模式
當(dāng)對象間存在一對多關(guān)系時,則使用觀察者模式(Observer Pattern)巷挥。比如桩卵,當(dāng)一個對象被修改時,則會自動通知它的依賴對象句各。觀察者模式屬于行為型模式吸占。
介紹
意圖: 定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時凿宾,所有依賴于它的對象都得到通知并被自動更新矾屯。
主要解決: 一個對象狀態(tài)改變給其他對象通知的問題,而且要考慮到易用和低耦合初厚,保證高度的協(xié)作件蚕。
何時使用: 一個對象(目標對象)的狀態(tài)發(fā)生改變,所有的依賴對象(觀察者對象)都將得到通知产禾,進行廣播通知排作。
如何解決: 使用面向?qū)ο蠹夹g(shù),可以將這種依賴關(guān)系弱化亚情。
關(guān)鍵代碼: 在抽象類里有一個 ArrayList 存放觀察者們妄痪。
應(yīng)用實例:
- 拍賣的時候,拍賣師觀察最高標價楞件,然后通知給其他競價者競價衫生。
- 西游記里面悟空請求菩薩降服紅孩兒,菩薩灑了一地水招來一個老烏龜土浸,這個烏龜就是觀察者罪针,他觀察菩薩灑水這個動作。
優(yōu)點:
- 觀察者和被觀察者是抽象耦合的黄伊。
- 建立一套觸發(fā)機制泪酱。
缺點:
- 如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間还最。
- 如果在觀察者和觀察目標之間有循環(huán)依賴的話墓阀,觀察目標會觸發(fā)它們之間進行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰拓轻。
- 觀察者模式?jīng)]有相應(yīng)的機制讓觀察者知道所觀察的目標對象是怎么發(fā)生變化的斯撮,而僅僅只是知道觀察目標發(fā)生了變化。
使用場景:
- 一個抽象模型有兩個方面悦即,其中一個方面依賴于另一個方面。將這些方面封裝在獨立的對象中使它們可以各自獨立地改變和復(fù)用。
- 一個對象的改變將導(dǎo)致其他一個或多個對象也發(fā)生改變辜梳,而不知道具體有多少對象將發(fā)生改變粱甫,可以降低對象之間的耦合度。
- 一個對象必須通知其他對象作瞄,而并不知道這些對象是誰茶宵。
- 需要在系統(tǒng)中創(chuàng)建一個觸發(fā)鏈,A 對象的行為將影響 B 對象宗挥,B 對象的行為將影響 C 對象……乌庶,可以使用觀察者模式創(chuàng)建一種鏈式觸發(fā)機制。
注意事項:
- JAVA 中已經(jīng)有了對觀察者模式的支持類契耿。
- 避免循環(huán)引用瞒大。
- 如果順序執(zhí)行,某一觀察者錯誤會導(dǎo)致系統(tǒng)卡殼搪桂,一般采用異步方式透敌。
狀態(tài)模式
在狀態(tài)模式(State Pattern)中,類的行為是基于它的狀態(tài)改變的踢械。這種類型的設(shè)計模式屬于行為型模式酗电。
在狀態(tài)模式中,我們創(chuàng)建表示各種狀態(tài)的對象和一個行為隨著狀態(tài)對象改變而改變的 context 對象内列。
介紹
意圖: 允許對象在內(nèi)部狀態(tài)發(fā)生改變時改變它的行為撵术,對象看起來好像修改了它的類。
主要解決: 對象的行為依賴于它的狀態(tài)(屬性)话瞧,并且可以根據(jù)它的狀態(tài)改變而改變它的相關(guān)行為嫩与。
何時使用: 代碼中包含大量與對象狀態(tài)有關(guān)的條件語句。
如何解決: 將各種具體的狀態(tài)類抽象出來移稳。
關(guān)鍵代碼: 通常命令模式的接口中只有一個方法蕴纳。而狀態(tài)模式的接口中有一個或者多個方法。而且个粱,狀態(tài)模式的實現(xiàn)類的方法古毛,一般返回值,或者是改變實例變量的值都许。也就是說稻薇,狀態(tài)模式一般和對象的狀態(tài)有關(guān)。實現(xiàn)類的方法有不同的功能胶征,覆蓋接口中的方法塞椎。狀態(tài)模式和命令模式一樣,也可以用于消除 if...else 等條件選擇語句睛低。
應(yīng)用實例:
- 打籃球的時候運動員可以有正常狀態(tài). 不正常狀態(tài)和超常狀態(tài)案狠。
- 曾侯乙編鐘中服傍,'鐘是抽象接口','鐘 A'等是具體狀態(tài),'曾侯乙編鐘'是具體環(huán)境(Context)骂铁。
優(yōu)點:
- 封裝了轉(zhuǎn)換規(guī)則吹零。
- 枚舉可能的狀態(tài),在枚舉狀態(tài)之前需要確定狀態(tài)種類拉庵。
- 將所有與某個狀態(tài)有關(guān)的行為放到一個類中灿椅,并且可以方便地增加新的狀態(tài),只需要改變對象狀態(tài)即可改變對象的行為钞支。
- 允許狀態(tài)轉(zhuǎn)換邏輯與狀態(tài)對象合成一體茫蛹,而不是某一個巨大的條件語句塊。
- 可以讓多個環(huán)境對象共享一個狀態(tài)對象烁挟,從而減少系統(tǒng)中對象的個數(shù)婴洼。
缺點:
- 狀態(tài)模式的使用必然會增加系統(tǒng)類和對象的個數(shù)。
- 狀態(tài)模式的結(jié)構(gòu)與實現(xiàn)都較為復(fù)雜信夫,如果使用不當(dāng)將導(dǎo)致程序結(jié)構(gòu)和代碼的混亂窃蹋。
- 狀態(tài)模式對 "開閉原則" 的支持并不太好,對于可以切換狀態(tài)的狀態(tài)模式静稻,增加新的狀態(tài)類需要修改那些負責(zé)狀態(tài)轉(zhuǎn)換的源代碼警没,否則無法切換到新增狀態(tài),而且修改某個狀態(tài)類的行為也需修改對應(yīng)類的源代碼振湾。
使用場景:
- 行為隨狀態(tài)改變而改變的場景杀迹。
- 條件、分支語句的代替者押搪。
注意事項: 在行為受狀態(tài)約束的時候使用狀態(tài)模式树酪,而且狀態(tài)不超過 5 個。