?
l ? 創(chuàng)建模式
1. ?????? 工廠模式
簡(jiǎn)單工廠:又稱靜態(tài)工廠方法模式,它可以根據(jù)傳進(jìn)來(lái)的參數(shù)來(lái)選擇創(chuàng)建哪些對(duì)象。這樣方便,但有個(gè)缺點(diǎn),因?yàn)楣S模式本來(lái)就是為了將對(duì)象的使用和創(chuàng)建脫藕,而如果使用簡(jiǎn)單工廠模式的話,那么客戶端就需要知道要?jiǎng)?chuàng)建的對(duì)象的類型。
工廠方法:又稱多態(tài)性工廠模式。工廠模式的核心是一個(gè)抽象工廠類,而簡(jiǎn)單工廠模式把核心放在一個(gè)具體類上。工廠方法模式可以允許很多具體工廠從抽象工廠類中將創(chuàng)建行為繼承下來(lái),從而可以成為多個(gè)簡(jiǎn)單工廠模式的統(tǒng)合,進(jìn)而推廣了簡(jiǎn)單工廠類。而且,當(dāng)需要?jiǎng)?chuàng)建新的對(duì)象時(shí),簡(jiǎn)單工廠需要直接改源代碼,而工廠方法模式只需要再創(chuàng)建一個(gè)工廠類繼續(xù)抽象工廠類就可以了。同時(shí),如果要?jiǎng)?chuàng)建的對(duì)象具體很深的層次的話,對(duì)應(yīng)的工廠也可以和對(duì)象建立同樣的等級(jí),這樣使用起來(lái)更方便,而簡(jiǎn)單工廠就亂套的多。
抽象工廠:又稱工具箱模式。抽象工廠不是為了創(chuàng)建單一的對(duì)象而存在的,它是為了創(chuàng)建一個(gè)產(chǎn)品族群。
2. ?????? 單例模式
單例模式分為兩種,懶漢模式和餓漢模式。
餓漢模式:
Private static final EagerSingleton m_instance = new EagerSingleton();
當(dāng)這個(gè)類被加載時(shí),靜態(tài)變量 m_instance 就會(huì)被初始化,此時(shí)類的私有構(gòu)造子會(huì)被調(diào)用,這時(shí)候,單例類的惟一實(shí)例就被創(chuàng)建出來(lái)了。
懶漢模式:
synchronized public static LazySingleton getInstance(){
???????? If(m_instance == null){
?????????????????? m_instance= new LazySingleton();
}else
return m_instance;
}
加 synchronized 是為了實(shí)現(xiàn)同步化。
餓漢模式在資源利用上不如懶漢模式,但因?yàn)闆](méi)有它天然就是線程安全的,不需要 synchronized ,所以它在速度和反應(yīng)時(shí)間上比餓漢好。
餓漢模式在 java 可以實(shí)現(xiàn),但在 c++ 中很難,因?yàn)殪o態(tài)初始化在 c++ 中沒(méi)有固定的順序,因而靜態(tài)的 m_instance 變量的初始化與類的加載順序沒(méi)有保證,可能會(huì)出問(wèn)題。
單例模式分關(guān)有狀態(tài)的和沒(méi)狀態(tài)的兩種,當(dāng)有狀態(tài)時(shí),它可以作為一個(gè)狀態(tài)庫(kù)使用,用來(lái)給系統(tǒng)提供一些信息。當(dāng)用到分布式,比如 ejb 中時(shí),需要跨 jvm 的運(yùn)行,如果要用到單例類,那么會(huì)在每個(gè) jvm 中都創(chuàng)建相應(yīng)的實(shí)例,這時(shí),就應(yīng)該使用沒(méi)有狀態(tài)的單例。
3. ?????? 建造模式( builder )
建造模式和抽象工廠模式有些相似,但它們的關(guān)注的方面不同。抽象工廠是創(chuàng)建一個(gè)產(chǎn)品族,所以它的創(chuàng)建一般只有一個(gè)方法,然后就返回創(chuàng)建好的產(chǎn)品,可以再用來(lái)加工,也可以直接使用。而建造模式,一般都有好幾個(gè)創(chuàng)建方法,用來(lái)逐漸創(chuàng)建 Product ,它的所有創(chuàng)建方法必須都使用才能創(chuàng)建一個(gè)完整的產(chǎn)品,然后再用 retrieveResult() 方法來(lái)返回所創(chuàng)建的產(chǎn)品。
4. ?????? 原始模型( Prototype )模式
原始模型模式是給出一個(gè)原型對(duì)象來(lái)指明所要他要?jiǎng)?chuàng)建的對(duì)象類型,然后用復(fù)制這個(gè)原型對(duì)象的方法創(chuàng)建出更多同類型的對(duì)象。換句話說(shuō),就是克隆。
Java 語(yǔ)言的構(gòu)件模型直接支持原始模型模式。所有的 javabean 都繼承自 java.lang.Object ,而 Object 類提供了一個(gè) clone() 方法,可以將 JavaBean 對(duì)象復(fù)制一份。但是,這個(gè) javabean 必須實(shí)現(xiàn)標(biāo)識(shí)接口 Cloneable ,表明這個(gè) javabean 支持復(fù)制。如果不實(shí)現(xiàn)就調(diào)用 clone() 方法,則拋出 CloneNotSupportedException 異常。( clone 方法的返回類型是 Object ,所以在子類重寫(xiě)該方法的時(shí)候要注意一下)
通常來(lái)說(shuō),克隆要滿足幾個(gè)條件
? ? ? ? ? ? ? ? ? ? ? ? ? i. ????????????? 對(duì)任何的對(duì)象 x ,都有: x.clone()!=x 。換言之,克隆對(duì)象與原對(duì)象不是同一個(gè)對(duì)象。
? ? ? ? ? ? ? ? ? ? ? ? ii. ????????????? 對(duì)任何的對(duì)象 x ,都有: x.clone().getClass == x.getClass() ,換言之克隆對(duì)象與原始對(duì)象的類型一樣。(即使重寫(xiě) clone ()返回類型是 Object ,但它的 getClass 依然為創(chuàng)建時(shí)的 class )
? ? ? ? ? ? ? ? ? ? ?? iii. ????????????? 如果對(duì)象 x 的 equals() 方法定義恰當(dāng)?shù)脑挘敲? x.clone().equals(x) 應(yīng)當(dāng)成立(不要求必須,但最好)。
復(fù)制又分為兩種,淺復(fù)制和深復(fù)制。淺復(fù)制只復(fù)制對(duì)象的所有變量,而引用的對(duì)象還是原來(lái)的對(duì)象,也就是說(shuō),當(dāng)淺復(fù)制時(shí),兩個(gè)不同的對(duì)象,它們引用了同一個(gè)對(duì)象(如果有的話)。
深復(fù)制是指連引用的對(duì)象也一并復(fù)制。但引用的對(duì)象還可能引用別的對(duì)象,所以深復(fù)制要深入多少層,是一個(gè)不易確定的問(wèn)題。而且可能出現(xiàn)循環(huán)引用,要小心使用。
可以利用串行化( Serilization )過(guò)程來(lái)深復(fù)制,把對(duì)象寫(xiě)進(jìn)流里再讀出來(lái),那就是個(gè)完全的深復(fù)制,但效率太低。
l ? 結(jié)構(gòu)模式
結(jié)構(gòu)模式( Structural Pattern )描述如何將類或者對(duì)象結(jié)合在一起形成更大的結(jié)構(gòu)。結(jié)構(gòu)模式描述兩種不同的東西:類與類的實(shí)例。根據(jù)這一不同,結(jié)構(gòu)模式可以分為類的結(jié)構(gòu)模式和對(duì)象的結(jié)構(gòu)模式。
5. ?????? 適配器模式( Adapter )
適配器模式是把一個(gè)類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無(wú)法在一起工作的兩個(gè)類能夠在一起工作。
它分為兩種,類的適配器和對(duì)象的適配器
類的適配器,要適配的類含有目標(biāo)接口的( SpecificRequest ())方法,所以子類 Adapter 不用重寫(xiě),只要把源( Adaptee )沒(méi)有的方法, Request 實(shí)現(xiàn)就可以了。
對(duì)象的適配器
適配器的特例:缺省適配模式( Default Adapter ),為一個(gè)接口提供缺省實(shí)現(xiàn),這樣子類型可以從這個(gè)缺省實(shí)現(xiàn)進(jìn)行擴(kuò)展,而不必從原有接口進(jìn)行擴(kuò)展。作為適配器模式的一個(gè)特例,缺省適配模式在 java 語(yǔ)言中有著特殊的應(yīng)用。
這樣天星類就是一個(gè)抽象的適配器類,魯智深現(xiàn)在不需要實(shí)現(xiàn)吃齋等方法,因?yàn)樗蛔觯侵灰獙?shí)現(xiàn)他做的方法就行了。
6. ????? 合成模式( Composite )
合成模式又叫做部分 - 整體模式,將對(duì)象組織到樹(shù)結(jié)構(gòu)中,可以用來(lái)描述整體與部分的關(guān)系。合成模式可以使客戶端將單純?cè)嘏c復(fù)合元素同等看待。
合成模式分為兩種,透明方式和安全方式。上圖就是透明方式,在 Component 里聲明所有的用來(lái)管理子類對(duì)象的方法,包括 add ()、 remove ()、 getChild ()方法。這樣做的好處是所有的構(gòu)件類都有相同的接口。在客戶端看來(lái),樹(shù)葉類對(duì)象與合成類對(duì)象的區(qū)別起碼在接口層次上消失了,客戶端可以同等地對(duì)待所有的對(duì)象。
這個(gè)選擇缺點(diǎn)是不夠安全,因?yàn)闃?shù)葉類對(duì)象和合成類對(duì)象本質(zhì)上是有區(qū)別的。樹(shù)葉類對(duì)象不可能有下一個(gè)層次的對(duì)象,因此 add 等方法是沒(méi)有意義的,但在編譯時(shí)期不會(huì)出錯(cuò),而只會(huì)在運(yùn)行時(shí)代奢會(huì)出錯(cuò)。
安全方式,是在 Composite 里聲明所有的用來(lái)管理子類對(duì)象的方法,而 Component 里的 add 、 remove 、 getChild 方法移到 Composite 里,這是安全的,因?yàn)闃?shù)葉類型的對(duì)象根本沒(méi)有管理子類對(duì)象的方法,因此,如果客戶端對(duì)樹(shù)葉類對(duì)象使用這些方法時(shí),程序會(huì)在編譯時(shí)期出錯(cuò)。這些的缺點(diǎn)是不夠透明,因?yàn)闃?shù)葉類和合成類將具有不同的接口。
7. ?????? 裝飾模式( Decorator )
裝飾模式以對(duì)客戶端透明的方式擴(kuò)展對(duì)象的功能,是繼承關(guān)系的一個(gè)替代方案。
裝飾模式的上層可以看作是個(gè)合成模式。
裝飾模式與繼承模式都能擴(kuò)展對(duì)象的功能,但裝飾模式更靈活,它可以選擇給對(duì)象裝飾成不同的樣式,比如用 ConcreteDectorator1 或 ConcreteDectorator2 裝飾,這些都可以由系統(tǒng)動(dòng)態(tài)決定,而繼承是靜態(tài)的,編譯時(shí)就決定了。
由于要面對(duì)抽象編程,所以透明的裝飾模式應(yīng)該這樣用:
Component c = new ConcreteComonent();
Comoponent c1 = new ConcreteDectorator1(c);
Comoponent c2 = new ConcreteDectorator2(c1);
而下面的是不對(duì)的
ConcreteComonent c = new ConcreteDectorator();
裝飾模式對(duì)客戶端是完全透明的。它們一系列的方法都有最初的 Component 接口所定義。但是,純粹的裝飾模式很難找到。裝飾模式的用意是在不改變接口的前提下,增強(qiáng)所考慮的類的性能。在增強(qiáng)性能的時(shí)候,往往需要那門(mén)新的公開(kāi)的方法。比如在 io 系統(tǒng)中, BufferedInputStream 是負(fù)責(zé)裝飾 InputStream 的,但它為了增強(qiáng)處理緩存的功能,提供了額外的方法,比如 ensureopen() 、 fill() 等。所以它是一個(gè)半透明的裝飾類,如果想使用這些方法,就要將 inputStream 轉(zhuǎn)化為 BufferedInputStream 。
8. ?????? 代理模式( Proxy )
代理模式給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)原對(duì)象的引用。通常情況下,如果被代理的對(duì)象需要很長(zhǎng)的加載時(shí)間,比如數(shù)據(jù)庫(kù)或遠(yuǎn)程方面的話, Proxy 可以在被代理對(duì)象真正創(chuàng)建的時(shí)候再去實(shí)例化它。
Proxy 類中有段代碼:
Private RelalSubject realSubject;
?
Public void request(){
// 方法調(diào)用前執(zhí)行
preRequest();
if(realSubject == null){
?????????????????? realSubject = new RealSubject();
???????? }
???????? realSubject.request();
???????? // 方法調(diào)用完后執(zhí)行
???????? afterRequest();
}
這樣不僅能延遲加載,也能面向切面編程,實(shí)際上, aop 就是用了這個(gè)原理。
代理模式看起來(lái)和適配器模式很相似,但它們有質(zhì)的區(qū)別。適配器模式是為了改變所考慮的對(duì)象的接口,而代理模式并不能改變所代理的對(duì)象的接口。雖然 RealSubject 看起來(lái)即使不繼承 Subject 也沒(méi)有關(guān)系,這樣可以轉(zhuǎn)化為委托模式,但系統(tǒng)可以選擇使用代理,當(dāng)然也可以選擇不使用代理,直接使用 RealSubject ,這時(shí),就需要它去實(shí)現(xiàn) Subject 接口了。所以 RealSubject 和 Proxy 的公用方法必須一致。
9. ?????? 享元模式 (FlyWeight Pattern)
?
術(shù)語(yǔ)粗粒度和細(xì)粒度用來(lái)形容由組件的公共接口所暴露的細(xì)節(jié)層次的。細(xì)粒度組件通過(guò)其公共接口暴露了有關(guān)組件如何工作的大量細(xì)節(jié),所以它的重用性比較好,但不靈活。提供公共接口但并不暴露其操作細(xì)節(jié)的組件則稱為粗粒度,它重用性差,但更靈活。
細(xì)粒度的查詢?nèi)蝿?wù)的接口
interface TaskService{
? public List getTaskById(int id);
? public List getTaskByName(String name);
? public List getTaskByAge(int age);
}
那么粗粒度的接口該是什么樣的呢?
interface TaskService{
? public List getTask(Person person);
}
?
享元模式以共享的方式高效地支持大量的細(xì)粒度對(duì)象。能做到共享的關(guān)鍵是區(qū)分內(nèi)蘊(yùn)狀態(tài)和外蘊(yùn)狀態(tài)。
一個(gè)內(nèi)蘊(yùn)狀態(tài)是存儲(chǔ)在享元對(duì)象內(nèi)部的,并且是不會(huì)隨環(huán)境改變而有所不同的。因此,一個(gè)享元可以具有內(nèi)蘊(yùn)狀態(tài)并可以共享。
一個(gè)外蘊(yùn)狀態(tài)是隨環(huán)境改變而改變的、不可以共享的狀態(tài)。享元對(duì)象的外蘊(yùn)狀態(tài)必須由客戶端保存,并在享元對(duì)象被創(chuàng)建之后,在需要使用的時(shí)候再傳入到享元對(duì)象內(nèi)部。外蘊(yùn)狀態(tài)不可能影響享元對(duì)象的內(nèi)蘊(yùn)狀態(tài)。換句話說(shuō),它們是相互獨(dú)立的。
在 java 語(yǔ)言中, String 類型就是使用了享元模式。 String 對(duì)象是不變對(duì)象,一旦創(chuàng)建出來(lái)就不能改變。如果需要改變一個(gè)字符串的值,就只好創(chuàng)建一個(gè)新的 String 對(duì)象。在 jvm 內(nèi)部, String 對(duì)象都是共享的,如果一個(gè)系統(tǒng)中有兩個(gè) String 對(duì)象所包含的字符串相同的話, jvm 實(shí)際上只創(chuàng)建一個(gè) String 對(duì)象提供給兩個(gè)引用。從而實(shí)現(xiàn) String 對(duì)象的共享。 String 的 intern() 方法給出這個(gè)字符串的共享池中的惟一實(shí)例。
客戶端不能將具體享元對(duì)象實(shí)例化,而是必須通過(guò)工廠對(duì)象 FlyweightFactory 使用 getFlyweight(key) 來(lái)得到享元對(duì)象。并有一個(gè) map 來(lái)存放這些對(duì)象,即如果已經(jīng)創(chuàng)建過(guò),就直接得到,如果沒(méi)有創(chuàng)建過(guò),那就新建。 Flyweight 提供了方法 Operation() ,這是可以通過(guò)參量方式傳入一個(gè)外蘊(yùn)狀態(tài)。
class ConcreteFlyweight implements Flyweight{
???????? // 內(nèi)蘊(yùn)狀態(tài)
???????? private Character intrinsicState = null;
???????? // 構(gòu)造子,內(nèi)蘊(yùn)狀態(tài)作為參量傳入, FlyweightFactory 工廠創(chuàng)建
???????? Public ConcreteFlyweight(Charater state){
?????????????????? This.intrinsicState = state;
???????? }
???????? // 外蘊(yùn)狀態(tài)作為參量傳入方法中,改變方法的行為,但是并不改變對(duì)象的內(nèi)蘊(yùn)狀態(tài)
???????? public void operation(String state){
?????????????????? System.out.println(“ 內(nèi)蘊(yùn)狀態(tài) =”+? state+” 外蘊(yùn)狀態(tài) =”+state);
???????? }
}
這樣,通過(guò)調(diào)用 operation 方法,可以在不改變內(nèi)蘊(yùn)狀態(tài) intrinsicState 的情況下,任意改變外蘊(yùn)狀態(tài)。
享元模式優(yōu)點(diǎn)在于能大同謀地降低內(nèi)存中對(duì)象的數(shù)量。但是它使得系統(tǒng)更加復(fù)雜,為了使對(duì)象可以共享,需要將一些狀態(tài)外部化,這使得程序的邏輯復(fù)雜化。
10. ?????? 門(mén)面模式( Facade )
迪米特法則說(shuō):“只與你直接的朋友們通信”。迪米特法要求每一個(gè)對(duì)象與其他對(duì)象的相互作用均是第三種的,而不是長(zhǎng)和的。只要可能,朋友的數(shù)目越少越好。換言之,一個(gè)對(duì)象只應(yīng)當(dāng)知道它的直接合作者的接口。
門(mén)面模式創(chuàng)建出一個(gè)門(mén)面對(duì)象,將客戶端所涉及的屬于一個(gè)子系統(tǒng)的協(xié)作伙伴的數(shù)目減到最少,使得客戶端與子系統(tǒng)內(nèi)部的對(duì)象的相互作用被門(mén)面對(duì)象所取代。顯然,門(mén)面模式就是實(shí)現(xiàn)代碼重構(gòu)以便達(dá)到迪米特法則要求的一個(gè)強(qiáng)有國(guó)的武器。
11. ?????? 橋梁模式( Bridge )
橋梁模式的用意是將抽象化與實(shí)現(xiàn)化脫耦,使得二者可以獨(dú)立地變化。
找到系統(tǒng)的可變因素,將之封裝起來(lái),通常就叫做對(duì)變化的封裝。對(duì)變化的封裝實(shí)際上是達(dá)到“開(kāi) - 閉”原則的途徑,與組合 / 聚合復(fù)用原則是相輔相成的。
一般來(lái)說(shuō),一個(gè)繼承結(jié)構(gòu)中的第一層是抽象角色,封裝了抽象的商業(yè)邏輯,這是系統(tǒng)中不變的部分。第二層是實(shí)現(xiàn)角色,封裝了設(shè)計(jì)中會(huì)變化的因素。這個(gè)實(shí)現(xiàn)請(qǐng)?jiān)试S實(shí)現(xiàn)化角色有多態(tài)性變化。
當(dāng)實(shí)現(xiàn)化需要改變時(shí),就換個(gè)實(shí)現(xiàn)化;但當(dāng)抽象化模塊需要改變時(shí),比如公共算法的改變,再添加一些其它功能,這樣就得改整個(gè)系統(tǒng)。所以要將抽象化與實(shí)現(xiàn)化脫耦。
Jdbc 驅(qū)動(dòng)器就是用了橋接模式
對(duì)象形式的的適配器模式可能看上去很像橋梁模式。然而適配器模式的目的是要改變已有的接口,讓它們可以相容,以使沒(méi)有關(guān)系的兩個(gè)類能在一起工作。而橋梁模式是分享抽象化和實(shí)現(xiàn)化,使得兩者的接口可以不同。因此兩個(gè)模式是相反方向努力。
行為模式
行為模式( Behavioral Pattern )是對(duì)在不同的對(duì)象之間劃分責(zé)任和算法的抽象化。行為模式不僅僅是關(guān)于類和對(duì)象的,而且是關(guān)于它們之間的相互作用的。
12. ?????? 不變模式( Immutable )
一個(gè)對(duì)象的狀態(tài)在對(duì)象被創(chuàng)建之后就不再變化,這就是所謂的不變模式。
不變模式可增強(qiáng)對(duì)象的強(qiáng)壯性。不變模式允許多個(gè)對(duì)象共享某一對(duì)象,降低了對(duì)該對(duì)象發(fā)訪問(wèn)時(shí)的同步化開(kāi)銷。如果需要修改一個(gè)不變對(duì)象的狀態(tài),那么就需要建立一個(gè)新的同類的對(duì)象,并在創(chuàng)建時(shí)將這個(gè)新的狀態(tài)存儲(chǔ)在新對(duì)象里。
不變模式分為兩種:弱不變,所考慮的對(duì)象沒(méi)有任何方法會(huì)修改對(duì)象的狀態(tài);這樣一來(lái),當(dāng)對(duì)象的構(gòu)造子將對(duì)象的狀態(tài)初始化之后,對(duì)象的狀態(tài)便不再改變。所有屬性都應(yīng)當(dāng)是私有的,不要聲明任何的公開(kāi)的屬性,以防客戶端對(duì)象直接修改任何的內(nèi)部狀態(tài)。這個(gè)對(duì)象所引用到的其它對(duì)象如果是可變對(duì)象的話,必須高潮限制外界改變這些引用的可變對(duì)象。最好將這可變對(duì)象復(fù)制一份,不要使用原來(lái)的拷貝。
但它的子對(duì)象可以是可變對(duì)象;子對(duì)象可能可以修改父對(duì)象的狀態(tài),從而可能會(huì)允許外界修改父對(duì)象的狀態(tài);
強(qiáng)不變:一個(gè)類的實(shí)例狀態(tài)不會(huì)改變;同時(shí)它的子類的實(shí)例也具有不可變化的狀態(tài)。這樣的類符合強(qiáng)不變模式。必須滿足下面條件之一。
<!--[if !supportLists]--> 1. ????????????? <!--[endif]--> 所考慮的類所有的方法都應(yīng)當(dāng)是 final ;這樣這個(gè)類的子類不能夠轉(zhuǎn)換掉此類的方法;
<!--[if !supportLists]--> 2. ????????????? <!--[endif]--> 這個(gè)類本身就是 final 的,那么這個(gè)類就不可能會(huì)有子類,從而也就不可能有被子類修改的問(wèn)題。
不變和之讀是有區(qū)別的:比如一個(gè)人的出生年月是不變的,而一個(gè)人的年齡便是只讀的,它會(huì)變化,但不能人為的改變。
不變模式的優(yōu)點(diǎn): 1. 因?yàn)椴荒苄薷囊粋€(gè)不變對(duì)象的狀態(tài),所以可以避免由此引起的不必要的程序錯(cuò)誤:換言之,一個(gè)不變對(duì)象要比可變對(duì)象的對(duì)象更加容易維護(hù)。 2. 因?yàn)闆](méi)有任何一個(gè)線程能夠修改不變對(duì)象的內(nèi)部狀態(tài),一個(gè)不變對(duì)象自動(dòng)就是線程安全的,這樣就可以省掉處理同步化的開(kāi)銷。
13. ?????? 策略模式( Strategy )
策略模式是針對(duì)一組算法,將每個(gè)算法封裝到具有共同接口的獨(dú)立的類中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶的情況下發(fā)生變化。
策略模式是對(duì)算法的包裝,是把使用算法的責(zé)任和算法本身分割開(kāi),委派給不同的對(duì)象管理。策略模式通常把一個(gè)系列的算法包裝到一系列的策略類里面,作為一個(gè)抽象策略的子類。用一句話說(shuō),就是:“準(zhǔn)備一組算法,并將每一個(gè)算法封裝起來(lái),使得它們可以互換”。
14. ?????? 模板方法模式( Template Method )
準(zhǔn)備一個(gè)抽象類,將部分邏輯以具體方法以及具體構(gòu)造子的形式實(shí)現(xiàn),然后聲明一些抽象方法來(lái)迫使子類實(shí)現(xiàn)剩余的邏輯。不同的子類可以以不同的方式實(shí)現(xiàn)這些抽象方法,從而對(duì)剩余的邏輯有不同的實(shí)現(xiàn)。
15. ?????? 觀察者模式
觀察者模式分為兩種,推模型和拉模型。
?
拉模型
推模型
通常情況下用拉模式更何情何理,既然是觀察對(duì)象,那么觀察者 observer 應(yīng)該自己可以去把想要的數(shù)據(jù)弄過(guò)來(lái)。但是,拉模式中,觀察都需要有個(gè)觀察對(duì)象的引用。而這在推模型中是不需要的。
但推模型有個(gè)好處,就是當(dāng)觀察對(duì)象有一堆數(shù)據(jù),但只有很少的幾個(gè)做了變化時(shí),拉模型的 observer 是不知道哪些起了變化的,它只能遍歷 subject 的所有屬性;而推模型就可以把起變化的數(shù)據(jù)推給觀察者,同時(shí)能有個(gè) hint 來(lái)做提示,當(dāng)然,它可以是個(gè)參數(shù)、枚舉、字符串等,它的值都可以推給觀察者。
16. ?????? 迭代子模式( Iterator )
迭代子模式又叫游標(biāo)模式,可以順序地訪問(wèn)一個(gè)聚集中的元素而不必暴露聚集的內(nèi)部表象。
聚集對(duì)象必須提供適當(dāng)?shù)姆椒ǎ试S客戶端能夠按照一個(gè)線性順序遍歷所有元素對(duì)象,把元素對(duì)象提取出來(lái)或者刪掉掉等。那么就會(huì)出現(xiàn)兩種情況: 1. 迭代邏輯沒(méi)有改變,但是需要將一種聚集換成另一種聚集,如果它們有不同的遍歷接口,那就要改客戶端代碼。 2. 聚集不改變,但迭代方式要改變,比如以前只要讀取元素和刪除元素,現(xiàn)在又加上添加元素,這時(shí)就只好修改聚集對(duì)象,修改已有的遍歷方法。所以使用聚集時(shí),就需要迭代子。
如果一個(gè)聚集的接口提供了可以用來(lái)修改聚集元素的方法,這個(gè)接口就是寬接口;如果一個(gè)聚集的接口沒(méi)有提供修改聚集元素的方法,這樣的接口就是窄接口。
上圖中, Aggregate 對(duì) client 就是窄接口,顯然不想讓客戶端能夠直接改變聚集的內(nèi)容;而 ConcreteAggregate 對(duì) ConcreateIterator 就是寬接口,具體迭代子必須能夠控制聚集的內(nèi)容。
上圖所示的是白箱聚集與外稟迭代子。
外稟指具體迭代子類是在外面,而內(nèi)稟則是具體迭代子類是具體聚集的內(nèi)部類,這樣能夠訪問(wèn)具體聚集的私有成員和方法。
白箱聚集向外界提供訪問(wèn)自己內(nèi)部元素的接口,從而使外稟迭代子可以通過(guò)聚集的遍歷方法實(shí)現(xiàn)迭代功能,如上圖,但這樣是不安全的,如果 client 沒(méi)有使用迭代子,直接使用白箱聚集的操作接口,就可能出現(xiàn)問(wèn)題。
黑箱聚集不向外部提供遍歷自己元素對(duì)象的接口,因此,這些元素對(duì)象只可以被內(nèi)部成員訪問(wèn)。這時(shí)就用到內(nèi)稟迭代子了。
由于迭代子要存儲(chǔ)當(dāng)前遍歷的游標(biāo)(當(dāng)前元素位置),所以白箱聚集使用外稟迭代子時(shí),可以是不變對(duì)象,前提是它的聚集元素是不會(huì)改變的。但黑箱由于提供的內(nèi)稟迭代子必須存儲(chǔ)動(dòng)態(tài)的游標(biāo),所以它不可能是不變對(duì)象。
AbstractList 是白箱聚集,它向外部提供了自己的遍歷方法,所以我們可以自定義自己的外稟迭代子,但它也使用了內(nèi)稟迭代子,即內(nèi)部類 Itr ,做為默認(rèn)的迭代實(shí)現(xiàn)。
17. ?????? 責(zé)任鏈模式( Chain of Responsibility )
在責(zé)任鏈模式里,很多對(duì)象由每一個(gè)對(duì)象對(duì)其下家的引用而連接起來(lái)形成一條鏈。請(qǐng)求在這個(gè)鏈上傳遞,直到鏈上的某個(gè)對(duì)象決定處理些請(qǐng)求。發(fā)出這個(gè)請(qǐng)求的客戶端并不知道鏈上的哪一個(gè)對(duì)象最終處理這個(gè)請(qǐng)求,這使得系統(tǒng)可以在不影響客戶端的情況下動(dòng)態(tài)地重新組織鏈和分配責(zé)任。
具體處理者( ConcreteHandler )接到請(qǐng)求后,可以選擇將請(qǐng)求處理掉,或者將請(qǐng)求傳給下家。由于具體處理者持有對(duì)下家的引用,因此,如果需要,具體處理者可以訪問(wèn)下家。
責(zé)任鏈模式分為兩種。一個(gè)純的責(zé)任鏈模式要求一個(gè)具體的處理者對(duì)象只能在兩個(gè)行為中選擇一個(gè):一個(gè)是承擔(dān)責(zé)任,二是把責(zé)任推給下家。不允許出現(xiàn)某一個(gè)具體處理者對(duì)象在承擔(dān)了一部分責(zé)任后又把責(zé)任向下傳的情況。在一個(gè)純的責(zé)任鏈模式里面,一個(gè)請(qǐng)求必須被某一個(gè)處理者對(duì)象所接收;在一個(gè)不純的責(zé)任鏈模式里,一個(gè)請(qǐng)求可以最終不被任何接收端對(duì)象所接收。
鏈結(jié)構(gòu)的由來(lái):責(zé)任鏈模式并不創(chuàng)建出責(zé)任鏈,而是由系統(tǒng)的其它部分比如客戶端來(lái)創(chuàng)建出來(lái)。客戶端負(fù)責(zé)將每個(gè)責(zé)任對(duì)象創(chuàng)建出來(lái),并手動(dòng)指定責(zé)任對(duì)象的下家。
18. ?????? 命令模式
命令模式把一個(gè)請(qǐng)求或者操作封裝到一個(gè)對(duì)象中。命令模式允許系統(tǒng)使用不同的請(qǐng)求把客戶端參數(shù)化。
客戶端代碼:
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);// 決定接收者
Invoker invoker = new Invoker(command);
Invoker.action();
19. 備忘錄模式( Memento )
備忘錄對(duì)象是個(gè)用來(lái)存儲(chǔ)另外一個(gè)對(duì)象內(nèi)部狀態(tài)的快照的對(duì)象。備忘錄模式的用意是在不破壞封裝的條件下,將一個(gè)對(duì)象的狀態(tài)捕捉住,并外部化,存儲(chǔ)起來(lái),從而可以在將來(lái)合適的時(shí)候把這個(gè)對(duì)象還原到存儲(chǔ)起來(lái)的狀態(tài)。
備忘錄角色( Memento ): 1. 將發(fā)起人( Originator )對(duì)象的內(nèi)部狀態(tài)存儲(chǔ)起來(lái)。備忘錄可以根據(jù)發(fā)起人對(duì)象的判斷來(lái)決定存儲(chǔ)多少發(fā)起人對(duì)象的內(nèi)部狀態(tài)。 2 ,可以保護(hù)其內(nèi)容不被發(fā)起人對(duì)象之外的對(duì)象所讀取。備忘錄有兩個(gè)等效的接口:
a) ???????? 窄接口:負(fù)責(zé)人( Caretaker )對(duì)象(和其他除發(fā)起人對(duì)象之外的任何對(duì)象)看到的是備忘錄的窄接口,這個(gè)窄接口只允許它把備忘錄對(duì)象傳給其他對(duì)象。
b) ???????? 寬接口:與負(fù)責(zé)人對(duì)象看到的窄接口相反的是,發(fā)起人對(duì)象可以看到一個(gè)寬接口,這個(gè)寬接口允許它讀取所有的數(shù)據(jù),以便根據(jù)這些數(shù)據(jù)恢復(fù)這個(gè)發(fā)起人對(duì)象的內(nèi)部狀態(tài)。
發(fā)起人角色( Originator ):創(chuàng)建一個(gè)含有當(dāng)前的內(nèi)部狀態(tài)的備忘錄對(duì)象。使用備忘錄對(duì)象存儲(chǔ)其內(nèi)部狀態(tài)。
???? 負(fù)責(zé)人角色( Caretaker ):負(fù)責(zé)保存?zhèn)渫泴?duì)象。不檢查備忘錄對(duì)象的內(nèi)容。
20. 狀態(tài)模式( State )
狀態(tài)模式允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)候改變其行為。這個(gè)對(duì)象看上去就像是改變了它的類一樣。
狀態(tài)模式和策略模式非常像。如果環(huán)境角色只有一個(gè)狀態(tài),那么就應(yīng)當(dāng)用策略模式。策略模式的特點(diǎn)是:一旦環(huán)境角色選擇了一個(gè)具體策略類,那么在整個(gè)環(huán)境類的生命周期里它都不會(huì)改變這個(gè)具體策略類。而狀態(tài)模式則適用于另一個(gè)情況,即環(huán)境角色有狀態(tài)轉(zhuǎn)移。在環(huán)境類的生命周期里面,會(huì)有幾個(gè)不同的狀態(tài)對(duì)象被使用。
21. 訪問(wèn)者模式( visitor )
訪問(wèn)者模式的目的是封裝一些施加于某種數(shù)據(jù)結(jié)構(gòu)元素之上的操作。一旦這些操作需要修改的話,接受這個(gè)操作的數(shù)據(jù)結(jié)構(gòu)則可以保持不變。
訪問(wèn)者模式使得增加新的操作變得很容易。如果一些操作依賴于一個(gè)復(fù)雜的結(jié)構(gòu)對(duì)象的話,那么一般而言,增加新的操作會(huì)很復(fù)雜。而使用訪問(wèn)者模式,增加新的操作就意味著增加一個(gè)新的訪問(wèn)者類,因此變得的很容易。訪問(wèn)者模式將有關(guān)的行為集中到一個(gè)訪問(wèn)者對(duì)象中,而不是分散到一個(gè)個(gè)的節(jié)點(diǎn)類中。訪問(wèn)者模式可以路過(guò)幾個(gè)類的等級(jí)結(jié)構(gòu)訪問(wèn)屬于不兩只的等級(jí)結(jié)構(gòu)的成員類中。迭代子只能訪問(wèn)屬于同一個(gè)類型等級(jí)結(jié)構(gòu)的成員對(duì)象,而不能訪問(wèn)屬于不同等級(jí)結(jié)構(gòu)的對(duì)象。訪問(wèn)者模式可以做到這一點(diǎn)。
訪問(wèn)者模式的缺點(diǎn):增加新的節(jié)點(diǎn)類變得很困難。每增加一個(gè)新的節(jié)點(diǎn)都意味著要在抽象訪問(wèn)者角色中增加一個(gè)新的抽象操作,并在每一個(gè)具體訪問(wèn)者類中增加相應(yīng)的具體操作。破壞封裝,訪問(wèn)者模式要求訪問(wèn)者對(duì)象訪問(wèn)并調(diào)用每個(gè)節(jié)點(diǎn)的對(duì)象操作,這隱含了一個(gè)對(duì)所有節(jié)點(diǎn)對(duì)象的要求:它們必須暴露一些自己的操作和內(nèi)部狀態(tài)。不然,訪問(wèn)者的訪問(wèn)就沒(méi)有意義。由于訪問(wèn)者對(duì)象自己會(huì)積累訪問(wèn)操作所需要的狀態(tài),從而使這些狀態(tài)不再存儲(chǔ)在節(jié)點(diǎn)對(duì)象中,這也是破壞封裝。
22. 解釋器模式( Interpreter )
給定一個(gè)語(yǔ)言之后,解釋器模式可以定義出其文法的一種表示,并同時(shí)提供一個(gè)解釋器。客商可以使用這個(gè)解釋器來(lái)解釋這個(gè)語(yǔ)言中的句子。
23. 調(diào)停者模式( Mediator )
調(diào)停者模式包裝了一系列對(duì)象相互作用的方式,使得這些對(duì)象不必互相明顯引用。從而使它們可以較松散地耦合。當(dāng)這些對(duì)象中的某些對(duì)象之間的相互作用發(fā)生改變時(shí),不會(huì)立即影響到其他的一些對(duì)象之間的酵素作用。從而保證這些相互作用可以彼此獨(dú)立地變化。
調(diào)停者和門(mén)面模式很相似。門(mén)面模式為一個(gè)子系統(tǒng)提供了一個(gè)簡(jiǎn)單的接口,其中消息的傳送是單方向的,因?yàn)殚T(mén)面模式的客戶端只通過(guò)門(mén)面類向子系統(tǒng)發(fā)出消息,而不是相反的情況。調(diào)停者模式則不同,它與同事對(duì)象的相互作用是多方向的。
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
