摘要:類(lèi)的適配器結(jié)構(gòu)目標(biāo)角色這就是所期待得到的接口,由于是類(lèi)適配器模式,因此目標(biāo)不可以是類(lèi)。這種類(lèi)型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類(lèi)的一個(gè)包裝。和適配器模式的關(guān)系適配器模式的用意是改變所考慮對(duì)象的接口,而代理模式不能改變。
點(diǎn)擊進(jìn)入我的博客 3.1 適配器模式
適配器模式把一個(gè)類(lèi)的接口變換成客戶(hù)端所期待的另一種接口,使得原本因接口不匹配而無(wú)法在一起工作的兩個(gè)類(lèi)能夠在一起工作。
3.1.1 類(lèi)的適配器結(jié)構(gòu)目標(biāo)(Target)角色:這就是所期待得到的接口,由于是類(lèi)適配器模式,因此目標(biāo)不可以是類(lèi)。
源(Adaptee)角色:現(xiàn)有需要適配的接口。
適配器(Adapter)角色:適配器類(lèi)是本模式的核心,必須是具體類(lèi)。
interface Target { void operation1(); void operation2(); } class Adaptee { public void operation1() {} } class Adapter extends Adaptee implements Target { @Override public void operation2() {} }
類(lèi)的適配器模式把被的類(lèi)的API轉(zhuǎn)換成目標(biāo)類(lèi)的API。
是通過(guò)繼承實(shí)現(xiàn)的。
使用一個(gè)具體類(lèi)把源適配到目標(biāo)中。這樣如果源以及源的子類(lèi)都使用此類(lèi)適配,就行不通了。
由于適配器是源的子類(lèi),因此可以在適配器中重寫(xiě)源的一些方法。
由于引進(jìn)了一個(gè)適配器類(lèi),因此只有一個(gè)線(xiàn)路到達(dá)目標(biāo)類(lèi),是問(wèn)題得到簡(jiǎn)化。
3.1.2 對(duì)象的適配器結(jié)構(gòu)目標(biāo)(Target)角色:這就是所期待得到的接口,因此目標(biāo)可以是具體或抽象的類(lèi)。
源(Adaptee)角色:現(xiàn)有需要適配的接口。
適配器(Adapter)角色:適配器類(lèi)是本模式的核心,必須是具體類(lèi)。
interface Target { void operation1(); void operation2(); } class Adaptee { public void operation1() {} } class Adapter implements Target { private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public void operation1() { adaptee.operation1(); } @Override public void operation2() { // do something } }
對(duì)象的適配器是通過(guò)依賴(lài)實(shí)現(xiàn)的
推薦使用該方法
一個(gè)適配器可以把多種不同的源適配到同一個(gè)目標(biāo),即同一個(gè)適配器可以把源類(lèi)和它的子類(lèi)都適配到目標(biāo)接口
與類(lèi)的適配器模式相比,要想置換源類(lèi)的方法就不容易。
增加新的方法方便的多
3.1.3 細(xì)節(jié)系統(tǒng)需要使用現(xiàn)有的類(lèi),而此類(lèi)的接口不符合系統(tǒng)的需要
想要建立一個(gè)可以重復(fù)使用的類(lèi),用于與一些彼此間沒(méi)有太大關(guān)聯(lián)的一些類(lèi),包括一些可能在將來(lái)引進(jìn)的類(lèi)一起工作。
對(duì)對(duì)象的適配器兒模式而言,在設(shè)計(jì)里,需要改變多個(gè)已有的子類(lèi)的接口,如果使用類(lèi)的適配器模式,就需要針對(duì)每個(gè)子類(lèi)做一個(gè)適配器類(lèi),這不太實(shí)際。
可以讓任何兩個(gè)沒(méi)有關(guān)聯(lián)的類(lèi)一起運(yùn)行。
提高了類(lèi)的復(fù)用。
增加了類(lèi)的透明度。
靈活性好。
過(guò)多地使用適配器,會(huì)讓系統(tǒng)非常零亂,不易整體進(jìn)行把握。比如,明明看到調(diào)用的是 A 接口,其實(shí)內(nèi)部被適配成了 B 接口的實(shí)現(xiàn),一個(gè)系統(tǒng)如果太多出現(xiàn)這種情況,無(wú)異于一場(chǎng)災(zāi)難。因此如果不是很有必要,可以不使用適配器,而是直接對(duì)系統(tǒng)進(jìn)行重構(gòu)。
由于 JAVA 至多繼承一個(gè)類(lèi),所以至多只能適配一個(gè)適配者類(lèi),而且目標(biāo)類(lèi)必須是抽象類(lèi)。
目標(biāo)接口可以忽略,此時(shí)目標(biāo)接口和源接口實(shí)際上是相同的
適配器類(lèi)可以是抽象類(lèi)
可以有帶參數(shù)的適配器模式
3.1.4 一個(gè)充電器案例// 充電器只能接受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è)有兩個(gè)方法分別是電源和數(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"); } } // 超級(jí)適配器,可以適配多種手機(jī)機(jī)型 class SuperAdapter implements USB { private Object obj; public SuperAdapter(Object obj) { this.obj = obj; } @Override public void power() { if(obj.getClass() == Lightning.class) { ((Lightning)obj).iosPower(); } else if(obj.getClass() == TypeC.class) { ((TypeC)obj).typeCPower(); } } @Override public void data() { if(obj.getClass() == Lightning.class) { ((Lightning)obj).iosData(); } else if(obj.getClass() == TypeC.class) { ((TypeC)obj).typeCData(); } } }3.2 缺省適配模式
缺省適配模式為一個(gè)接口提供缺省實(shí)現(xiàn),這樣子類(lèi)型可以從這個(gè)缺省實(shí)現(xiàn)進(jìn)行擴(kuò)展,而不必從原有接口進(jìn)行擴(kuò)展。
3.2.1 缺省適配模式結(jié)構(gòu)下面程序中,Monk接口定義了兩個(gè)方法,于是它的子類(lèi)必須實(shí)現(xiàn)這兩個(gè)方法。
但出現(xiàn)了一個(gè)LuZhiShen,他只能實(shí)現(xiàn)一部分方法,另一部分方法無(wú)法實(shí)現(xiàn)
所以需要一個(gè)抽象的適配類(lèi)MonkAdapter實(shí)現(xiàn)此Monk接口,此抽象類(lèi)給接口所有方法都提供一個(gè)空的方法,LuZhiShen只需要繼承該適配類(lèi)即可。
// 和尚 interface Monk { void practiceKungfu(); void chantPrayer(); } abstract class MonkAdapter implements Monk { @Override public void practiceKungfu() {} @Override public void chantPrayer() {} } class LuZhiShen extends MonkAdapter { @Override public void practiceKungfu() { System.out.println("拳打鎮(zhèn)關(guān)西"); } }3.2.2 細(xì)節(jié)
任何時(shí)候不準(zhǔn)備實(shí)現(xiàn)一個(gè)接口中所有方法的時(shí)候
缺省適配器模式可以使所需要的類(lèi)不必實(shí)現(xiàn)不需要的接口。
缺省適配的類(lèi)必須是抽象類(lèi),因?yàn)檫@個(gè)類(lèi)不應(yīng)當(dāng)被實(shí)例化
缺省適配的類(lèi)提供的方法必須是具體的方法,而不是抽象的方法。
3.3 組合模式組合模式,就是在一個(gè)對(duì)象中包含其他對(duì)象,這些被包含的對(duì)象可能是終點(diǎn)對(duì)象(不再包含別的對(duì)象),也有可能是非終點(diǎn)對(duì)象(其內(nèi)部還包含其他對(duì)象)。
我們將對(duì)象稱(chēng)為節(jié)點(diǎn),即一個(gè)根節(jié)點(diǎn)包含許多子節(jié)點(diǎn),這些子節(jié)點(diǎn)有的不再包含子節(jié)點(diǎn),而有的仍然包含子節(jié)點(diǎn),以此類(lèi)推。很明顯,這是樹(shù)形結(jié)構(gòu),終結(jié)點(diǎn)叫葉子節(jié)點(diǎn),非終節(jié)點(diǎn)叫樹(shù)枝節(jié)點(diǎn),第一個(gè)節(jié)點(diǎn)叫根節(jié)點(diǎn)。
安全式的合成模式要求管理集合的方法只出現(xiàn)在樹(shù)枝結(jié)點(diǎn)(Composite)中,而不出現(xiàn)在樹(shù)葉結(jié)點(diǎn)中。
抽象構(gòu)建(Component)角色:這是一個(gè)抽象角色,他給參加組合的對(duì)象定義出公共的接口及其默認(rèn)行為,可以用來(lái)管理所有的子對(duì)象。
樹(shù)葉(Leaf)角色:樹(shù)葉是沒(méi)有子對(duì)象的對(duì)象,定義出參加組合的原始對(duì)象的行為。
樹(shù)枝(Composite)角色:代表參加組合的有下級(jí)子對(duì)象的對(duì)象。樹(shù)枝構(gòu)件類(lèi)給出所有管理子對(duì)象的方法。
3.3.2 透明的合成模式結(jié)構(gòu)
透明的合成模式要求所有的具體構(gòu)建類(lèi),都符合一個(gè)固定的接口。
裝飾器模式(Decorator)允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。這種類(lèi)型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類(lèi)的一個(gè)包裝。
3.4.1 裝飾器結(jié)構(gòu)抽象構(gòu)件(Component)角色:給出一個(gè)抽象結(jié)構(gòu),以規(guī)范準(zhǔn)備接受附加責(zé)任的對(duì)象。
具體構(gòu)件(Concrete Component)角色:定義一個(gè)要接受附加責(zé)任的類(lèi)
裝飾(Decorator)角色:持有一個(gè)構(gòu)件對(duì)象的實(shí)例,并定義一個(gè)與抽象構(gòu)件接口一致的接口。
具體裝飾(Concrete Decorator)角色:負(fù)責(zé)給構(gòu)件對(duì)象“貼上”附加的責(zé)任。
3.4.2 裝飾器細(xì)節(jié)需要擴(kuò)展一個(gè)類(lèi)的功能
需要?jiǎng)討B(tài)地給一個(gè)對(duì)象增加功能,這些功能可以再動(dòng)態(tài)的插銷(xiāo)
需要增加由一些基本功能的排列組合而產(chǎn)生非常大量的功能
更加靈活:裝飾模式和繼承關(guān)系的目的都是要擴(kuò)展對(duì)象的功能,但是裝飾模式比繼承更加靈活
多樣性:通過(guò)使用不同具體裝飾類(lèi)及其排列組合,可以創(chuàng)造出不同的行為
動(dòng)態(tài)擴(kuò)展:裝飾器可以動(dòng)態(tài)擴(kuò)展構(gòu)件類(lèi)
會(huì)產(chǎn)生比繼承關(guān)系更多的對(duì)象
比繼承更加容易出錯(cuò)
裝飾類(lèi)的接口必須與被裝飾類(lèi)的接口相容。
盡量保持抽象構(gòu)件(Component)簡(jiǎn)單。
可以沒(méi)有抽象的(Component),此時(shí)裝飾器(Decorator)一般是具體構(gòu)件(Concrete Component)的一個(gè)子類(lèi)。
裝飾(Decorator)和具體裝飾(Concrete Decorator)可以合并。
抽象構(gòu)件(Component)角色:InputStream
具體構(gòu)件(Concrete Component)角色:ByteArrayInputStream、PipedInputStream、StringBufferInputStream等原始流處理器。
裝飾(Decorator)角色:FilterInputStream
具體裝飾(Concrete Decorator)角色:DateInputStream、BufferedInputStream、LineNumberInputStream
也用到類(lèi)裝飾器模式
3.4.3 例子// Component:一個(gè)藝人 interface Artist { void show(); } // Concrete Component:一個(gè)歌手 class Singer implements Artist { @Override public void show() { System.out.println("Let It Go"); } } // 裝飾后的歌手:不僅會(huì)唱歌,還會(huì)講笑話(huà)和跳舞 class SuperSinger implements Artist { private Artist role; public SuperSinger(Artist role) { this.role = role; } @Override public void show() { System.out.println("Tell Jokes!"); role.show(); System.out.println("Dance!"); } }3.5 代理模式
代理模式給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)原對(duì)象對(duì)引用。
3.5.1 代理模式結(jié)構(gòu)抽象主題(Subject)角色:聲明了真實(shí)主題和代理主題的共同接口
代理主題(Proxy)角色:代理主題角色內(nèi)部含有對(duì)真實(shí)主題的引用,從而可以在任何時(shí)候操作真實(shí)主題對(duì)象;代理主題角色提供一個(gè)與真實(shí)主題角色相同的接口,以便可以在任何時(shí)候都可以替代真實(shí)主題控制對(duì)真實(shí)主題的引用,負(fù)責(zé)在需要的時(shí)候創(chuàng)建真實(shí)主題對(duì)象(和刪除真實(shí)主題對(duì)象);代理角色通常在將客戶(hù)端調(diào)用傳遞給真實(shí)的主題之前或之后,都要執(zhí)行某個(gè)操作,而不是單純地將調(diào)用傳遞給真實(shí)主題對(duì)象。
真實(shí)主題(RealSubject)角色:定義了代理角色所代表的真實(shí)對(duì)象。
public class Test { public static void main(String[] args) { Subject subject = new RealSubject(); Subject proxy = new ProxySubject(subject); proxy.request(); // 此處通過(guò)代理類(lèi)來(lái)執(zhí)行 } } interface Subject { void request(); } class RealSubject implements Subject { @Override public void request() { System.out.println("RealSubject"); } } class ProxySubject implements Subject { private Subject subject; public ProxySubject(Subject subject) { this.subject = subject; } @Override public void request() { System.out.println("ProxySubject"); } }3.5.2 動(dòng)態(tài)代理
自從JDK 1.3以后,Java在java.lang.reflect庫(kù)中提供了一下三個(gè)類(lèi)直接支持代理模式:Proxy、InvocationHander、Method。
創(chuàng)建一個(gè)真實(shí)對(duì)象
創(chuàng)建一個(gè)與真實(shí)對(duì)象有關(guān)的調(diào)用處理器對(duì)象InvocationHandler
創(chuàng)建代理,把調(diào)用處理器和要代理的類(lèi)聯(lián)系起來(lái)Proxy.newInstance()
在調(diào)用處理對(duì)象的invoke()方法中執(zhí)行相應(yīng)操作
public class Test { public static void main(String[] args) { // 創(chuàng)建要被代理的實(shí)例對(duì)象 Subject subject = new RealSubject(); // 創(chuàng)建一個(gè)與被代理實(shí)例對(duì)象有關(guān)的InvocationHandler InvocationHandler handler = new ProxySubject(subject); // 創(chuàng)建一個(gè)代理對(duì)象來(lái)代理subject,被代理的對(duì)象subject的每個(gè)方法執(zhí)行都會(huì)調(diào)用代理對(duì)象proxySubject的invoke方法 Subject proxySubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, handler); // 代理對(duì)象執(zhí)行 proxySubject.request(); } } interface Subject { void request(); } class RealSubject implements Subject { @Override public void request() { System.out.println("RealSubject"); } } class ProxySubject implements InvocationHandler { private Subject subject; public ProxySubject(Subject subject) { this.subject = subject; } /** * @param proxy 要代理的 * @param method * @param args * @return */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before Proxy"); Object obj = method.invoke(subject, args); System.out.println("After Proxy"); return obj; } }
可以使用范型來(lái)創(chuàng)建ProxySubject
可以使用匿名內(nèi)部類(lèi)減少代碼數(shù)量請(qǐng)查看14.7節(jié)
3.5.3 細(xì)節(jié)代理類(lèi)和真實(shí)類(lèi)分離,職責(zé)清晰。
在不改變真是累代碼的基礎(chǔ)上擴(kuò)展了功能。
由于在客戶(hù)端和真實(shí)主題之間增加了代理對(duì)象,因此有些類(lèi)型的代理模式可能會(huì)造成請(qǐng)求的處理速度變慢。
實(shí)現(xiàn)代理模式需要額外的工作,有些代理模式的實(shí)現(xiàn)非常復(fù)雜。
適配器模式的用意是改變所考慮對(duì)象的接口,而代理模式不能改變。
裝飾模式應(yīng)當(dāng)為所裝飾的對(duì)象提供增強(qiáng)功能
代理模式對(duì)對(duì)象的使用施加控制,并不提供對(duì)象本身的增強(qiáng)功能
虛擬代理模式(Virtual PRoxy)會(huì)推遲真正所需對(duì)象實(shí)例化時(shí)間。在需要真正的對(duì)象工作之前,如果代理對(duì)象能夠處理,那么暫時(shí)不需要真正對(duì)象來(lái)出手。
當(dāng)一個(gè)真實(shí)主題對(duì)象的加載需要耗費(fèi)資源時(shí),一個(gè)虛擬代理對(duì)象可以代替真實(shí)對(duì)象接受請(qǐng)求,并展示“正在加載”的信息,并在適當(dāng)?shù)臅r(shí)候加載真實(shí)主題對(duì)象。
3.6 享元模式享元模式以共享的方式高效地支持大量的細(xì)粒度對(duì)象。
3.6.1 單純享元模式單純享元模式中,所有的享元對(duì)象都是可以共享的。
抽象享元(Flyweight)角色:是所有具體享元角色的超類(lèi),并為這些類(lèi)規(guī)定公共接口。
具體享元(Concrete Flyweight)角色:實(shí)現(xiàn)抽象享元的接口。如果由內(nèi)蘊(yùn)狀態(tài)的話(huà),必須負(fù)責(zé)為內(nèi)蘊(yùn)狀態(tài)提供空間。
享元工廠(chǎng)(Flyweight Factory)角色:負(fù)責(zé)創(chuàng)建和管理享元角色。如果系統(tǒng)中有了則返回該角色,沒(méi)有則創(chuàng)建。
客戶(hù)端(Client)角色:維護(hù)一個(gè)所有享元對(duì)象的引用。存儲(chǔ)所有享元對(duì)象的外蘊(yùn)狀態(tài)。
3.6.2 復(fù)合享元模式抽象享元(Flyweight)角色 :給出一個(gè)抽象接口,以規(guī)定出所有具體享元角色需要實(shí)現(xiàn)的方法。
具體享元(ConcreteFlyweight)角色:實(shí)現(xiàn)抽象享元角色所規(guī)定出的接口。如果有內(nèi)蘊(yùn)狀態(tài)的話(huà),必須負(fù)責(zé)為內(nèi)蘊(yùn)狀態(tài)提供存儲(chǔ)空間。
復(fù)合享元(ConcreteCompositeFlyweight)角色 :復(fù)合享元角色所代表的對(duì)象是不可以共享的,但是一個(gè)復(fù)合享元對(duì)象可以分解成為多個(gè)本身是單純享元對(duì)象的組合。復(fù)合享元角色又稱(chēng)作不可共享的享元對(duì)象(UnsharedConcreteFlyweight)。
享元工廠(chǎng)(FlyweightFactory)角色 :負(fù)責(zé)創(chuàng)建和管理享元角色。當(dāng)一個(gè)客戶(hù)端對(duì)象調(diào)用一個(gè)享元對(duì)象的時(shí)候,如果已經(jīng)有了,享元工廠(chǎng)角色就應(yīng)當(dāng)提供這個(gè)已有的享元對(duì)象;如果系統(tǒng)中沒(méi)有一個(gè)適當(dāng)?shù)南碓獙?duì)象的話(huà),享元工廠(chǎng)角色就應(yīng)當(dāng)創(chuàng)建一個(gè)合適的享元對(duì)象。
3.6.3 細(xì)節(jié)內(nèi)蘊(yùn)狀態(tài):是存儲(chǔ)在享元對(duì)象內(nèi)部的,不會(huì)隨環(huán)境改變而改變的。一個(gè)享元可以具有內(nèi)蘊(yùn)狀態(tài)并可以共享。
外蘊(yùn)狀態(tài):隨環(huán)境改變而改變、不可以共享的狀態(tài)。享元對(duì)象的外蘊(yùn)狀態(tài)必須由客戶(hù)端保存,并在享元對(duì)象被創(chuàng)建之后,在需要使用的時(shí)候再傳入到享元對(duì)象內(nèi)部。
享元模式中的對(duì)象不一定非要是不變對(duì)象,但大多數(shù)享元對(duì)象的確是這么設(shè)計(jì)的。
使用單例模式:一般只需要一個(gè)享元工廠(chǎng),可以設(shè)計(jì)成單例的。
備忘錄模式:享元工廠(chǎng)負(fù)責(zé)維護(hù)一個(gè)表,通過(guò)這個(gè)表把很多相同的實(shí)例與它們的一個(gè)對(duì)象聯(lián)系起來(lái)。
減少對(duì)象的創(chuàng)建,降低內(nèi)存消耗
提高了系統(tǒng)的復(fù)雜度,為了使對(duì)象可以共享,需要將一些狀態(tài)外部化
需要將一些狀態(tài)外部化,而讀取外部狀態(tài)是的運(yùn)行時(shí)間稍微變長(zhǎng)
一個(gè)系統(tǒng)中有大量對(duì)象。
這些對(duì)象消耗大量?jī)?nèi)存。
這些對(duì)象的狀態(tài)大部分可以外部化。
這些對(duì)象可以按照內(nèi)蘊(yùn)狀態(tài)分為很多組,當(dāng)把外蘊(yùn)對(duì)象從對(duì)象中剔除出來(lái)時(shí),每一組對(duì)象都可以用一個(gè)對(duì)象來(lái)代替。
系統(tǒng)不依賴(lài)于這些對(duì)象身份,換言之,這些對(duì)象是不可分辨的。
String對(duì)象,有則返回,沒(méi)有則創(chuàng)建一個(gè)字符串并保存
數(shù)據(jù)庫(kù)的連接池
3.6.4 案例依舊是熟悉的KFC點(diǎn)餐為例:
外蘊(yùn)狀態(tài):點(diǎn)餐的顧客
內(nèi)蘊(yùn)狀態(tài):顧客點(diǎn)的食物
具體享元角色:維護(hù)內(nèi)蘊(yùn)狀態(tài)(客人要點(diǎn)的食物)。
public class KFC { public static void main(String[] args) { OrderFactory orderFactory = OrderFactory.getInstance(); Order order = orderFactory.getOrder(Food.MiniBurger); order.operation("李雷"); order = orderFactory.getOrder(Food.MiniBurger); order.operation("韓梅梅"); } } enum Food { MiniBurger, MexicanTwister, CornSalad, HotWing, PepsiCola } // Flyweight角色 interface Order { // 傳入的是外蘊(yùn)對(duì)象:顧客 void operation(String customer); } // ConcreteFlyweight角色 class FoodOrder implements Order { // 內(nèi)蘊(yùn)狀態(tài) private Food food; // 構(gòu)造方法,傳入享元對(duì)象的內(nèi)部狀態(tài)的數(shù)據(jù) public FoodOrder(Food food) { this.food = food; } @Override public void operation(String customer) { System.out.println("顧客[" + customer + "]點(diǎn)的是" + food.toString()); } } // FlyweightFactory角色 class OrderFactory { private Map3.7 門(mén)面模式orderPool = new HashMap<>(); private static OrderFactory instance = new OrderFactory(); private OrderFactory() {} public static OrderFactory getInstance() { return instance; } // 獲取Food對(duì)應(yīng)的享元對(duì)象 public Order getOrder(Food food) { Order order = orderPool.get(food); if (null == order) { order = new FoodOrder(food); orderPool.put(food, order); } return order; } }
門(mén)面模式(Facade Pattern)要求一個(gè)子系統(tǒng)的外部與其內(nèi)部通信,必須通過(guò)一個(gè)統(tǒng)一的門(mén)面對(duì)象進(jìn)行。
3.7.1 門(mén)面模式結(jié)構(gòu)門(mén)面模式?jīng)]有一個(gè)一般化的類(lèi)圖描述,可以用下面的例子來(lái)說(shuō)明。
門(mén)面(Facade)角色:外部可以調(diào)用這個(gè)角色的方法。此角色知道子系統(tǒng)的功能和責(zé)任。
子系統(tǒng)(Subsystem)角色:可以有多個(gè)子系統(tǒng),子系統(tǒng)不需要知道門(mén)面的存在。
3.7.2 細(xì)節(jié)通常只需要一個(gè)門(mén)面類(lèi),而且只有一個(gè)實(shí)例,因此可以設(shè)計(jì)稱(chēng)單例模式。當(dāng)然也可有多個(gè)類(lèi)。
為一個(gè)復(fù)雜的子系統(tǒng)提供一個(gè)簡(jiǎn)單的接口
使子系統(tǒng)和外部分離開(kāi)來(lái)
構(gòu)建一個(gè)層次化系統(tǒng)時(shí),可以使使用Facade模式定義系統(tǒng)中每一層,實(shí)現(xiàn)分層。
減少系統(tǒng)之間的相互依賴(lài)。
提高了安全性。
不符合開(kāi)閉原則
如果要改東西很麻煩,繼承重寫(xiě)都不合適。
MVC三層結(jié)構(gòu)
3.7.3 KFC例子假如沒(méi)有服務(wù)員(門(mén)面),顧客(外部系統(tǒng))要點(diǎn)一個(gè)套餐需要知道每個(gè)套餐包含的食物(子系統(tǒng))種類(lèi),這樣就會(huì)非常麻煩,所以最好的方式是直接告訴服務(wù)員套餐名稱(chēng)就好了。
public class Customer { public static void main(String[] args) { Waiter waiter = new Waiter(); List3.8 過(guò)濾器模式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 orderCombo(String comboName) { List 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; } }
過(guò)濾器模式使用不同的條件過(guò)濾一組對(duì)象,并通過(guò)邏輯操作以解耦方式將其鏈接。這種類(lèi)型的設(shè)計(jì)模式屬于結(jié)構(gòu)模式,因?yàn)樵撃J浇M合多個(gè)標(biāo)準(zhǔn)以獲得單個(gè)標(biāo)準(zhǔn)。
3.8.1 細(xì)節(jié)創(chuàng)建一個(gè)要過(guò)濾的普通類(lèi),要有獲得其私有屬性的get方法
創(chuàng)建一個(gè)接口,規(guī)定過(guò)濾方法
實(shí)現(xiàn)接口,可以依需要重寫(xiě)過(guò)濾方法,參數(shù)傳遞的一般是存儲(chǔ)過(guò)濾類(lèi)的容器類(lèi)
復(fù)雜過(guò)濾類(lèi)可以通過(guò)設(shè)置傳遞接口參數(shù)(復(fù)用其他基礎(chǔ)過(guò)濾類(lèi))來(lái)實(shí)現(xiàn)多重過(guò)濾
Java8中的lambda表達(dá)式可以更簡(jiǎn)單的實(shí)現(xiàn)過(guò)濾器
List3.8.2 電影的例子movies = Stream.of( new Movie("大話(huà)西游","comedy"), new Movie("泰囧", "comedy"), new Movie("禁閉島", "suspense")) .filter(var -> "comedy".equals(var.getType())) .collect(Collectors.toList());
創(chuàng)建被過(guò)濾的類(lèi)Movie,根據(jù)它的type屬性實(shí)現(xiàn)過(guò)濾
創(chuàng)建接口Criteria,規(guī)定過(guò)濾方法
創(chuàng)建喜劇電影過(guò)濾器ComedyMovieCriteria,根據(jù)comedy==movie.type來(lái)過(guò)濾出需要的喜劇電影
public class Test { public static void main(String[] args) { List3.9 橋接模式movies = new ArrayList(){{ add(new Movie("大話(huà)西游","comedy")); add(new Movie("泰囧", "comedy")); add(new Movie("禁閉島", "suspense")); }}; System.out.println(new ComedyMovieCriteria().meetCriteria(movies)); } } // 被篩選的對(duì)象 class Movie { private String name; // 電影類(lèi)型 private String type; public Movie(String name, String type) { this.name = name; this.type = type; } // getters & setters & toString } // 過(guò)濾器接口 interface Criteria { /** * @param movies 要被篩選的電影 * @return 篩選后的結(jié)果 */ List meetCriteria(List movies); } // 過(guò)濾喜劇電影的過(guò)濾器,要求是movie.type==comedy class ComedyMovieCriteria implements Criteria { @Override public List meetCriteria(List movies) { List result = new ArrayList<>(); for (Movie movie : movies) { if ("comedy".equals(movie.getType())) { result.add(movie); } } return result; } }
橋接模式是將抽象化與實(shí)現(xiàn)化解耦,使兩者可以獨(dú)立地變化。橋接模式有助于理解面向?qū)ο蟮脑O(shè)計(jì)原則,包括開(kāi)閉原則以及組合聚合復(fù)用原則。
3.9.1 橋接模式結(jié)構(gòu)由抽象化角色和修正抽象化角色組成的抽象化等級(jí)結(jié)構(gòu)。
由實(shí)現(xiàn)化角色和兩個(gè)具體實(shí)現(xiàn)化角色所組成的實(shí)現(xiàn)化等級(jí)結(jié)構(gòu)。
抽象化(Abstraction)角色:抽象化給出的定義,并保存一個(gè)對(duì)實(shí)現(xiàn)化對(duì)象的引用。
修正抽象化(Refined Abstraction)角色:擴(kuò)展抽象化角色,改變和修正父類(lèi)對(duì)抽象化的定義。
實(shí)現(xiàn)化(Implementor)角色:這個(gè)角色給出實(shí)現(xiàn)化角色的接口,但不給出具體的實(shí)現(xiàn)。必須指出的是,這個(gè)接口不一定和抽象化角色的接口定義相同,實(shí)際上,這兩個(gè)接口可以非常不一樣。實(shí)現(xiàn)化角色應(yīng)該只給出底層操作,而抽象化角色應(yīng)該只給出基于底層操作的更高一層的操作。
具體實(shí)現(xiàn)化(Concrete Implementor)角色:這個(gè)角色給出實(shí)現(xiàn)化角色接口的具體實(shí)現(xiàn)。
3.9.2 細(xì)節(jié)抽象化:存在于多個(gè)實(shí)體中的共同的概念性聯(lián)系;通過(guò)忽略一些信息,把不同的實(shí)體當(dāng)作相同的實(shí)體來(lái)對(duì)待。
實(shí)現(xiàn)化:抽象化給出的具體實(shí)現(xiàn)就是實(shí)現(xiàn)化。一個(gè)類(lèi)的實(shí)例就是這個(gè)類(lèi)的實(shí)現(xiàn)化,一個(gè)子類(lèi)就是它超類(lèi)的實(shí)現(xiàn)化。
解耦:耦合就是兩個(gè)實(shí)體的某種強(qiáng)關(guān)聯(lián),把它們的強(qiáng)關(guān)聯(lián)去掉就是解耦。
強(qiáng)關(guān)聯(lián)與弱關(guān)聯(lián):所謂強(qiáng)關(guān)聯(lián),就是在編譯期已經(jīng)確定的,無(wú)法在運(yùn)行期動(dòng)態(tài)改變的關(guān)聯(lián);所謂弱關(guān)聯(lián),就是可以動(dòng)態(tài)地確定并且可以在運(yùn)行期動(dòng)態(tài)地改變的關(guān)聯(lián)。繼承是強(qiáng)關(guān)聯(lián),而聚合關(guān)系是弱關(guān)聯(lián)。
橋接模式中的脫耦,就是在抽象化和實(shí)現(xiàn)化之間使用組合關(guān)系而不是繼承關(guān)系,從而使兩者可以相對(duì)獨(dú)立的變化。
實(shí)現(xiàn)抽象化和實(shí)現(xiàn)化的分離。
提高了代碼的擴(kuò)展能力。
實(shí)現(xiàn)細(xì)節(jié)對(duì)客戶(hù)透明。
橋接模式的引入會(huì)增加系統(tǒng)的理解與設(shè)計(jì)難度
由于聚合關(guān)聯(lián)關(guān)系建立在抽象層,要求開(kāi)發(fā)者針對(duì)抽象進(jìn)行設(shè)計(jì)與編程。
如果一個(gè)系統(tǒng)需要在構(gòu)件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個(gè)層次之間建立靜態(tài)的繼承聯(lián)系,通過(guò)橋接模式可以使它們?cè)诔橄髮咏⒁粋€(gè)關(guān)聯(lián)關(guān)系。
抽象化角色和實(shí)現(xiàn)化角色可以以繼承的方式獨(dú)立擴(kuò)展而互不影響,在程序運(yùn)行時(shí)可以動(dòng)態(tài)將一個(gè)抽象化子類(lèi)的對(duì)象和一個(gè)實(shí)現(xiàn)化子類(lèi)的對(duì)象進(jìn)行組合,即系統(tǒng)需要對(duì)抽象化角色和實(shí)現(xiàn)化角色進(jìn)行動(dòng)態(tài)耦合。
一個(gè)類(lèi)存在兩個(gè)獨(dú)立變化的維度,且這兩個(gè)維度都需要進(jìn)行擴(kuò)展。
雖然在系統(tǒng)中使用繼承是沒(méi)有問(wèn)題的,但是由于抽象化角色和具體化角色需要獨(dú)立變化,設(shè)計(jì)要求需要獨(dú)立管理這兩者。
對(duì)于那些不希望使用繼承或因?yàn)槎鄬哟卫^承導(dǎo)致系統(tǒng)類(lèi)的個(gè)數(shù)急劇增加的系統(tǒng),橋接模式尤為適用
大多數(shù)的驅(qū)動(dòng)器(Driver)都是橋接模式的應(yīng)用,使用驅(qū)動(dòng)程序的應(yīng)用系統(tǒng)就是抽象化角色,而驅(qū)動(dòng)器本身扮演實(shí)現(xiàn)化角色。
下面案例中,SendMsg及其子類(lèi)是按照發(fā)送消息的方式進(jìn)行擴(kuò)展的;而Send是按照發(fā)送消息的時(shí)間進(jìn)行擴(kuò)展的,兩者互不影響。
Send持有類(lèi)一個(gè)SendMsg對(duì)象,并可以使用此對(duì)象的方法。
// Implementor角色 interface SendMsg { void sendMsg(); } // Concrete Implementor角色 class EmailSendMsg implements SendMsg { @Override public void sendMsg() { System.out.println("Send Msg By Email"); } } // Concrete Implementor角色 class WeChatSendMsg implements SendMsg { @Override public void sendMsg() { System.out.println("Send Msg By WeChat"); } } // Abstraction 角色 abstract class Send { protected SendMsg sendMsg; public Send(SendMsg sendMsg) { this.sendMsg = sendMsg; } public abstract void send(); } // Concrete Implementor角色 class ImmediatelySend extends Send { public ImmediatelySend(SendMsg sendMsg) { super(sendMsg); } @Override public void send() { sendMsg.sendMsg(); System.out.println("Send Msg Immediately"); } } // Concrete Implementor角色 class DelayedlySend extends Send { public DelayedlySend(SendMsg sendMsg) { super(sendMsg); } @Override public void send() { sendMsg.sendMsg(); System.out.println("Send Msg DelayedlySend"); } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/72495.html
摘要:設(shè)計(jì)模式的類(lèi)別設(shè)計(jì)模式一共分為種類(lèi)型,共種。屬于結(jié)構(gòu)型的設(shè)計(jì)模式適配器模式橋接模式裝飾模式組合模式外觀(guān)模式享元模式代理模式。問(wèn)題描述了應(yīng)該在何時(shí)使用設(shè)計(jì)模式。解決方案描述了設(shè)計(jì)的組成成分,它們之間的相互關(guān)系及各自的職責(zé)和協(xié)作方式。 設(shè)計(jì)模式概述 1. 設(shè)計(jì)模式是什么 我們?cè)谄綍r(shí)編寫(xiě)代碼的過(guò)程中,會(huì)遇到各種各樣的問(wèn)題,細(xì)想一下很多問(wèn)題的解決思路大致一樣的,這時(shí)候你就可以把解決問(wèn)題的思路整...
摘要:能夠協(xié)調(diào)調(diào)用者和被調(diào)用者,能夠在一定程度上降低系統(tǒng)的耦合性。特點(diǎn)低耦合性,獨(dú)立性好,安全性應(yīng)用客戶(hù)訪(fǎng)問(wèn)不到或者被訪(fǎng)問(wèn)者希望隱藏自己,所以通過(guò)代理來(lái)訪(fǎng)問(wèn)自己。 我們接著上面的幾種模式繼續(xù)講: 4、組合模式 將對(duì)象組合成樹(shù)形結(jié)構(gòu)表示部分-整體的層次結(jié)構(gòu)。 特點(diǎn):靈活性強(qiáng) 應(yīng)用:對(duì)象的部分-整體的層次結(jié)構(gòu),模糊組合對(duì)象和簡(jiǎn)單對(duì)象處理問(wèn)題 代碼實(shí)現(xiàn) /** 組合模式* *///繼承模式clas...
摘要:設(shè)計(jì)模式設(shè)計(jì)模式基本原則設(shè)計(jì)原則按接口而不是按實(shí)現(xiàn)來(lái)編程按接口而不是按實(shí)現(xiàn)編程是指,要將變量設(shè)置為一個(gè)抽象類(lèi)或接口數(shù)據(jù)類(lèi)型的實(shí)例,而不是一個(gè)具體實(shí)現(xiàn)的實(shí)例。例如父類(lèi)的一個(gè)改變會(huì)逐級(jí)向下傳遞給子類(lèi)實(shí)現(xiàn),這可能會(huì)影響子類(lèi)使用的某個(gè)算法。 設(shè)計(jì)模式 設(shè)計(jì)模式基本原則 設(shè)計(jì)原則 ① : 按接口而不是按實(shí)現(xiàn)來(lái)編程 按接口而不是按實(shí)現(xiàn)編程是指,要將變量設(shè)置為一個(gè)抽象類(lèi)或接口數(shù)據(jù)類(lèi)型的實(shí)例,而不是一...
摘要:哪吒社區(qū)技能樹(shù)打卡打卡貼函數(shù)式接口簡(jiǎn)介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號(hào)作者架構(gòu)師奮斗者掃描主頁(yè)左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無(wú)意間聽(tīng)到領(lǐng)導(dǎo)們的談話(huà),現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡(jiǎn)而言之,不缺干 ? 哪吒社區(qū)Java技能樹(shù)打卡?【打卡貼 day2...
閱讀 3474·2023-04-25 18:52
閱讀 2486·2021-11-22 15:31
閱讀 1225·2021-10-22 09:54
閱讀 3014·2021-09-29 09:42
閱讀 608·2021-09-26 09:55
閱讀 914·2021-09-13 10:28
閱讀 1106·2019-08-30 15:56
閱讀 2111·2019-08-30 15:55