一、剩下的模式 1、橋接模式(Bridge Pattern) 概念:不只改變你的實現,也改變你的抽象。橋接模式的主要特點是把抽象(Abstraction)與行為實現(Implementation)分離開來,從而可以保持各部分的獨立性以及應對他們的功能擴展。 角色: 1. 實現類介面(Im ...
一、剩下的模式
1、橋接模式(Bridge Pattern)
- 概念:不只改變你的實現,也改變你的抽象。橋接模式的主要特點是把抽象(Abstraction)與行為實現(Implementation)分離開來,從而可以保持各部分的獨立性以及應對他們的功能擴展。
- 角色:
1. 實現類介面(Implementor):定義行為實現介面。
2. 具體實現類(ConcreteImplementor):實現 Implementor 介面,提供不同的行為實現。
3. 橋接抽象類(Abstraction):用於定義抽象類的介面,它一般是抽象類而不是介面,其中定義了一個Implementor(實現類介面)類型的對象並可以維護該對象,它與Implementor之間具有關聯關係,它既可以包含抽象業務方法,也可以包含具體業務方法。
4. 擴充抽象類(RefinedAbstraction):擴充由 Abstraction 定義的介面,通常情況下它不再是抽象類而是具體類,它實現了在 Abstraction 中聲明的抽象業務方法,在 RefinedAbstraction 中可以調用在 Implementor 中定義的業務方法。
UML圖:
- 優點:將抽象與行為實現解耦,二者可以獨立擴展,不會影響到對方。
- 適用場景:當需要使用不同的方式改變介面和實現時。
Demo 實現:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/bridge
2、建造者模式(Builder Pattern)
- 概念:封裝一個產品的構造過程,並允許按步驟構造。建造者模式可以將一個產品的內部表象與產品的生成過程分割開來,從而使一個建造過程生成具有不同的內部表象的產品對象。
- 角色:
1. 抽象建造者(Builder):給出一個抽象介面,以規範產品對象的各個組成成分的建造。
2. 具體建造者(Concrete Builder):實現 Builder 角色提供的介面,一步一步完成創建產品實例的過程;在建造過程完成後,提供產品的實例。
3. 指導者(Director):與客戶端打交道,調用具體建造者角色以創建產品對象,指導者並沒有產品類的具體知識。
4. 產品(Product):具體建造者構建的複雜對象。
UML圖:
- 優點:
1. 將一個複雜對象的創建過程封裝起來;向客戶隱藏產品內部的實現。
2. 允許對象通過多個步驟來創建,並且可以改變過程(這和只有一個步驟的工廠模式不同)。
3. 產品的實現可以被替換,因為客戶只看到一個抽象的介面。
- 建造者模式和模板方法模式的比較區別?
1. 建造者是使用組合方式實現不同的表示,而模板方法使用的是繼承的方式。
2. 建造者抽象的是構建過程,而模板方法提取的是實現公共。
Demo 實現:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/builder
3、責任鏈模式(Chain of Responsibility Pattern)
- 概念:讓一個以上的對象有機會能夠處理某個請求。責任鏈模式為請求創建了一條接收者對象的鏈,每個接收者都包含對另一個接收者的引用,當某個接受者不能處理該請求時,會將該請求轉給下一個接受者處理。
- 角色:
1. 抽象請求處理者(Handler):是所有具體請求處理者的父類。
2. 具體請求處理者(Concrete Handler):實現抽象請求處理者,包含下一個具體請求處理者的引用。 UML圖:
- 優點:
1. 弱化了發出請求的人和處理請求的人之間的關係。發出請求的人只需要向第一個具體的處理者發送請求,然後就可以不用管了,處理者會在責任鏈上自己尋找處理的方法。
2. 通過改變鏈內的成員或調動他們的次序,允許你動態地新增或刪除責任。
- 缺點:請求需要在責任鏈上傳播責任,直至找到合適的處理對象。這樣雖然提高了程式的靈活性,但同時也出現了處理的延遲。
- 應用場景:在視窗系統中,經常會使用到責任鏈模式,尤其是事件的處理,熟悉 javaScript 開發的朋友,可能會知道,瀏覽器中的事件有冒泡機制,就是事件的是向父控制項傳播的,如果自己處理不了,就會傳播給父控制項去處理。
tips:Struts 的攔截器,Servlet 的過濾器,Netty 的 ChannelPipeline 都是責任鏈模式~
4、蠅量模式(Flyweight Pattern)
- 概念:讓某個類的一個實例能用來提供許多“虛擬實例”。蠅量模式主要用於減少創建對象的數量,以減少記憶體占用和提高性能。蠅量模式嘗試重用現有的同類對象,如果未找到匹配的對象,則創建新對象。
- 角色:
1. 抽象蠅量(Flyweight):定義了對蠅量對象外部狀態的操作介面。使用該介面,可在Client中修改蠅量對象的外部狀態。
2. 可共用內部狀態的具體蠅量(ConcreteFlyweight):維護可以共用的內部狀態;實現操作外部狀態的介面。
3. 無可共用內部狀態的具體蠅量(UnsharedConcreteFlyweight):無內部狀態,所以只實現操作外部狀態的介面。
4. 蠅量工廠(FlyweightFactory):負責管理蠅量對象,形成一個對象池,提供對取出對象的方法,取出時若池中對象足夠,就直接返回,若對象不足則創建後返回。
UML:
- 優點:
1. 減少運行時對象實例的個數,節省記憶體。
2. 將許多“虛擬”對象(可共用內部狀態的具體蠅量)的狀態集中管理。
- 缺點:單個的邏輯實例將無法擁有獨立而不同的行為。
- 應用場景:當一個類有許多的實例,而這些實例能被同一方法控制的時候。
Demo 實現:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/flyweight
5、解釋器模式(Interpreter Pattern)
- 概念:給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
- 角色:
1. 抽象表達式(Expression):聲明一個所有的具體表達式都需要實現的抽象介面;這個介面主要是一個interpret()方法,稱做解釋操作。
2. 終結符表達式(Terminal Expression):終結符表達式,實現了抽象表達式所要求的介面;文法中的每一個終結符都有一個具體終結表達式與之相對應。比如公式 R = R1 + R2,R1 和 R2 就是終結符,對應的解析 R1 和 R2 的解釋器就是終結符表達式。
3. 非終結符表達式(Nonterminal Expression):文法中的每一條規則都需要一個具體的非終結符表達式,非終結符表達式一般是文法中的運算符或者其他關鍵字,比如公式 R = R1 + R2 中,“+"就是非終結符,解析“+”的解釋器就是一個非終結符表達式。
4. 上下文(Context):它的任務一般是用來存放文法中各個終結符所對應的具體值,比如 R = R1 + R2,給 R1 賦值100,給 R2 賦值200,這些信息需要存放到上下文中。
UML:
- 優點:
1. 將每一個語法規則表示成一個類,方便於實現語言。
2. 因為語法由許多類表示,所以你可以輕易的改變或者擴展此語言。
3. 通過在類結構中加入新的方法,可以在解釋的同時增加新的行為。例如列印格式的美化或者進行複雜的程式驗證。
- 應用場景:
1. 解釋器模式在平常使用的較少,可以用來處理腳本語言和編程語言。
2. 當你需要實現一個簡單的語言時,或者簡單比效率更重要時,使用解釋器模式。
Demo 實現:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/interpreter
6、中介者模式(Mediator Pattern)
- 概念:來集中相關對象之間複雜的溝通和控制方式。中介者模式定義了一個中介對象來封裝一系列對象之間的交互關係。中介者使各個對象之間不需要顯式地相互引用,從而使耦合性降低,而且可以獨立地改變它們之間的交互行為。
- 角色:
1. 抽象中介者(Mediator)角色:它是中介者的介面,提供了同事對象註冊與轉發同事對象信息的抽象方法。
2. 具體中介者(ConcreteMediator)角色:實現中介者介面,定義一個 List 來管理同事對象,協調各個同事角色之間的交互關係,因此它依賴於同事角色。
3. 抽象同事類(Colleague)角色:定義同事類的介面,保存中介者對象,提供同事對象交互的抽象方法,實現所有相互影響的同事類的公共功能。
4. 具體同事類(Concrete Colleague)角色:是抽象同事類的實現者,當需要與其他同事對象交互時,由中介者對象負責後續的交互。 UML:
- 優點:
1. 通過將對象彼此解耦,可以增加對象的復用性。
2. 通過將控制邏輯集中,可以簡化系統維護。
3. 將對象間的一對多關聯轉變為一對一的關聯,可以讓對象之間所傳遞的消息變得簡單而且大幅減少。
- 缺點:如果設計不當,中介者對象本身會變得過於複雜。
- 應用場景:
1. 中介者模式常常被用來協調相關的 GUI 組件。
2. 當一組對象要進行溝通或者業務上的交互,但是其關係卻又很複雜混亂時。
Demo 實現:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/mediator
7、備忘錄模式(Memento Pattern)
- 概念:在不破壞封閉的前提下,捕獲一個對象的內部狀態,併在該對象之外保存這個狀態。備忘錄模式的目標包括:儲存系統關鍵對象的重要狀態;維護關鍵對象的封裝。
- 角色:
1. 備忘錄(Memento)角色:備忘錄角色存儲“備忘發起角色”的內部狀態。“備忘發起角色”根據需要決定備忘錄角色存儲“備忘發起角色”的哪些內部狀態。為了防止“備忘發起角色”以外的其他對象訪問備忘錄。備忘錄實際上有兩個介面,“備忘錄管理者角色”只能看到備忘錄提供的窄介面——對於備忘錄角色中存放的屬性是不可見的。“備忘發起角色”則能夠看到一個寬介面——能夠得到自己放入備忘錄角色中屬性。
2. 備忘發起(Originator)角色:“備忘發起角色”創建一個備忘錄,用以記錄當前時刻它的內部狀態。在需要時使用備忘錄恢復內部狀態。
3. 備忘錄管理者(Caretaker)角色:負責保存好備忘錄。不能對備忘錄的內容進行操作或檢查。
UML:
- 優點:
1. 將被存儲的狀態放在外面,不要和關鍵對象混在一起,這可以幫助維護內聚。
2. 保持關鍵對象的數據封裝。
3. 提供了容易實現的恢復能力。
- 缺點和應用場景:
1. 備忘錄用於存儲狀態。
2. 儲存和恢復狀態的過程可能相當耗時。
3. 在 Java 系統中,其實可以考慮使用序列化機制儲存系統的狀態。
Demo 實現:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/memento
8、原型模式(ProtoType Pattern)
- 概念:允許通過複製現有的實例來創建新的實例(在 Java 中,這通常意味著使用 clone() 方法,或者反序列化)。這個模式的重點在於,客戶的代碼在不知道要實例化何種特定類的情況下,可以製造出新的實例。
- 角色:
1. 抽象原型類(Prototype):聲明克隆自身的介面。在 java 中就是 Cloneable 介面。
2. 具體原型類(ConcretePrototype):實現克隆的具體操作。
UML:
- 優點:
1. 向客戶隱藏製造新實例的複雜性。
2. 提供讓客戶能夠產生未知類型對象的選項。
3. 在某些環境下,複製對象比創建新對象更有效。
- 用途和缺點:
1. 在一個複雜的類層次中,當系統必須從其中的許多類型創建新對象時,可以考慮原型。
2. 使用原型模式的缺點:對象的複製有時相當複雜(淺拷貝和深拷貝)。
Demo 實現:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/prototype
9、訪問者模式(Visitor Pattern)
- 概念:為一個對象的組合增加新的能力,且封裝並不重要時。訪問者模式將作用於某種數據結構中的各元素的操作分離出來封裝成獨立的類,使其在不改變數據結構的前提下可以添加作用於這些元素的新的操作,為數據結構中的每個元素提供多種訪問方式。它將對數據的操作與數據結構進行分離,是行為類模式中最複雜的一種模式。
- 角色:
1. 抽象訪問者(Visitor)角色:定義一個訪問具體元素的介面,為每個具體元素類對應一個訪問操作 visit() ,該操作中的參數類型標識了被訪問的具體元素。
2. 具體訪問者(ConcreteVisitor)角色:實現抽象訪問者角色中聲明的各個訪問操作,確定訪問者訪問一個元素時該做什麼。
3. 抽象元素(Element)角色:聲明一個包含接受操作 accept() 的介面,被接受的訪問者對象作為 accept() 方法的參數。
4. 具體元素(ConcreteElement)角色:實現抽象元素角色提供的 accept() 操作,其方法體通常都是 visitor.visit(this) ,另外具體元素中可能還包含本身業務邏輯的相關操作。
5. 對象結構(Object Structure)角色:是一個包含元素角色的容器,提供讓訪問者對象遍歷容器中的所有元素的方法,通常由 List、Set、Map 等聚合類實現。 UML:
- 優點:
1. 允許你對組合結構加入新的操作,而無需改變結構本身。
2. 想要加入新的行為,相對容易。
3. 訪問者所進行的操作,其代碼是集中在一起的。 - 缺點:
1. 當採用訪問者模式的時候,就會打破組合類的封裝。
2. 因為游走的功能牽涉其中,所以對組合結構的改變就更加困難。
3. 違反了依賴倒置原則。訪問者模式依賴了具體類,而沒有依賴抽象類。
Demo 實現:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/visitor
二、其他
1、複合模式
- 複合模式:在一個解決方案中結合兩個或多個模式,以解決一般或重覆發生的問題。
- MVC 是複合模式,結合了觀察者模式、策略模式和組合模式(視圖)。這些模式攜手合作,把 MVC 模型的三層解耦,這樣可以保持設計乾凈又有彈性。
- 模型使用觀察者模式,以便觀察者(視圖)更新,同時保持兩者之間解耦。
- 控制器是視圖的策略,視圖可以使用不同的控制器實現,得到不同的行為。
- Model 2是 MVC 在 Web 上的應用。
- 在Model 2中,控制器實現成 Servlet,而 JSP/HTML 是實現視圖。
2、設計模式概論
- 模式是在某情境(context)下,針對某問題的某種解決方案。設計模式是解決一個經常重覆發生的設計問題。
- 模式不是法律或準則,模式只是指導方針,你可以改變模式來符合你的需要,真實世界中的許多實例,都不符合經典的設計模式。
- 請務必牢記在心,模式是被“發現的”,而不是被創建的。所以,任何人都可能發現某個設計模式,然後寫出它的描述。
- 一般來說,必須要通過“三次準則”,才算是一個合格的模式。也就是說,只有在真實世界中被應用三次以上,才能算是一個模式。
根據模式的目標,我們將設計模式分成三個不同的類目:創建型、行為型(對象之間的溝通和互聯)和結構型(動態的組合對象):
- 模式只是一種工具,只有在需要時才使用這種工具,而我們時刻要遵守的是模式中的設計原則。
- 儘可能地用最簡單的方式解決問題,不要刻意去使用設計模式!當你在設計的時候,如果確定在你的設計中可以利用某個模式解決某個問題,那麼就使用這個模式!如果有更簡單的解決方案,那麼在決定使用模式之前應該先考慮這個方案。
- 有一種情況,即使有更簡單的解決方案,你仍然想要使用模式,這種情況就是:你預期系統在未來會發生改變,但是務必要確定一件事,這個改變是可能發生的實際改變,而不是假想的改變。
模式可能帶來複雜性,可能導致代碼被過度工程化,如果沒有必要,我們絕不需要這樣的複雜性。
3、設計原則
- 單一職責原則(single responsibility principle,SPR):一個類負責一項職責。
- 開閉原則(Open-Close Principe,OCP):一個軟體實體如類、模塊和函數應該對擴展開放,對修改關閉。
- 里氏替換原則(Liskov Substitution Principe,LSP):一個軟體實體如果使用的是一個父類的話,那麼一定適用於其他子類,二且他察覺不出父類對象和子類對象的區別。
- 依賴倒置原則(Dependence Inversion Principle):高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。即針對介面編程,不要針對實現編程。
- 介面隔離原則(Interface Segregation Principle, ISP):建立單一介面,不要建立龐大臃腫的介面,儘量細化介面,介面中的方法儘量少。
- 迪米特法則(Law of Demeter,LoD):最少知識原則,清掉了類之間的松耦合,降低了系統的耦合度。
- 組合/聚合復用原則(Composite Reuse Principe,CRP):儘量使用組合和聚合少使用繼承的關係來達到復用的原則。
4、反模式
- 反模式告訴你如何採用一個不好的解決方案解決一個問題。雖然反模式看起來總像是一個好的解決方案,但是當它被真正採用後,就會帶來麻煩。
- 反模式的工作內容包括:警告你不要陷入某種致命的誘惑;為何這個解決方案從長遠看會造成不同的影響;建議改用其他的模式以提供更好的解決方案。
三、結語
折騰了快四個月的設計模式,終於到終章了~~頗有收穫,時有一種豁然開朗的感覺——原來繼承和組合還能這麼玩。
好的,終於從一個設計模式的小白成長為入門級別了,終於能夠跟人吹牛逼的時候時不時說個設計模式了,哈哈哈哈...任重而道遠!