《设计模式》3.结构型模式
點擊進入我的博客
3.1 適配器模式
適配器模式把一個類的接口變換成客戶端所期待的另一種接口,使得原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。
3.1.1 類的適配器結(jié)構(gòu)
- 目標(Target)角色:這就是所期待得到的接口,由于是類適配器模式,因此目標不可以是類。
- 源(Adaptee)角色:現(xiàn)有需要適配的接口。
- 適配器(Adapter)角色:適配器類是本模式的核心,必須是具體類。
類的適配器
- 類的適配器模式把被的類的API轉(zhuǎn)換成目標類的API。
- 是通過繼承實現(xiàn)的。
類的適配器效果
- 使用一個具體類把源適配到目標中。這樣如果源以及源的子類都使用此類適配,就行不通了。
- 由于適配器是源的子類,因此可以在適配器中重寫源的一些方法。
- 由于引進了一個適配器類,因此只有一個線路到達目標類,是問題得到簡化。
3.1.2 對象的適配器結(jié)構(gòu)
- 目標(Target)角色:這就是所期待得到的接口,因此目標可以是具體或抽象的類。
- 源(Adaptee)角色:現(xiàn)有需要適配的接口。
- 適配器(Adapter)角色:適配器類是本模式的核心,必須是具體類。
對象的適配器
- 對象的適配器是通過依賴實現(xiàn)的
- 推薦使用該方法
對象的適配器效果
- 一個適配器可以把多種不同的源適配到同一個目標,即同一個適配器可以把源類和它的子類都適配到目標接口
- 與類的適配器模式相比,要想置換源類的方法就不容易。
- 增加新的方法方便的多
3.1.3 細節(jié)
使用場景
優(yōu)點
缺點
注意點
3.1.4 一個充電器案例
// 充電器只能接受USB接口 public class Charger {public static void main(String[] args) throws Exception{USB usb = new SuperAdapter(new TypeC());connect(usb);usb = new SuperAdapter(new Lightning());connect(usb);}public static void connect(USB usb) {usb.power();usb.data();} }// 充電器的接口都是USB的,假設(shè)有兩個方法分別是電源和數(shù)據(jù) interface USB {void power();void data(); }// IOS的Lightning接口 class Lightning {void iosPower() {System.out.println("IOS Power");}void iosData() {System.out.println("IOS Data");} }// TYPE-C接口 class TypeC {void typeCPower() {System.out.println("TypeC Power");}void typeCData() {System.out.println("TypeC Data");} }// 超級適配器,可以適配多種手機機型 class SuperAdapter implements USB {private Object obj;public SuperAdapter(Object obj) {this.obj = obj;}@Overridepublic void power() {if(obj.getClass() == Lightning.class) {((Lightning)obj).iosPower();} else if(obj.getClass() == TypeC.class) {((TypeC)obj).typeCPower();}}@Overridepublic void data() {if(obj.getClass() == Lightning.class) {((Lightning)obj).iosData();} else if(obj.getClass() == TypeC.class) {((TypeC)obj).typeCData();}} }3.2 缺省適配模式
缺省適配模式為一個接口提供缺省實現(xiàn),這樣子類型可以從這個缺省實現(xiàn)進行擴展,而不必從原有接口進行擴展。
3.2.1 缺省適配模式結(jié)構(gòu)
簡單的例子
- 下面程序中,Monk接口定義了兩個方法,于是它的子類必須實現(xiàn)這兩個方法。
- 但出現(xiàn)了一個LuZhiShen,他只能實現(xiàn)一部分方法,另一部分方法無法實現(xiàn)
- 所以需要一個抽象的適配類MonkAdapter實現(xiàn)此Monk接口,此抽象類給接口所有方法都提供一個空的方法,LuZhiShen只需要繼承該適配類即可。
3.2.2 細節(jié)
使用場景
- 任何時候不準備實現(xiàn)一個接口中所有方法的時候
作用
缺省適配器模式可以使所需要的類不必實現(xiàn)不需要的接口。
核心點
- 缺省適配的類必須是抽象類,因為這個類不應(yīng)當被實例化
- 缺省適配的類提供的方法必須是具體的方法,而不是抽象的方法。
3.3 組合模式
組合模式,就是在一個對象中包含其他對象,這些被包含的對象可能是終點對象(不再包含別的對象),也有可能是非終點對象(其內(nèi)部還包含其他對象)。
我們將對象稱為節(jié)點,即一個根節(jié)點包含許多子節(jié)點,這些子節(jié)點有的不再包含子節(jié)點,而有的仍然包含子節(jié)點,以此類推。很明顯,這是樹形結(jié)構(gòu),終結(jié)點叫葉子節(jié)點,非終節(jié)點叫樹枝節(jié)點,第一個節(jié)點叫根節(jié)點。
3.3.1 安全式的合成模式結(jié)構(gòu)
安全式的合成模式要求管理集合的方法只出現(xiàn)在樹枝結(jié)點(Composite)中,而不出現(xiàn)在樹葉結(jié)點中。
- 抽象構(gòu)建(Component)角色:這是一個抽象角色,他給參加組合的對象定義出公共的接口及其默認行為,可以用來管理所有的子對象。
- 樹葉(Leaf)角色:樹葉是沒有子對象的對象,定義出參加組合的原始對象的行為。
- 樹枝(Composite)角色:代表參加組合的有下級子對象的對象。樹枝構(gòu)件類給出所有管理子對象的方法。
3.3.2 透明的合成模式結(jié)構(gòu)
透明的合成模式要求所有的具體構(gòu)建類,都符合一個固定的接口。
3.4 裝飾器模式
裝飾器模式(Decorator)允許向一個現(xiàn)有的對象添加新的功能,同時又不改變其結(jié)構(gòu)。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類的一個包裝。
3.4.1 裝飾器結(jié)構(gòu)
- 抽象構(gòu)件(Component)角色:給出一個抽象結(jié)構(gòu),以規(guī)范準備接受附加責(zé)任的對象。
- 具體構(gòu)件(Concrete Component)角色:定義一個要接受附加責(zé)任的類
- 裝飾(Decorator)角色:持有一個構(gòu)件對象的實例,并定義一個與抽象構(gòu)件接口一致的接口。
- 具體裝飾(Concrete Decorator)角色:負責(zé)給構(gòu)件對象“貼上”附加的責(zé)任。
3.4.2 裝飾器細節(jié)
使用場景
優(yōu)點
缺點
注意點
InputStream及其子類
- 抽象構(gòu)件(Component)角色:InputStream
- 具體構(gòu)件(Concrete Component)角色:ByteArrayInputStream、PipedInputStream、StringBufferInputStream等原始流處理器。
- 裝飾(Decorator)角色:FilterInputStream
- 具體裝飾(Concrete Decorator)角色:DateInputStream、BufferedInputStream、LineNumberInputStream
OutputStream及其子類
也用到類裝飾器模式
3.4.3 例子
// Component:一個藝人 interface Artist {void show(); }// Concrete Component:一個歌手 class Singer implements Artist {@Overridepublic void show() {System.out.println("Let It Go");} }// 裝飾后的歌手:不僅會唱歌,還會講笑話和跳舞 class SuperSinger implements Artist {private Artist role;public SuperSinger(Artist role) {this.role = role;}@Overridepublic void show() {System.out.println("Tell Jokes!");role.show();System.out.println("Dance!");} }3.5 代理模式
代理模式給某一個對象提供一個代理對象,并由代理對象控制對原對象對引用。
3.5.1 代理模式結(jié)構(gòu)
- 抽象主題(Subject)角色:聲明了真實主題和代理主題的共同接口
- 代理主題(Proxy)角色:代理主題角色內(nèi)部含有對真實主題的引用,從而可以在任何時候操作真實主題對象;代理主題角色提供一個與真實主題角色相同的接口,以便可以在任何時候都可以替代真實主題控制對真實主題的引用,負責(zé)在需要的時候創(chuàng)建真實主題對象(和刪除真實主題對象);代理角色通常在將客戶端調(diào)用傳遞給真實的主題之前或之后,都要執(zhí)行某個操作,而不是單純地將調(diào)用傳遞給真實主題對象。
- 真實主題(RealSubject)角色:定義了代理角色所代表的真實對象。
3.5.2 動態(tài)代理
自從JDK 1.3以后,Java在java.lang.reflect庫中提供了一下三個類直接支持代理模式:Proxy、InvocationHander、Method。
動態(tài)代理步驟
- 可以使用范型來創(chuàng)建ProxySubject
- 可以使用匿名內(nèi)部類減少代碼數(shù)量請查看14.7節(jié)
3.5.3 細節(jié)
優(yōu)點
缺點
和適配器模式的關(guān)系
適配器模式的用意是改變所考慮對象的接口,而代理模式不能改變。
和裝飾模式
- 裝飾模式應(yīng)當為所裝飾的對象提供增強功能
- 代理模式對對象的使用施加控制,并不提供對象本身的增強功能
虛擬代理
- 虛擬代理模式(Virtual PRoxy)會推遲真正所需對象實例化時間。在需要真正的對象工作之前,如果代理對象能夠處理,那么暫時不需要真正對象來出手。
- 當一個真實主題對象的加載需要耗費資源時,一個虛擬代理對象可以代替真實對象接受請求,并展示“正在加載”的信息,并在適當?shù)臅r候加載真實主題對象。
3.6 享元模式
享元模式以共享的方式高效地支持大量的細粒度對象。
3.6.1 單純享元模式
單純享元模式中,所有的享元對象都是可以共享的。
- 抽象享元(Flyweight)角色:是所有具體享元角色的超類,并為這些類規(guī)定公共接口。
- 具體享元(Concrete Flyweight)角色:實現(xiàn)抽象享元的接口。如果由內(nèi)蘊狀態(tài)的話,必須負責(zé)為內(nèi)蘊狀態(tài)提供空間。
- 享元工廠(Flyweight Factory)角色:負責(zé)創(chuàng)建和管理享元角色。如果系統(tǒng)中有了則返回該角色,沒有則創(chuàng)建。
- 客戶端(Client)角色:維護一個所有享元對象的引用。存儲所有享元對象的外蘊狀態(tài)。
3.6.2 復(fù)合享元模式
- 抽象享元(Flyweight)角色 :給出一個抽象接口,以規(guī)定出所有具體享元角色需要實現(xiàn)的方法。
- 具體享元(ConcreteFlyweight)角色:實現(xiàn)抽象享元角色所規(guī)定出的接口。如果有內(nèi)蘊狀態(tài)的話,必須負責(zé)為內(nèi)蘊狀態(tài)提供存儲空間。
- 復(fù)合享元(ConcreteCompositeFlyweight)角色 :復(fù)合享元角色所代表的對象是不可以共享的,但是一個復(fù)合享元對象可以分解成為多個本身是單純享元對象的組合。復(fù)合享元角色又稱作不可共享的享元對象(UnsharedConcreteFlyweight)。
- 享元工廠(FlyweightFactory)角色 :負責(zé)創(chuàng)建和管理享元角色。當一個客戶端對象調(diào)用一個享元對象的時候,如果已經(jīng)有了,享元工廠角色就應(yīng)當提供這個已有的享元對象;如果系統(tǒng)中沒有一個適當?shù)南碓獙ο蟮脑?#xff0c;享元工廠角色就應(yīng)當創(chuàng)建一個合適的享元對象。
3.6.3 細節(jié)
內(nèi)蘊狀態(tài)和外蘊狀態(tài)
內(nèi)蘊狀態(tài):是存儲在享元對象內(nèi)部的,不會隨環(huán)境改變而改變的。一個享元可以具有內(nèi)蘊狀態(tài)并可以共享。
外蘊狀態(tài):隨環(huán)境改變而改變、不可以共享的狀態(tài)。享元對象的外蘊狀態(tài)必須由客戶端保存,并在享元對象被創(chuàng)建之后,在需要使用的時候再傳入到享元對象內(nèi)部。
不變模式
享元模式中的對象不一定非要是不變對象,但大多數(shù)享元對象的確是這么設(shè)計的。
享元工廠
優(yōu)點
減少對象的創(chuàng)建,降低內(nèi)存消耗
缺點
使用場景
Java應(yīng)用
3.6.4 案例
依舊是熟悉的KFC點餐為例:
- 外蘊狀態(tài):點餐的顧客
- 內(nèi)蘊狀態(tài):顧客點的食物
- 具體享元角色:維護內(nèi)蘊狀態(tài)(客人要點的食物)。
3.7 門面模式
門面模式(Facade Pattern)要求一個子系統(tǒng)的外部與其內(nèi)部通信,必須通過一個統(tǒng)一的門面對象進行。
3.7.1 門面模式結(jié)構(gòu)
門面模式?jīng)]有一個一般化的類圖描述,可以用下面的例子來說明。
- 門面(Facade)角色:外部可以調(diào)用這個角色的方法。此角色知道子系統(tǒng)的功能和責(zé)任。
- 子系統(tǒng)(Subsystem)角色:可以有多個子系統(tǒng),子系統(tǒng)不需要知道門面的存在。
3.7.2 細節(jié)
門面數(shù)量
通常只需要一個門面類,而且只有一個實例,因此可以設(shè)計稱單例模式。當然也可有多個類。
使用場景
優(yōu)點
缺點
Java例子
MVC三層結(jié)構(gòu)
3.7.3 KFC例子
假如沒有服務(wù)員(門面),顧客(外部系統(tǒng))要點一個套餐需要知道每個套餐包含的食物(子系統(tǒng))種類,這樣就會非常麻煩,所以最好的方式是直接告訴服務(wù)員套餐名稱就好了。
public class Customer {public static void main(String[] args) {Waiter waiter = new Waiter();List<Food> foodList = waiter.orderCombo("Combo1");} }abstract class Food {} class MiniBurger extends Food {} class MexicanTwister extends Food {} class CornSalad extends Food {} class HotWing extends Food {} class PepsiCola extends Food {}class Waiter {public List<Food> orderCombo(String comboName) {List<Food> foodList;switch (comboName) {case "Combo1" : foodList = Arrays.asList(new MiniBurger(), new CornSalad(), new PepsiCola()); break;case "Combo2":foodList = Arrays.asList(new MexicanTwister(), new HotWing(), new PepsiCola());break;default:foodList = new ArrayList<>();}return foodList;} }3.8 過濾器模式
過濾器模式使用不同的條件過濾一組對象,并通過邏輯操作以解耦方式將其鏈接。這種類型的設(shè)計模式屬于結(jié)構(gòu)模式,因為該模式組合多個標準以獲得單個標準。
3.8.1 細節(jié)
步驟
Java8
Java8中的lambda表達式可以更簡單的實現(xiàn)過濾器
List<Movie> movies = Stream.of(new Movie("大話西游","comedy"),new Movie("泰囧", "comedy"),new Movie("禁閉島", "suspense")).filter(var -> "comedy".equals(var.getType())).collect(Collectors.toList());3.8.2 電影的例子
3.9 橋接模式
橋接模式是將抽象化與實現(xiàn)化解耦,使兩者可以獨立地變化。橋接模式有助于理解面向?qū)ο蟮脑O(shè)計原則,包括開閉原則以及組合聚合復(fù)用原則。
3.9.1 橋接模式結(jié)構(gòu)
這個系統(tǒng)含有兩個等級結(jié)構(gòu)
- 由抽象化角色和修正抽象化角色組成的抽象化等級結(jié)構(gòu)。
- 由實現(xiàn)化角色和兩個具體實現(xiàn)化角色所組成的實現(xiàn)化等級結(jié)構(gòu)。
橋接模式所涉及的角色
- 抽象化(Abstraction)角色:抽象化給出的定義,并保存一個對實現(xiàn)化對象的引用。
- 修正抽象化(Refined Abstraction)角色:擴展抽象化角色,改變和修正父類對抽象化的定義。
- 實現(xiàn)化(Implementor)角色:這個角色給出實現(xiàn)化角色的接口,但不給出具體的實現(xiàn)。必須指出的是,這個接口不一定和抽象化角色的接口定義相同,實際上,這兩個接口可以非常不一樣。實現(xiàn)化角色應(yīng)該只給出底層操作,而抽象化角色應(yīng)該只給出基于底層操作的更高一層的操作。
- 具體實現(xiàn)化(Concrete Implementor)角色:這個角色給出實現(xiàn)化角色接口的具體實現(xiàn)。
3.9.2 細節(jié)
抽象化、實現(xiàn)化、解耦
抽象化:存在于多個實體中的共同的概念性聯(lián)系;通過忽略一些信息,把不同的實體當作相同的實體來對待。
實現(xiàn)化:抽象化給出的具體實現(xiàn)就是實現(xiàn)化。一個類的實例就是這個類的實現(xiàn)化,一個子類就是它超類的實現(xiàn)化。
解耦:耦合就是兩個實體的某種強關(guān)聯(lián),把它們的強關(guān)聯(lián)去掉就是解耦。
強關(guān)聯(lián)與弱關(guān)聯(lián):所謂強關(guān)聯(lián),就是在編譯期已經(jīng)確定的,無法在運行期動態(tài)改變的關(guān)聯(lián);所謂弱關(guān)聯(lián),就是可以動態(tài)地確定并且可以在運行期動態(tài)地改變的關(guān)聯(lián)。繼承是強關(guān)聯(lián),而聚合關(guān)系是弱關(guān)聯(lián)。
核心理解
橋接模式中的脫耦,就是在抽象化和實現(xiàn)化之間使用組合關(guān)系而不是繼承關(guān)系,從而使兩者可以相對獨立的變化。
優(yōu)點
缺點
使用場景
Java例子
大多數(shù)的驅(qū)動器(Driver)都是橋接模式的應(yīng)用,使用驅(qū)動程序的應(yīng)用系統(tǒng)就是抽象化角色,而驅(qū)動器本身扮演實現(xiàn)化角色。
3.9.3 發(fā)送消息的案例
- 下面案例中,SendMsg及其子類是按照發(fā)送消息的方式進行擴展的;而Send是按照發(fā)送消息的時間進行擴展的,兩者互不影響。
- Send持有類一個SendMsg對象,并可以使用此對象的方法。
總結(jié)
以上是生活随笔為你收集整理的《设计模式》3.结构型模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: yii---where or该如何使用
- 下一篇: Microsoft POS for .N