設計模式 --結構型
范圍\目的 |
創建型模式 |
結構型模式 |
行為型模式 |
類模式 |
工廠方法模式 |
(類)適配器模式 |
解釋器模式 模板方法模式 |
對象模式 |
抽象工廠模式 建造者模式 原型模式 單例模式 |
(對象)適配器模式 橋接模式 組合模式 裝飾模式 外觀模式 享元模式 代理模式 |
職責鏈模式 命令模式 迭代器模式 中介者模式 備忘錄模式 觀察者模式 狀態模式 策略模式 訪問者模式 |
根據目的、用途的不同,分為創建性模式、結構性模式、行為性模式。創建型模式主要用於創建對象,結構型模式主要用於處理類和對象的組合,行為性模式主要用於描述類或對象的交互以及職責分配。
根據處理范圍不同,設計模式又可分為類模式和對象模式,類模式處理類與子類的關系,通過處理這些關系來建立繼承,屬於靜態關系,在編譯時候確定下來;對象模式處理對象之間的關系,運行時發生變化,屬於動態關系。
2.結構型設計模式Structural(7種)
結構型模式所描述的是如何將類和對象結合在一起來形成一個更大的結構,它描述兩種不同的事物:類和對象,根據這一點,可分為類結構型和對象結構型模式。類結構型模式關心類的組合,由多個類可以組合成一個更大的系統,在類結構型模式中一般只存在繼承關系和實現關系;對象結構型模式關心類與對象的組合,通過關聯關系使得在一個類中定義另一個類的實例對象,然后通過該對象調用其方法。根據“合成復用原則”,在系統中盡量使用關聯關系來替代繼承關系,因此大部分結構型模式都是對象結構型模式
一. 適配器模式(對象型和類型)
模式動機
a) 在軟件開發中采用類似於電源適配器的設計和編碼技巧被稱為適配器模式。
b) 通常情況下,客戶端可以通過目標類的接口訪問它所提供的服務。有時,現有的類可以滿足客戶類的功能需要,但是它所提供的接口不一定是客戶類所期望的,這可能是因為現有類中方法名與目標類中定義的方法名不一致等原因所導致的。
c) 在這種情況下,現有的接口需要轉化為客戶類期望的接口,這樣保證了對現有類的重用。如果不進行這樣的轉化,客戶類就不能利用現有類所提供的功能,適配器模式可以完成這樣的轉化。
d) 在適配器模式中可以定義一個包裝類,包裝不兼容接口的對象,這個包裝類指的就是適配器(Adapter),它所包裝的對象就是適配者(Adaptee),即被適配的類。
e) 適配器提供客戶類需要的接口,適配器的實現就是把客戶類的請求轉化為對適配者的相應接口的調用。也就是說:當客戶類調用適配器的方法時,在適配器類的內部將調用適配者類的方法,而這個過程對客戶類是透明的,客戶類並不直接訪問適配者類。因此,適配器可以使由於接口不兼容而不能交互的類可以一起工作。這就是適配器模式的模式動機。
定義:將一個接口轉換成客戶希望的另一個接口,從而使接口不兼容的那些類可以在一起工作。
說明:適配器模式既可作為類結構模式,也可作為對象結構模式,類適配模式是通過一個具體的類,將適配者適配到目標接口當中;對象適配模式是指一個適配器可以將多個不同的適配者適配到同一目標
類適配器模式:
public class Adapter extends Adaptee implements Target { public void request() { specificRequest(); } }
public class Adapter extends Target { private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee=adaptee; } public void request() { adaptee.specificRequest(); } }
適配器模式包含如下角色:
Target:目標抽象類
Adapter:適配器類
Adaptee:適配者類
Client:客戶類
適配器模式的優點
將目標類和適配者類解耦,通過引入一個適配器類來重用現有的適配者類,而無須修改原有代碼。
增加了類的透明性和復用性,將具體的實現封裝在適配者類中,對於客戶端類來說是透明的,而且提高了適配者的復用性。
靈活性和擴展性都非常好,通過使用配置文件,可以很方便地更換適配器,也可以在不修改原有代碼的基礎上增加新的適配器類,完全符合“開閉原則”。
類適配器模式還具有如下優點:
由於適配器類是適配者類的子類,因此可以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強。
類適配器模式的缺點如下:
對於Java、C#等不支持多重繼承的語言,一次最多只能適配一個適配者類,而且目標抽象類只能為抽象類,不能為具體類,其使用有一定的局限性,不能將一個適配者類和它的子類都適配到目標接口。
在以下情況下可以使用適配器模式:
系統需要使用現有的類,而這些類的接口不符合系統的需要。
想要建立一個可以重復使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。
二. 橋接模式(對象型模式)
將抽象部分和實現部分分離,使它們都可以獨立地發生變化,橋接模式是一種對象的結構模式,橋接模式類似於多重繼承方案,但是多重繼承方案往往違背了類的單一職責原則,並且復用性較差,橋接模式相對於多重繼承來說,是更好的解決方案,橋接模式既能達到多重繼承的用途,又能有利於復用。
模式動機
設想如果要繪制矩形、圓形、橢圓、正方形,我們至少需要4個形狀類,但是如果繪制的圖形需要具有不同的顏色,如紅色、綠色、藍色等,此時至少有如下兩種設計方案:
第一種設計方案是為每一種形狀都提供一套各種顏色的版本。
第二種設計方案是根據實際需要對形狀和顏色進行組合。
對於有兩個變化維度(即兩個變化的原因)的系統,采用方案二來進行設計系統中類的個數更少,且系統擴展更為方便。設計方案二即是橋接模式的應用。橋接模式將繼承關系轉換為關聯關系,從而降低了類與類之間的耦合,減少了代碼編寫量
定義:將抽象部分和實現部分分離,使它們都可以獨立地發生變化
橋接模式包含如下角色:
Abstraction:抽象類
RefinedAbstraction:擴充抽象類
Implementor:實現類接口
ConcreteImplementor:具體實現類
理解橋接模式,重點需要理解如何將抽象化(Abstraction)與實現化(Implementation)脫耦,使得二者可以獨立地變化。
抽象化:抽象化就是忽略一些信息,把不同的實體當作同樣的實體對待。在面向對象中,將對象的共同性質抽取出來形成類的過程即為抽象化的過程。
實現化:針對抽象化給出的具體實現,就是實現化,抽象化與實現化是一對互逆的概念,實現化產生的對象比抽象化更具體,是對抽象化事物的進一步具體化的產物。
脫耦:脫耦就是將抽象化和實現化之間的耦合解脫開,或者說是將它們之間的強關聯改換成弱關聯,將兩個角色之間的繼承關系改為關聯關系。橋接模式中的所謂脫耦,就是指在一個軟件系統的抽象化和實現化之間使用關聯關系(組合或者聚合關系)而不是繼承關系,從而使兩者可以相對獨立地變化,這就是橋接模式的用意。
在以下情況下可以使用橋接模式:
如果一個系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態的繼承聯系,通過橋接模式可以使它們在抽象層建立一個關聯關系。
抽象化角色和實現化角色可以以繼承的方式獨立擴展而互不影響,在程序運行時可以動態將一個抽象化子類的對象和一個實現化子類的對象進行組合,即系統需要對抽象化角色和實現化角色進行動態耦合。
一個類存在兩個獨立變化的維度,且這兩個維度都需要進行擴展。
雖然在系統中使用繼承是沒有問題的,但是由於抽象化角色和具體化角色需要獨立變化,設計要求需要獨立管理這兩者。
對於那些不希望使用繼承或因為多層次繼承導致系統類的個數急劇增加的系統,橋接模式尤為適用。
三.組合模式(對象型模式),也稱為整體-部分模式
是一種整體和部分的結構,通過組合多個對象形成樹形結構,用這種方式表示整體和部分的結構層次。在組合模式中,對單個對象和組合對象的使用具有一致性。
定義:將對象組合成樹形結構以表示整體和部分的層次結構,使得用戶對單個對象和組合對象的使用具有一致性
組合模式包含如下角色:
- Component: 抽象構件
- Leaf: 葉子構件
- Composite: 容器構件
- Client: 客戶類
在以下情況下可以使用組合模式:
- 需要表示一個對象整體或部分層次,在具有整體和部分的層次結構中,希望通過一種方式忽略整體與部分的差異,可以一致地對待它們。
- 讓客戶能夠忽略不同對象層次的變化,客戶端可以針對抽象構件編程,無須關心對象層次結構的細節。
- 對象的結構是動態的並且復雜程度不一樣,但客戶需要一致地處理它們。
四.裝飾模式(對象模式)
一般有兩種方式可以實現給一個類或對象增加行為:
a) 繼承機制,使用繼承機制是給現有類添加功能的一種有效途徑,通過繼承一個現有類可以使得子類在擁有自身方法的同時還擁有父類的方法。但是這種方法是靜態的,用戶不能控制增加行為的方式和時機。
b) 關聯機制,即將一個類的對象嵌入另一個對象中,由另一個對象來決定是否調用嵌入對象的行為以便擴展自己的行為,我們稱這個嵌入的對象為裝飾器(Decorator)。
它可以動態地對一個對象增加一些額外的職責,就增加對象功能來說,比生成子類更加靈活一些,為類增加職責就是定義類的功能,為達到這個目的,可以增加子類的方法來實現,但是裝飾模式也可以做到,而且更加靈活。
定義:動態地對一個對象增加額外的職責。它提供了用子類擴展功能的一個靈活的替代,比派生一個子類更加靈活。
當需要為對象動態的添加一些功能,並且可以動態的撤銷或是不能使用子類展功能的時候,這兩種情況時可以使用裝飾模式
裝飾模式包含如下角色:
- Component: 抽象構件
- ConcreteComponent: 具體構件
- Decorator: 抽象裝飾類
- ConcreteDecorator: 具體裝飾類
在以下情況下可以使用裝飾模式:
在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責。
需要動態地給一個對象增加功能,這些功能也可以動態地被撤銷。
當不能采用繼承的方式對系統進行擴充或者采用繼承不利於系統擴展和維護時。不能采用繼承的情況主要有兩類:第一類是系統中存在大量獨立的擴展,為支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長;第二類是因為類定義不能繼承(如final類)。
典型的抽象裝飾類代碼:
public class Decorator extends Component { private Component component; public Decorator(Component component) { this.component=component; } public void operation() { component.operation(); } }
典型的具體裝飾類代碼:
public class ConcreteDecorator extends Decorator { public ConcreteDecorator(Component component) { super(component); } public void operation() { super.operation(); addedBehavior(); } public void addedBehavior() { //新增方法 } }
五. 外觀模式(對象結構模式)
定義:定義一個高層接口,為子系統中的一組接口提供一個一致的外觀,從而簡化了該子系統的使用
外觀模式包含如下角色:
Facade: 外觀角色
SubSystem:子系統角色
典型的外觀角色代碼:
public class Facade { private SubSystemA obj1 = new SubSystemA(); private SubSystemB obj2 = new SubSystemB(); private SubSystemC obj3 = new SubSystemC(); public void method() { obj1.method(); obj2.method(); obj3.method(); } }
在以下情況下可以使用外觀模式:
當要為一個復雜子系統提供一個簡單接口時可以使用外觀模式。該接口可以滿足大多數用戶的需求,而且用戶也可以越過外觀類直接訪問子系統。
客戶程序與多個子系統之間存在很大的依賴性。引入外觀類將子系統與客戶以及其他子系統解耦,可以提高子系統的獨立性和可移植性。
在層次化結構中,可以使用外觀模式定義系統中每一層的入口,層與層之間不直接產生聯系,而通過外觀類建立聯系,降低層之間的耦合度。
不要試圖通過外觀類為子系統增加新行為
不要通過繼承一個外觀類在子系統中加入新的行為,這種做法是錯誤的。外觀模式的用意是為子系統提供一個集中化和簡化的溝通渠道,而不是向子系統加入新的行為,新的行為的增加應該通過修改原有子系統類或增加新的子系統類來實現,不能通過外觀類來實現。
外觀模式與迪米特法則
外觀模式創造出一個外觀對象,將客戶端所涉及的屬於一個子系統的協作伙伴的數量減到最少,使得客戶端與子系統內部的對象的相互作用被外觀對象所取代。外觀類充當了客戶類與子系統類之間的“第三者”,降低了客戶類與子系統類之間的耦合度,外觀模式就是實現代碼重構以便達到“迪米特法則”要求的一個強有力的武器。
六.享元模式(對象型模式)
定義:提供支持大量細粒度對象共享的有效方法
通過運用共享技術,有效地自制細粒度的對象,系統只使用少量的對象,而這些對象都很類似,狀態變化很小,對象使用次數增多。享元對象可以做到共享的關鍵,是能區分出內部狀態和外部狀態。內部狀態是存儲在享元對象的內部,並且不會隨時間和環境而發生變化,內部狀態是可以共享的。外部狀態是隨着外部環境的改變而改變的,不可以共享,所以外部狀態必須由客戶端保存,並且在享元對象被創建之后,在需要使用的時候,傳遞到享元對象的內部,外部狀態之間是相互獨立的。
七. 代理模式(對象型模式)
模式動機
a)在某些情況下,一個客戶不想或者不能直接引用一個對象,此時可以通過一個稱之為“代理”的第三者來實現間接引用。代理對象可以在客戶端和目標對象之間起到中介的作用,並且可以通過代理對象去掉客戶不能看到的內容和服務或者添加客戶需要的額外服務。
定義:為其他對象提供一種代理,由代理對象控制這個對象的訪問
協調調用者和被調用者,減低耦合度
缺點是請求速度變慢,而且增加了額外的工作量
代理模式包含如下角色:
- Subject: 抽象主題角色
- Proxy: 代理主題角色
- RealSubject: 真實主題角色
根據代理模式的使用目的,常見的代理模式有以下幾種類型:
遠程(Remote)代理:為一個位於不同的地址空間的對象提供一個本地的代理對象,這個不同的地址空間可以是在同一台主機中,也可是在另一台主機中,遠程代理又叫做大使(Ambassador)。
虛擬(Virtual)代理:如果需要創建一個資源消耗較大的對象,先創建一個消耗相對較小的對象來表示,真實對象只在需要時才會被真正創建。
Copy-on-Write代理:它是虛擬代理的一種,把復制(克隆)操作延遲到只有在客戶端真正需要時才執行。一般來說,對象的深克隆是一個開銷較大的操作,Copy-on-Write代理可以讓這個操作延遲,只有對象被用到的時候才被克隆。