23中模式的解析

一、設(shè)計模式的分類

總體來說設(shè)計模式分為三大類:

創(chuàng)建型模式析苫,共五種:工廠方法模式兜叨、抽象工廠模式、單例模式衩侥、建造者模式国旷、原型模式。

結(jié)構(gòu)型模式茫死,共七種:適配器模式跪但、裝飾器模式、代理模式峦萎、外觀模式屡久、橋接模式、組合模式爱榔、享元模式被环。

行為型模式,共十一種:策略模式详幽、模板方法模式筛欢、觀察者模式浸锨、迭代子模式、責(zé)任鏈模式版姑、命令模式柱搜、備忘錄模式、狀態(tài)模式剥险、訪問者模式聪蘸、中介者模式、解釋器模式炒嘲。

其實還有兩類:并發(fā)型模式和線程池模式宇姚。用一個圖片來整體描述一下:

二、設(shè)計模式的六大原則

總原則:開閉原則(Open Close

Principle

開閉原則就是說對擴展開放夫凸,對修改關(guān)閉浑劳。在程序需要進行拓展的時候,不能去修改原有的代碼夭拌,而是要擴展原有代碼魔熏,實現(xiàn)一個熱插拔的效果。所以一句話概括就是:為了使程序的擴展性好鸽扁,易于維護和升級蒜绽。想要達到這樣的效果,我們需要使用接口和抽象類等桶现,后面的具體設(shè)計中我們會提到這點躲雅。

1、單一職責(zé)原則

不要存在多于一個導(dǎo)致類變更的原因骡和,也就是說每個類應(yīng)該實現(xiàn)單一的職責(zé)相赁,如若不然,就應(yīng)該把類拆分慰于。

2钮科、里氏替換原則(Liskov

Substitution Principle

里氏代換原則(Liskov

Substitution Principle LSP)面向?qū)ο笤O(shè)計的基本原則之一。里氏代換原則中說婆赠,任何基類可以出現(xiàn)的地方绵脯,子類一定可以出現(xiàn)。LSP是繼承復(fù)用的基石休里,只有當衍生類可以替換掉基類蛆挫,軟件單位的功能不受到影響時,基類才能真正被復(fù)用份帐,而衍生類也能夠在基類的基礎(chǔ)上增加新的行為璃吧。里氏代換原則是對“開-閉”原則的補充。實現(xiàn)“開-閉”原則的關(guān)鍵步驟就是抽象化废境。而基類與子類的繼承關(guān)系就是抽象化的具體實現(xiàn)畜挨,所以里氏代換原則是對實現(xiàn)抽象化的具體步驟的規(guī)范筒繁。—— From Baidu百科

歷史替換原則中巴元,子類對父類的方法盡量不要重寫和重載毡咏。因為父類代表了定義好的結(jié)構(gòu),通過這個規(guī)范的接口與外界交互逮刨,子類不應(yīng)該隨便破壞它呕缭。

3、依賴倒轉(zhuǎn)原則(Dependence

Inversion Principle

這個是開閉原則的基礎(chǔ)修己,具體內(nèi)容:面向接口編程恢总,依賴于抽象而不依賴于具體。寫代碼時用到具體類時睬愤,不與具體類交互片仿,而與具體類的上層接口交互。

4尤辱、接口隔離原則(Interface

Segregation Principle

這個原則的意思是:每個接口中不存在子類用不到卻必須實現(xiàn)的方法砂豌,如果不然,就要將接口拆分光督。使用多個隔離的接口阳距,比使用單個接口(多個接口方法集合到一個的接口)要好。

5结借、迪米特法則(最少知道原則)(Demeter Principle

就是說:一個類對自己依賴的類知道的越少越好筐摘。也就是說無論被依賴的類多么復(fù)雜,都應(yīng)該將邏輯封裝在方法的內(nèi)部船老,通過public方法提供給外部蓄拣。這樣當被依賴的類變化時,才能最小的影響該類努隙。

最少知道原則的另一個表達方式是:只與直接的朋友通信。類之間只要有耦合關(guān)系辜昵,就叫朋友關(guān)系荸镊。耦合分為依賴、關(guān)聯(lián)堪置、聚合躬存、組合等。我們稱出現(xiàn)為成員變量舀锨、方法參數(shù)岭洲、方法返回值中的類為直接朋友。局部變量坎匿、臨時變量則不是直接的朋友盾剩。我們要求陌生的類不要作為局部變量出現(xiàn)在類中雷激。

6、合成復(fù)用原則(Composite

Reuse Principle

原則是盡量首先使用合成/聚合的方式告私,而不是使用繼承屎暇。

三、Java23中設(shè)計模式

A驻粟、創(chuàng)建模式

從這一塊開始根悼,我們詳細介紹Java中23種設(shè)計模式的概念,應(yīng)用場景等情況蜀撑,并結(jié)合他們的特點及設(shè)計模式的原則進行分析挤巡。

首先,簡單工廠模式不屬于23中涉及模式酷麦,簡單工廠一般分為:普通簡單工廠矿卑、多方法簡單工廠、靜態(tài)方法簡單工廠贴铜。

0粪摘、簡單工廠模式

簡單工廠模式模式分為三種:

01、普通

就是建立一個工廠類绍坝,對實現(xiàn)了同一接口的一些類進行實例的創(chuàng)建徘意。首先看下關(guān)系圖:

舉例如下:(我們舉一個發(fā)送郵件和短信的例子)

首先,創(chuàng)建二者的共同接口:

[java]view plaincopy

1.publicinterfaceSender?{

2.publicvoidSend();

3.}

其次轩褐,創(chuàng)建實現(xiàn)類:

[java]view plaincopy

1.publicclassMailSenderimplementsSender?{

2.@Override

3.publicvoidSend()?{

4.System.out.println("this?is?mailsender!");

5.}

6.}

[java]view plaincopy

1.publicclassSmsSenderimplementsSender?{

2.

3.@Override

4.publicvoidSend()?{

5.System.out.println("this?is?sms?sender!");

6.}

7.}

最后椎咧,建工廠類:

[java]view plaincopy

1.publicclassSendFactory?{

2.

3.publicSender?produce(String?type)?{

4.if("mail".equals(type))?{

5.returnnewMailSender();

6.}elseif("sms".equals(type))?{

7.returnnewSmsSender();

8.}else{

9.System.out.println("請輸入正確的類型!");

10.returnnull;

11.}

12.}

13.}

我們來測試下:

1.publicclassFactoryTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.SendFactory?factory?=newSendFactory();

5.Sender?sender?=?factory.produce("sms");

6.sender.Send();

7.}

8.}

輸出:this is smssender!

02、多個方法

是對普通工廠方法模式的改進把介,在普通工廠方法模式中勤讽,如果傳遞的字符串出錯,則不能正確創(chuàng)建對象拗踢,而多個工廠方法模式是提供多個工廠方法脚牍,分別創(chuàng)建對象。關(guān)系圖:

將上面的代碼做下修改巢墅,改動下SendFactory類就行诸狭,如下:

[java]view plaincopypublicclassSendFactory?{

publicSender?produceMail(){

1.returnnewMailSender();

2.}

3.

4.publicSender?produceSms(){

5.returnnewSmsSender();

6.}

7.}

測試類如下:

[java]view plaincopy

1.publicclassFactoryTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.SendFactory?factory?=newSendFactory();

5.Sender?sender?=?factory.produceMail();

6.sender.Send();

7.}

8.}

輸出:this ismailsender!

03、多個靜態(tài)方法

將上面的多個工廠方法模式里的方法置為靜態(tài)的君纫,不需要創(chuàng)建實例驯遇,直接調(diào)用即可。

[java]view plaincopy

1.publicclassSendFactory?{

2.

3.publicstaticSender?produceMail(){

4.returnnewMailSender();

5.}

6.

7.publicstaticSender?produceSms(){

8.returnnewSmsSender();

9.}

10.}

[java]view plaincopy

1.publicclassFactoryTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Sender?sender?=?SendFactory.produceMail();

5.sender.Send();

6.}

7.}

輸出:this ismailsender!

總體來說蓄髓,工廠模式適合:凡是出現(xiàn)了大量的產(chǎn)品需要創(chuàng)建叉庐,并且具有共同的接口時,可以通過工廠方法模式進行創(chuàng)建会喝。在以上的三種模式中陡叠,第一種如果傳入的字符串有誤玩郊,不能正確創(chuàng)建對象,第三種相對于第二種匾竿,不需要實例化工廠類瓦宜,所以,大多數(shù)情況下岭妖,我們會選用第三種——靜態(tài)工廠方法模式临庇。

1、工廠方法模式(Factory

Method

簡單工廠模式有一個問題就是昵慌,類的創(chuàng)建依賴工廠類假夺,也就是說,如果想要拓展程序斋攀,必須對工廠類進行修改已卷,這違背了閉包原則,所以淳蔼,從設(shè)計角度考慮侧蘸,有一定的問題,如何解決鹉梨?就用到工廠方法模式讳癌,創(chuàng)建一個工廠接口和創(chuàng)建多個工廠實現(xiàn)類,這樣一旦需要增加新的功能存皂,直接增加新的工廠類就可以了晌坤,不需要修改之前的代碼。

請看例子:

[java]view plaincopy

1.publicinterfaceSender?{

2.publicvoidSend();

3.}

兩個實現(xiàn)類:

[java]view plaincopy

1.publicclassMailSenderimplementsSender?{

2.@Override

3.publicvoidSend()?{

4.System.out.println("this?is?mailsender!");

5.}

6.}

[java]view plaincopy

1.publicclassSmsSenderimplementsSender?{

2.

3.@Override

4.publicvoidSend()?{

5.System.out.println("this?is?sms?sender!");

6.}

7.}

兩個工廠類:

[java]view plaincopy

1.publicclassSendMailFactoryimplementsProvider?{

2.

3.@Override

4.publicSender?produce(){

5.returnnewMailSender();

6.}

7.}

[java]view plaincopy

1.publicclassSendSmsFactoryimplementsProvider{

2.

3.@Override

4.publicSender?produce()?{

5.returnnewSmsSender();

6.}

7.}

在提供一個接口:

[java]view plaincopy

1.publicinterfaceProvider?{

2.publicSender?produce();

3.}

測試類:

[java]view plaincopy

1.publicclassTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Provider?provider?=newSendMailFactory();

5.Sender?sender?=?provider.produce();

6.sender.Send();

7.}

8.}

其實這個模式的好處就是旦袋,如果你現(xiàn)在想增加一個功能:發(fā)及時信息骤菠,則只需做一個實現(xiàn)類,實現(xiàn)Sender接口疤孕,同時做一個工廠類商乎,實現(xiàn)Provider接口,就OK了祭阀,無需去改動現(xiàn)成的代碼截亦。這樣做,拓展性較好柬讨!

2、抽象工廠模式

工廠方法模式和抽象工廠模式不好分清楚袍啡,他們的區(qū)別如下:

工廠方法模式:

一個抽象產(chǎn)品類踩官,可以派生出多個具體產(chǎn)品類。

一個抽象工廠類境输,可以派生出多個具體工廠類蔗牡。

每個具體工廠類只能創(chuàng)建一個具體產(chǎn)品類的實例颖系。

抽象工廠模式:

多個抽象產(chǎn)品類,每個抽象產(chǎn)品類可以派生出多個具體產(chǎn)品類辩越。

一個抽象工廠類嘁扼,可以派生出多個具體工廠類。

每個具體工廠類可以創(chuàng)建多個具體產(chǎn)品類的實例黔攒,也就是創(chuàng)建的是一個產(chǎn)品線下的多個產(chǎn)品趁啸。

區(qū)別:

工廠方法模式只有一個抽象產(chǎn)品類,而抽象工廠模式有多個督惰。

工廠方法模式的具體工廠類只能創(chuàng)建一個具體產(chǎn)品類的實例不傅,而抽象工廠模式可以創(chuàng)建多個。

工廠方法創(chuàng)建"一種"產(chǎn)品赏胚,他的著重點在于"怎么創(chuàng)建"访娶,也就是說如果你開發(fā)树碱,你的大量代碼很可能圍繞著這種產(chǎn)品的構(gòu)造蛇券,初始化這些細節(jié)上面。也因為如此蜕依,類似的產(chǎn)品之間有很多可以復(fù)用的特征典勇,所以會和模版方法相隨劫哼。

抽象工廠需要創(chuàng)建一些列產(chǎn)品,著重點在于"創(chuàng)建哪些"產(chǎn)品上痴柔,也就是說沦偎,如果你開發(fā),你的主要任務(wù)是劃分不同差異的產(chǎn)品線咳蔚,并且盡量保持每條產(chǎn)品線接口一致豪嚎,從而可以從同一個抽象工廠繼承。

對于java來說谈火,你能見到的大部分抽象工廠模式都是這樣的:

---它的里面是一堆工廠方法侈询,每個工廠方法返回某種類型的對象。

比如說工廠可以生產(chǎn)鼠標和鍵盤糯耍。那么抽象工廠的實現(xiàn)類(它的某個具體子類)的對象都可以生產(chǎn)鼠標和鍵盤扔字,但可能工廠A生產(chǎn)的是羅技的鍵盤和鼠標,工廠B是微軟的温技。

這樣A和B就是工廠革为,對應(yīng)于抽象工廠;

每個工廠生產(chǎn)的鼠標和鍵盤就是產(chǎn)品舵鳞,對應(yīng)于工廠方法震檩;

用了工廠方法模式,你替換生成鍵盤的工廠方法,就可以把鍵盤從羅技換到微軟抛虏。但是用了抽象工廠模式博其,你只要換家工廠,就可以同時替換鼠標和鍵盤一套迂猴。如果你要的產(chǎn)品有幾十個慕淡,當然用抽象工廠模式一次替換全部最方便(這個工廠會替你用相應(yīng)的工廠方法)

所以說抽象工廠就像工廠,而工廠方法則像是工廠的一種產(chǎn)品生產(chǎn)線

3沸毁、單例模式(Singleton

單例對象(Singleton)是一種常用的設(shè)計模式峰髓。在Java應(yīng)用中,單例對象能保證在一個JVM中以清,該對象只有一個實例存在儿普。這樣的模式有幾個好處:

1、某些類創(chuàng)建比較頻繁掷倔,對于一些大型的對象眉孩,這是一筆很大的系統(tǒng)開銷。

2勒葱、省去了new操作符浪汪,降低了系統(tǒng)內(nèi)存的使用頻率,減輕GC壓力凛虽。

3死遭、有些類如交易所的核心交易引擎,控制著交易流程凯旋,如果該類可以創(chuàng)建多個的話呀潭,系統(tǒng)完全亂了。(比如一個軍隊出現(xiàn)了多個司令員同時指揮至非,肯定會亂成一團)钠署,所以只有使用單例模式,才能保證核心交易服務(wù)器獨立控制整個流程荒椭。

首先我們寫一個簡單的單例類:

[java]view plaincopy

1.publicclassSingleton?{

2.

3./*持有私有靜態(tài)實例谐鼎,防止被引用,此處賦值為null趣惠,目的是實現(xiàn)延遲加載*/

4.privatestaticSingleton?instance?=null;

5.

6./*私有構(gòu)造方法狸棍,防止被實例化*/

7.privateSingleton()?{

8.}

9.

10./*靜態(tài)工程方法,創(chuàng)建實例*/

11.publicstaticSingleton?getInstance()?{

12.if(instance?==null)?{

13.instance?=newSingleton();

14.}

15.returninstance;

16.}

17.

18./*如果該對象被用于序列化味悄,可以保證對象在序列化前后保持一致*/

19.publicObject?readResolve()?{

20.returninstance;

21.}

22.}

這個類可以滿足基本要求草戈,但是,像這樣毫無線程安全保護的類侍瑟,如果我們把它放入多線程的環(huán)境下猾瘸,肯定就會出現(xiàn)問題了,如何解決?我們首先會想到對getInstance方法加synchronized關(guān)鍵字牵触,如下:

[java]view plaincopy

1.publicstaticsynchronizedSingleton?getInstance()?{

2.if(instance?==null)?{

3.instance?=newSingleton();

4.}

5.returninstance;

6.}

但是,synchronized關(guān)鍵字鎖住的是這個對象咐低,這樣的用法揽思,在性能上會有所下降,因為每次調(diào)用getInstance()见擦,都要對對象上鎖钉汗,事實上,只有在第一次創(chuàng)建對象的時候需要加鎖鲤屡,之后就不需要了损痰,所以,這個地方需要改進酒来。我們改成下面這個:

[java]view plaincopy

1.publicstaticSingleton?getInstance()?{

2.if(instance?==null)?{

3.synchronized(instance)?{

4.if(instance?==null)?{

5.instance?=newSingleton();

6.}

7.}

8.}

9.returninstance;

10.}

似乎解決了之前提到的問題卢未,將synchronized關(guān)鍵字加在了內(nèi)部,也就是說當調(diào)用的時候是不需要加鎖的堰汉,只有在instance為null辽社,并創(chuàng)建對象的時候才需要加鎖,性能有一定的提升翘鸭。但是滴铅,這樣的情況,還是有可能有問題的就乓,看下面的情況:在Java指令中創(chuàng)建對象和賦值操作是分開進行的汉匙,也就是說instance = new Singleton();語句是分兩步執(zhí)行的。但是JVM并不保證這兩個操作的先后順序生蚁,也就是說有可能JVM會為新的Singleton實例分配空間噩翠,然后直接賦值給instance成員,然后再去初始化這個Singleton實例守伸。這樣就可能出錯了绎秒,我們以A、B兩個線程為例:

a>A尼摹、B線程同時進入了第一個if判斷

b>A首先進入synchronized塊见芹,由于instance為null,所以它執(zhí)行instance = new Singleton();

c>由于JVM內(nèi)部的優(yōu)化機制蠢涝,JVM先畫出了一些分配給Singleton實例的空白內(nèi)存玄呛,并賦值給instance成員(注意此時JVM沒有開始初始化這個實例),然后A離開了synchronized塊和二。

d>B進入synchronized塊徘铝,由于instance此時不是null,因此它馬上離開了synchronized塊并將結(jié)果返回給調(diào)用該方法的程序。

e>此時B線程打算使用Singleton實例惕它,卻發(fā)現(xiàn)它沒有被初始化怕午,于是錯誤發(fā)生了。

所以程序還是有可能發(fā)生錯誤淹魄,其實程序在運行過程是很復(fù)雜的郁惜,從這點我們就可以看出,尤其是在寫多線程環(huán)境下的程序更有難度甲锡,有挑戰(zhàn)性兆蕉。我們對該程序做進一步優(yōu)化:

[java]view plaincopy

1.privatestaticclassSingletonFactory{

2.privatestaticSingleton?instance?=newSingleton();

3.}

4.publicstaticSingleton?getInstance(){

5.returnSingletonFactory.instance;

6.}

實際情況是,單例模式使用內(nèi)部類來維護單例的實現(xiàn)缤沦,JVM內(nèi)部的機制能夠保證當一個類被加載的時候虎韵,這個類的加載過程是線程互斥的。這樣當我們第一次調(diào)用getInstance的時候缸废,JVM能夠幫我們保證instance只被創(chuàng)建一次包蓝,并且會保證把賦值給instance的內(nèi)存初始化完畢,這樣我們就不用擔心上面的問題呆奕。同時該方法也只會在第一次調(diào)用的時候使用互斥機制养晋,這樣就解決了低性能問題。這樣我們暫時總結(jié)一個完美的單例模式:

[java]view plaincopy

1.publicclassSingleton?{

2.

3./*私有構(gòu)造方法梁钾,防止被實例化*/

4.privateSingleton()?{

5.}

6.

7./*此處使用一個內(nèi)部類來維護單例*/

8.privatestaticclassSingletonFactory?{

9.privatestaticSingleton?instance?=newSingleton();

10.}

11.

12./*獲取實例*/

13.publicstaticSingleton?getInstance()?{

14.returnSingletonFactory.instance;

15.}

16.

17./*如果該對象被用于序列化绳泉,可以保證對象在序列化前后保持一致*/

18.publicObject?readResolve()?{

19.returngetInstance();

20.}

21.}

其實說它完美,也不一定姆泻,如果在構(gòu)造函數(shù)中拋出異常零酪,實例將永遠得不到創(chuàng)建,也會出錯拇勃。所以說四苇,十分完美的東西是沒有的,我們只能根據(jù)實際情況方咆,選擇最適合自己應(yīng)用場景的實現(xiàn)方法月腋。也有人這樣實現(xiàn):因為我們只需要在創(chuàng)建類的時候進行同步,所以只要將創(chuàng)建和getInstance()分開瓣赂,單獨為創(chuàng)建加synchronized關(guān)鍵字榆骚,也是可以的:

[java]view plaincopy

1.publicclassSingletonTest?{

2.

3.privatestaticSingletonTest?instance?=null;

4.

5.privateSingletonTest()?{

6.}

7.

8.privatestaticsynchronizedvoidsyncInit()?{

9.if(instance?==null)?{

10.instance?=newSingletonTest();

11.}

12.}

13.

14.publicstaticSingletonTest?getInstance()?{

15.if(instance?==null)?{

16.syncInit();

17.}

18.returninstance;

19.}

20.}

考慮性能的話,整個程序只需創(chuàng)建一次實例煌集,所以性能也不會有什么影響妓肢。

補充:采用"影子實例"的辦法為單例對象的屬性同步更新

[java]view plaincopy

1.publicclassSingletonTest?{

2.

3.privatestaticSingletonTest?instance?=null;

4.privateVector?properties?=null;

5.

6.publicVector?getProperties()?{

7.returnproperties;

8.}

9.

10.privateSingletonTest()?{

11.}

12.

13.privatestaticsynchronizedvoidsyncInit()?{

14.if(instance?==null)?{

15.instance?=newSingletonTest();

16.}

17.}

18.

19.publicstaticSingletonTest?getInstance()?{

20.if(instance?==null)?{

21.syncInit();

22.}

23.returninstance;

24.}

25.

26.publicvoidupdateProperties()?{

27.SingletonTest?shadow?=newSingletonTest();

28.properties?=?shadow.getProperties();

29.}

30.}

通過單例模式的學(xué)習(xí)告訴我們:

1、單例模式理解起來簡單苫纤,但是具體實現(xiàn)起來還是有一定的難度碉钠。

2纲缓、synchronized關(guān)鍵字鎖定的是對象,在用的時候喊废,一定要在恰當?shù)牡胤绞褂茫ㄗ⒁庑枰褂面i的對象和過程祝高,可能有的時候并不是整個對象及整個過程都需要鎖)。

到這兒污筷,單例模式基本已經(jīng)講完了褂策,結(jié)尾處,筆者突然想到另一個問題颓屑,就是采用類的靜態(tài)方法,實現(xiàn)單例模式的效果耿焊,也是可行的揪惦,此處二者有什么不同?

首先罗侯,靜態(tài)類不能實現(xiàn)接口器腋。(從類的角度說是可以的,但是那樣就破壞了靜態(tài)了钩杰。因為接口中不允許有static修飾的方法纫塌,所以即使實現(xiàn)了也是非靜態(tài)的)

其次,單例可以被延遲初始化讲弄,靜態(tài)類一般在第一次加載是初始化措左。之所以延遲加載,是因為有些類比較龐大避除,所以延遲加載有助于提升性能怎披。

再次,單例類可以被繼承瓶摆,他的方法可以被覆寫凉逛。但是靜態(tài)類內(nèi)部方法都是static,無法被覆寫群井。

最后一點状飞,單例類比較靈活,畢竟從實現(xiàn)上只是一個普通的Java類书斜,只要滿足單例的基本需求诬辈,你可以在里面隨心所欲的實現(xiàn)一些其它功能,但是靜態(tài)類不行菩佑。從上面這些概括中自晰,基本可以看出二者的區(qū)別,但是稍坯,從另一方面講酬荞,我們上面最后實現(xiàn)的那個單例模式搓劫,內(nèi)部就是用一個靜態(tài)類來實現(xiàn)的,所以混巧,二者有很大的關(guān)聯(lián)枪向,只是我們考慮問題的層面不同罷了。兩種思想的結(jié)合咧党,才能造就出完美的解決方案秘蛔,就像HashMap采用數(shù)組+鏈表來實現(xiàn)一樣,其實生活中很多事情都是這樣傍衡,單用不同的方法來處理問題深员,總是有優(yōu)點也有缺點,最完美的方法是蛙埂,結(jié)合各個方法的優(yōu)點倦畅,才能最好的解決問題!

4绣的、建造者模式(Builder

5叠赐、原型模式(Prototype

原型模式雖然是創(chuàng)建型的模式,但是與工程模式?jīng)]有關(guān)系屡江,從名字即可看出芭概,該模式的思想就是將一個對象作為原型,對其進行復(fù)制惩嘉、克隆罢洲,產(chǎn)生一個和原對象類似的新對象。本小結(jié)會通過對象的復(fù)制宏怔,進行講解奏路。在Java中,復(fù)制對象是通過clone()實現(xiàn)的臊诊,先創(chuàng)建一個原型類:

[java]view plaincopy

1.publicclassPrototypeimplementsCloneable?{

2.

3.publicObject?clone()throwsCloneNotSupportedException?{

4.Prototype?proto?=?(Prototype)super.clone();

5.returnproto;

6.}

7.}

很簡單鸽粉,一個原型類,只需要實現(xiàn)Cloneable接口抓艳,覆寫clone方法触机,此處clone方法可以改成任意的名稱,因為Cloneable接口是個空接口玷或,你可以任意定義實現(xiàn)類的方法名儡首,如cloneA或者cloneB,因為此處的重點是super.clone()這句話偏友,super.clone()調(diào)用的是Object的clone()方法蔬胯,而在Object類中,clone()是native的位他,具體怎么實現(xiàn),我會在另一篇文章中,關(guān)于解讀Java中本地方法的調(diào)用傻盟,此處不再深究虽画。在這兒蝉绷,我將結(jié)合對象的淺復(fù)制和深復(fù)制來說一下,首先需要了解對象深、淺復(fù)制的概念:

淺復(fù)制:將一個對象復(fù)制后,基本數(shù)據(jù)類型的變量都會重新創(chuàng)建确徙,而引用類型,指向的還是原對象所指向的执桌。

深復(fù)制:將一個對象復(fù)制后鄙皇,不論是基本數(shù)據(jù)類型還有引用類型,都是重新創(chuàng)建的仰挣。簡單來說育苟,就是深復(fù)制進行了完全徹底的復(fù)制,而淺復(fù)制不徹底椎木。

此處,寫一個深淺復(fù)制的例子:

[java]view plaincopy

1.publicclassPrototypeimplementsCloneable,?Serializable?{

2.

3.privatestaticfinallongserialVersionUID?=?1L;

4.privateString?string;

5.

6.privateSerializableObject?obj;

7.

8./*淺復(fù)制*/

9.publicObject?clone()throwsCloneNotSupportedException?{

10.Prototype?proto?=?(Prototype)super.clone();

11.returnproto;

12.}

13.

14./*深復(fù)制*/

15.publicObject?deepClone()throwsIOException,?ClassNotFoundException?{

16.

17./*寫入當前對象的二進制流*/

18.ByteArrayOutputStream?bos?=newByteArrayOutputStream();

19.ObjectOutputStream?oos?=newObjectOutputStream(bos);

20.oos.writeObject(this);

21.

22./*讀出二進制流產(chǎn)生的新對象*/

23.ByteArrayInputStream?bis?=newByteArrayInputStream(bos.toByteArray());

24.ObjectInputStream?ois?=newObjectInputStream(bis);

25.returnois.readObject();

26.}

27.

28.publicString?getString()?{

29.returnstring;

30.}

31.

32.publicvoidsetString(String?string)?{

33.this.string?=?string;

34.}

35.

36.publicSerializableObject?getObj()?{

37.returnobj;

38.}

39.

40.publicvoidsetObj(SerializableObject?obj)?{

41.this.obj?=?obj;

42.}

43.

44.}

45.

46.classSerializableObjectimplementsSerializable?{

47.privatestaticfinallongserialVersionUID?=?1L;

48.}

要實現(xiàn)深復(fù)制博烂,需要采用流的形式讀入當前對象的二進制輸入香椎,再寫出二進制數(shù)據(jù)對應(yīng)的對象。

B禽篱、結(jié)構(gòu)模式(7種)

我們接著討論設(shè)計模式畜伐,上篇文章我講完了5種創(chuàng)建型模式,這章開始躺率,我將講下7種結(jié)構(gòu)型模式:適配器模式玛界、裝飾模式、代理模式悼吱、外觀模式慎框、橋接模式、組合模式后添、享元模式笨枯。其中對象的適配器模式是各種模式的起源,我們看下面的圖:

6遇西、適配器模式

適配器模式將某個類的接口轉(zhuǎn)換成客戶端期望的另一個接口表示馅精,目的是消除由于接口不匹配所造成的類的兼容性問題。主要分為三類:類的適配器模式粱檀、對象的適配器模式洲敢、接口的適配器模式。

01茄蚯、類的適配器模式

核心思想就是:有一個Source類压彭,擁有一個方法睦优,待適配,目標接口是Targetable哮塞,通過Adapter類刨秆,將Source的功能擴展到Targetable里,看代碼:

[java]view plaincopy

1.publicclassSource?{

2.

3.publicvoidmethod1()?{

4.System.out.println("this?is?original?method!");

5.}

6.}

[java]view plaincopy

1.publicinterfaceTargetable?{

2.

3./*與原類中的方法相同*/

4.publicvoidmethod1();

5.

6./*新類的方法*/

7.publicvoidmethod2();

8.}

[java]view plaincopy

1.publicclassAdapterextendsSourceimplementsTargetable?{

2.

3.@Override

4.publicvoidmethod2()?{

5.System.out.println("this?is?the?targetable?method!");

6.}

7.}

Adapter類繼承Source類忆畅,實現(xiàn)Targetable接口衡未,下面是測試類:

[java]view plaincopy

1.publicclassAdapterTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Targetable?target?=newAdapter();

5.target.method1();

6.target.method2();

7.}

8.}

輸出:

this is original method!

this is the targetable method!

這樣Targetable接口的實現(xiàn)類就具有了Source類的功能。

02家凯、對象的適配器模式

基本思路和類的適配器模式相同缓醋,只是將Adapter類作修改,這次不繼承Source類绊诲,而是持有Source類的實例送粱,以達到解決兼容性的問題〉嘀看圖:

只需要修改Adapter類的源碼即可:

[java]view plaincopy

1.publicclassWrapperimplementsTargetable?{

2.

3.privateSource?source;

4.

5.publicWrapper(Source?source){

6.super();

7.this.source?=?source;

8.}

9.@Override

10.publicvoidmethod2()?{

11.System.out.println("this?is?the?targetable?method!");

12.}

13.

14.@Override

15.publicvoidmethod1()?{

16.source.method1();

17.}

18.}

測試類:

[java]view plaincopy

1.publicclassAdapterTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Source?source?=newSource();

5.Targetable?target?=newWrapper(source);

6.target.method1();

7.target.method2();

8.}

9.}

輸出與第一種一樣抗俄,只是適配的方法不同而已。

03世舰、接口的適配器模式

第三種適配器模式是接口的適配器模式动雹,接口的適配器是這樣的:有時我們寫的一個接口中有多個抽象方法,當我們寫該接口的實現(xiàn)類時跟压,必須實現(xiàn)該接口的所有方法胰蝠,這明顯有時比較浪費,因為并不是所有的方法都是我們需要的震蒋,有時只需要某一些茸塞,此處為了解決這個問題,我們引入了接口的適配器模式查剖,借助于一個抽象類钾虐,該抽象類實現(xiàn)了該接口,實現(xiàn)了所有的方法笋庄,而我們不和原始的接口打交道禾唁,只和該抽象類取得聯(lián)系,所以我們寫一個類无切,繼承該抽象類荡短,重寫我們需要的方法就行《呒看一下類圖:

這個很好理解掘托,在實際開發(fā)中,我們也常會遇到這種接口中定義了太多的方法籍嘹,以致于有時我們在一些實現(xiàn)類中并不是都需要闪盔⊥湓海看代碼:

[java]view plaincopy

1.publicinterfaceSourceable?{

2.

3.publicvoidmethod1();

4.publicvoidmethod2();

5.}

抽象類Wrapper2:

[java]view plaincopy

1.publicabstractclassWrapper2implementsSourceable{

2.

3.publicvoidmethod1(){}

4.publicvoidmethod2(){}

5.}

[java]view plaincopy

1.publicclassSourceSub1extendsWrapper2?{

2.publicvoidmethod1(){

3.System.out.println("the?sourceable?interface's?first?Sub1!");

4.}

5.}

[java]view plaincopy

1.publicclassSourceSub2extendsWrapper2?{

2.publicvoidmethod2(){

3.System.out.println("the?sourceable?interface's?second?Sub2!");

4.}

5.}

[java]view plaincopy

1.publicclassWrapperTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Sourceable?source1?=newSourceSub1();

5.Sourceable?source2?=newSourceSub2();

6.

7.source1.method1();

8.source1.method2();

9.source2.method1();

10.source2.method2();

11.}

12.}

測試輸出:

the sourceable interface's first Sub1!

the sourceable interface's second Sub2!

達到了我們的效果!

講了這么多泪掀,總結(jié)一下三種適配器模式的應(yīng)用場景:

類的適配器模式:當希望將一個類轉(zhuǎn)換成滿足另一個新接口的類時听绳,可以使用類的適配器模式,創(chuàng)建一個新類异赫,繼承原有的類椅挣,實現(xiàn)新的接口即可。

對象的適配器模式:當希望將一個對象轉(zhuǎn)換成滿足另一個新接口的對象時塔拳,可以創(chuàng)建一個Wrapper類鼠证,持有原類的一個實例,在Wrapper類的方法中靠抑,調(diào)用實例的方法就行量九。

接口的適配器模式:當不希望實現(xiàn)一個接口中所有的方法時,可以創(chuàng)建一個抽象類Wrapper颂碧,實現(xiàn)所有方法荠列,我們寫別的類的時候,繼承抽象類即可载城。

7弯予、裝飾模式(Decorator

顧名思義,裝飾模式就是給一個對象增加一些新的功能个曙,而且是動態(tài)的,要求裝飾對象和被裝飾對象實現(xiàn)同一個接口受楼,裝飾對象持有被裝飾對象的實例垦搬,關(guān)系圖如下:

Source類是被裝飾類,Decorator類是一個裝飾類艳汽,可以為Source類動態(tài)的添加一些功能猴贰,代碼如下:

[java]view plaincopy

1.publicinterfaceSourceable?{

2.publicvoidmethod();

3.}

[java]view plaincopy

1.publicclassSourceimplementsSourceable?{

2.

3.@Override

4.publicvoidmethod()?{

5.System.out.println("the?original?method!");

6.}

7.}

[java]view plaincopy

1.publicclassDecoratorimplementsSourceable?{

2.

3.privateSourceable?source;

4.

5.publicDecorator(Sourceable?source){

6.super();

7.this.source?=?source;

8.}

9.@Override

10.publicvoidmethod()?{

11.System.out.println("before?decorator!");

12.source.method();

13.System.out.println("after?decorator!");

14.}

15.}

測試類:

[java]view plaincopy

1.publicclassDecoratorTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Sourceable?source?=newSource();

5.Sourceable?obj?=newDecorator(source);

6.obj.method();

7.}

8.}

輸出:

before decorator!

the original method!

after decorator!

裝飾器模式的應(yīng)用場景:

1、需要擴展一個類的功能河狐。

2米绕、動態(tài)的為一個對象增加功能,而且還能動態(tài)撤銷馋艺。(繼承不能做到這一點栅干,繼承的功能是靜態(tài)的,不能動態(tài)增刪捐祠。)

缺點:產(chǎn)生過多相似的對象碱鳞,不易排錯!

8踱蛀、代理模式(Proxy

其實每個模式名稱就表明了該模式的作用窿给,代理模式就是多一個代理類出來贵白,替原對象進行一些操作,比如我們在租房子的時候回去找中介崩泡,為什么呢禁荒?因為你對該地區(qū)房屋的信息掌握的不夠全面,希望找一個更熟悉的人去幫你做角撞,此處的代理就是這個意思呛伴。再如我們有的時候打官司,我們需要請律師靴寂,因為律師在法律方面有專長磷蜀,可以替我們進行操作,表達我們的想法百炬。先來看看關(guān)系圖:

根據(jù)上文的闡述褐隆,代理模式就比較容易的理解了,我們看下代碼:

[java]view plaincopy

1.publicinterfaceSourceable?{

2.publicvoidmethod();

3.}

[java]view plaincopy

1.publicclassSourceimplementsSourceable?{

2.

3.@Override

4.publicvoidmethod()?{

5.System.out.println("the?original?method!");

6.}

7.}

[java]view plaincopy

1.publicclassProxyimplementsSourceable?{

2.

3.privateSource?source;

4.publicProxy(){

5.super();

6.this.source?=newSource();

7.}

8.@Override

9.publicvoidmethod()?{

10.before();

11.source.method();

12.atfer();

13.}

14.privatevoidatfer()?{

15.System.out.println("after?proxy!");

16.}

17.privatevoidbefore()?{

18.System.out.println("before?proxy!");

19.}

20.}

測試類:

[java]view plaincopy

1.publicclassProxyTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Sourceable?source?=newProxy();

5.source.method();

6.}

7.

8.}

輸出:

before proxy!

the original method!

after proxy!

代理模式的應(yīng)用場景:

如果已有的方法在使用的時候需要對原有的方法進行改進剖踊,此時有兩種辦法:

1庶弃、修改原有的方法來適應(yīng)。這樣違反了“對擴展開放德澈,對修改關(guān)閉”的原則歇攻。

2、就是采用一個代理類調(diào)用原有的方法梆造,且對產(chǎn)生的結(jié)果進行控制缴守。這種方法就是代理模式。

使用代理模式镇辉,可以將功能劃分的更加清晰屡穗,有助于后期維護!

9忽肛、外觀模式(Facade

外觀模式是為了解決類與類之家的依賴關(guān)系的村砂,像spring一樣,可以將類和類之間的關(guān)系配置到配置文件中屹逛,而外觀模式就是將他們的關(guān)系放在一個Facade類中础废,降低了類類之間的耦合度,該模式中沒有涉及到接口罕模,看下類圖:(我們以一個計算機的啟動過程為例)

我們先看下實現(xiàn)類:

[java]view plaincopy

1.publicclassCPU?{

2.

3.publicvoidstartup(){

4.System.out.println("cpu?startup!");

5.}

6.

7.publicvoidshutdown(){

8.System.out.println("cpu?shutdown!");

9.}

10.}

[java]view plaincopy

1.publicclassMemory?{

2.

3.publicvoidstartup(){

4.System.out.println("memory?startup!");

5.}

6.

7.publicvoidshutdown(){

8.System.out.println("memory?shutdown!");

9.}

10.}

[java]view plaincopy

1.publicclassDisk?{

2.

3.publicvoidstartup(){

4.System.out.println("disk?startup!");

5.}

6.

7.publicvoidshutdown(){

8.System.out.println("disk?shutdown!");

9.}

10.}

[java]view plaincopy

1.publicclassComputer?{

2.privateCPU?cpu;

3.privateMemory?memory;

4.privateDisk?disk;

5.

6.publicComputer(){

7.cpu?=newCPU();

8.memory?=newMemory();

9.disk?=newDisk();

10.}

11.

12.publicvoidstartup(){

13.System.out.println("start?the?computer!");

14.cpu.startup();

15.memory.startup();

16.disk.startup();

17.System.out.println("start?computer?finished!");

18.}

19.

20.publicvoidshutdown(){

21.System.out.println("begin?to?close?the?computer!");

22.cpu.shutdown();

23.memory.shutdown();

24.disk.shutdown();

25.System.out.println("computer?closed!");

26.}

27.}

User類如下:

[java]view plaincopy

1.publicclassUser?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Computer?computer?=newComputer();

5.computer.startup();

6.computer.shutdown();

7.}

8.}

輸出:

start the computer!

cpu startup!

memory startup!

disk startup!

start computer finished!

begin to close the computer!

cpu shutdown!

memory shutdown!

disk shutdown!

computer closed!

如果我們沒有Computer類评腺,那么,CPU淑掌、Memory歇僧、Disk他們之間將會相互持有實例,產(chǎn)生關(guān)系,這樣會造成嚴重的依賴诈悍,修改一個類祸轮,可能會帶來其他類的修改,這不是我們想要看到的侥钳,有了Computer類适袜,他們之間的關(guān)系被放在了Computer類里,這樣就起到了解耦的作用舷夺,這苦酱,就是外觀模式!

10给猾、橋接模式(Bridge

橋接模式就是把事物和其具體實現(xiàn)分開疫萤,使他們可以各自獨立的變化。橋接的用意是:將抽象化與實現(xiàn)化解耦敢伸,使得二者可以獨立變化扯饶,像我們常用的JDBC橋DriverManager一樣,JDBC進行連接數(shù)據(jù)庫的時候池颈,在各個數(shù)據(jù)庫之間進行切換尾序,基本不需要動太多的代碼,甚至絲毫不用動躯砰,原因就是JDBC提供統(tǒng)一接口每币,每個數(shù)據(jù)庫提供各自的實現(xiàn),用一個叫做數(shù)據(jù)庫驅(qū)動的程序來橋接就行了琢歇。我們來看看關(guān)系圖:

實現(xiàn)代碼:

先定義接口:

[java]view plaincopy

1.publicinterfaceSourceable?{

2.publicvoidmethod();

3.}

分別定義兩個實現(xiàn)類:

[java]view plaincopy

1.publicclassSourceSub1implementsSourceable?{

2.

3.@Override

4.publicvoidmethod()?{

5.System.out.println("this?is?the?first?sub!");

6.}

7.}

[java]view plaincopy

1.publicclassSourceSub2implementsSourceable?{

2.

3.@Override

4.publicvoidmethod()?{

5.System.out.println("this?is?the?second?sub!");

6.}

7.}

定義一個橋兰怠,持有Sourceable的一個實例:

[java]view plaincopy

1.publicabstractclassBridge?{

2.privateSourceable?source;

3.

4.publicvoidmethod(){

5.source.method();

6.}

7.

8.publicSourceable?getSource()?{

9.returnsource;

10.}

11.

12.publicvoidsetSource(Sourceable?source)?{

13.this.source?=?source;

14.}

15.}

[java]view plaincopy

1.publicclassMyBridgeextendsBridge?{

2.publicvoidmethod(){

3.getSource().method();

4.}

5.}

測試類:

[java]view plaincopy

1.publicclassBridgeTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.

5.Bridge?bridge?=newMyBridge();

6.

7./*調(diào)用第一個對象*/

8.Sourceable?source1?=newSourceSub1();

9.bridge.setSource(source1);

10.bridge.method();

11.

12./*調(diào)用第二個對象*/

13.Sourceable?source2?=newSourceSub2();

14.bridge.setSource(source2);

15.bridge.method();

16.}

17.}

output:

this is the first sub!

this is the second sub!

這樣,就通過對Bridge類的調(diào)用李茫,實現(xiàn)了對接口Sourceable的實現(xiàn)類SourceSub1和SourceSub2的調(diào)用揭保。接下來我再畫個圖,大家就應(yīng)該明白了涌矢,因為這個圖是我們JDBC連接的原理,有數(shù)據(jù)庫學(xué)習(xí)基礎(chǔ)的快骗,一結(jié)合就都懂了娜庇。

11、組合模式(Composite

組合模式有時又叫部分-整體模式在處理類似樹形結(jié)構(gòu)的問題時比較方便方篮,看看關(guān)系圖:

直接來看代碼:

[java]view plaincopy

1.publicclassTreeNode?{

2.

3.privateString?name;

4.privateTreeNode?parent;

5.privateVector?children?=newVector();

6.

7.publicTreeNode(String?name){

8.this.name?=?name;

9.}

10.

11.publicString?getName()?{

12.returnname;

13.}

14.

15.publicvoidsetName(String?name)?{

16.this.name?=?name;

17.}

18.

19.publicTreeNode?getParent()?{

20.returnparent;

21.}

22.

23.publicvoidsetParent(TreeNode?parent)?{

24.this.parent?=?parent;

25.}

26.

27.//添加孩子節(jié)點

28.publicvoidadd(TreeNode?node){

29.children.add(node);

30.}

31.

32.//刪除孩子節(jié)點

33.publicvoidremove(TreeNode?node){

34.children.remove(node);

35.}

36.

37.//取得孩子節(jié)點

38.publicEnumeration?getChildren(){

39.returnchildren.elements();

40.}

41.}

[java]view plaincopy

1.publicclassTree?{

2.

3.TreeNode?root?=null;

4.

5.publicTree(String?name)?{

6.root?=newTreeNode(name);

7.}

8.

9.publicstaticvoidmain(String[]?args)?{

10.Tree?tree?=newTree("A");

11.TreeNode?nodeB?=newTreeNode("B");

12.TreeNode?nodeC?=newTreeNode("C");

13.

14.nodeB.add(nodeC);

15.tree.root.add(nodeB);

16.System.out.println("build?the?tree?finished!");

17.}

18.}

使用場景:將多個對象組合在一起進行操作名秀,常用于表示樹形結(jié)構(gòu)中,例如二叉樹藕溅,數(shù)等匕得。

12、享元模式(Flyweight

享元模式的主要目的是實現(xiàn)對象的共享,即共享池汁掠,當系統(tǒng)中對象多的時候可以減少內(nèi)存的開銷略吨,通常與工廠模式一起使用。

FlyWeightFactory負責(zé)創(chuàng)建和管理享元單元考阱,當一個客戶端請求時翠忠,工廠需要檢查當前對象池中是否有符合條件的對象,如果有乞榨,就返回已經(jīng)存在的對象秽之,如果沒有,則創(chuàng)建一個新對象吃既,F(xiàn)lyWeight是超類考榨。一提到共享池,我們很容易聯(lián)想到Java里面的JDBC連接池鹦倚,想想每個連接的特點河质,我們不難總結(jié)出:適用于作共享的一些個對象,他們有一些共有的屬性申鱼,就拿數(shù)據(jù)庫連接池來說愤诱,url、driverClassName捐友、username淫半、password及dbname,這些屬性對于每個連接來說都是一樣的匣砖,所以就適合用享元模式來處理科吭,建一個工廠類,將上述類似屬性作為內(nèi)部數(shù)據(jù)猴鲫,其它的作為外部數(shù)據(jù)对人,在方法調(diào)用時,當做參數(shù)傳進來拂共,這樣就節(jié)省了空間牺弄,減少了實例的數(shù)量。

看個例子:

看下數(shù)據(jù)庫連接池的代碼:

[java]view plaincopy

1.publicclassConnectionPool?{

2.

3.privateVector?pool;

4.

5./*公有屬性*/

6.privateString?url?="jdbc:mysql://localhost:3306/test";

7.privateString?username?="root";

8.privateString?password?="root";

9.privateString?driverClassName?="com.mysql.jdbc.Driver";

10.

11.privateintpoolSize?=100;

12.privatestaticConnectionPool?instance?=null;

13.Connection?conn?=null;

14.

15./*構(gòu)造方法宜狐,做一些初始化工作*/

16.privateConnectionPool()?{

17.pool?=newVector(poolSize);

18.

19.for(inti?=0;?i?<?poolSize;?i++)?{

20.try{

21.Class.forName(driverClassName);

22.conn?=?DriverManager.getConnection(url,?username,?password);

23.pool.add(conn);

24.}catch(ClassNotFoundException?e)?{

25.e.printStackTrace();

26.}catch(SQLException?e)?{

27.e.printStackTrace();

28.}

29.}

30.}

31.

32./*返回連接到連接池*/

33.publicsynchronizedvoidrelease()?{

34.pool.add(conn);

35.}

36.

37./*返回連接池中的一個數(shù)據(jù)庫連接*/

38.publicsynchronizedConnection?getConnection()?{

39.if(pool.size()?>0)?{

40.Connection?conn?=?pool.get(0);

41.pool.remove(conn);

42.returnconn;

43.}else{

44.returnnull;

45.}

46.}

47.}

通過連接池的管理势告,實現(xiàn)了數(shù)據(jù)庫連接的共享,不需要每一次都重新創(chuàng)建連接抚恒,節(jié)省了數(shù)據(jù)庫重新創(chuàng)建的開銷咱台,提升了系統(tǒng)的性能!

C俭驮、關(guān)系模式(11種)

先來張圖俘闯,看看這11中模式的關(guān)系:

第一類:通過父類與子類的關(guān)系進行實現(xiàn)。

第二類:兩個類之間居触。

第三類:類的狀態(tài)。

第四類:通過中間類

父類與子類關(guān)系

13萍恕、策略模式(strategy

策略模式定義了一系列算法,并將每個算法封裝起來瓮恭,使他們可以相互替換雄坪,且算法的變化不會影響到使用算法的客戶。需要設(shè)計一個接口屯蹦,為一系列實現(xiàn)類提供統(tǒng)一的方法维哈,多個實現(xiàn)類實現(xiàn)該接口,設(shè)計一個抽象類(可有可無登澜,屬于輔助類)阔挠,提供輔助函數(shù),關(guān)系圖如下:

圖中ICalculator提供同意的方法脑蠕,

AbstractCalculator是輔助類购撼,提供輔助方法,接下來谴仙,依次實現(xiàn)下每個類:

首先統(tǒng)一接口:

[java]view plaincopy

1.publicinterfaceICalculator?{

2.publicintcalculate(String?exp);

3.}

輔助類:

[java]view plaincopy

1.publicabstractclassAbstractCalculator?{

2.

3.publicint[]?split(String?exp,String?opt){

4.String?array[]?=?exp.split(opt);

5.intarrayInt[]?=newint[2];

6.arrayInt[0]?=?Integer.parseInt(array[0]);

7.arrayInt[1]?=?Integer.parseInt(array[1]);

8.returnarrayInt;

9.}

10.}

三個實現(xiàn)類:

[java]view plaincopy

1.publicclassPlusextendsAbstractCalculatorimplementsICalculator?{

2.

3.@Override

4.publicintcalculate(String?exp)?{

5.intarrayInt[]?=?split(exp,"\\+");

6.returnarrayInt[0]+arrayInt[1];

7.}

8.}

[java]view plaincopy

1.publicclassMinusextendsAbstractCalculatorimplementsICalculator?{

2.

3.@Override

4.publicintcalculate(String?exp)?{

5.intarrayInt[]?=?split(exp,"-");

6.returnarrayInt[0]-arrayInt[1];

7.}

8.

9.}

[java]view plaincopy

1.publicclassMultiplyextendsAbstractCalculatorimplementsICalculator?{

2.

3.@Override

4.publicintcalculate(String?exp)?{

5.intarrayInt[]?=?split(exp,"\\*");

6.returnarrayInt[0]*arrayInt[1];

7.}

8.}

簡單的測試類:

[java]view plaincopy

1.publicclassStrategyTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.String?exp?="2+8";

5.ICalculator?cal?=newPlus();

6.intresult?=?cal.calculate(exp);

7.System.out.println(result);

8.}

9.}

輸出:10

策略模式的決定權(quán)在用戶迂求,系統(tǒng)本身提供不同算法的實現(xiàn),新增或者刪除算法晃跺,對各種算法做封裝揩局。因此,策略模式多用在算法決策系統(tǒng)中掀虎,外部用戶只需要決定用哪個算法即可凌盯。

14、模板方法模式(Template

Method

解釋一下模板方法模式烹玉,就是指:一個抽象類中驰怎,有一個主方法,再定義1...n個方法二打,可以是抽象的县忌,也可以是實際的方法,定義一個類继效,繼承該抽象類症杏,重寫抽象方法,通過調(diào)用抽象類莲趣,實現(xiàn)對子類的調(diào)用鸳慈,先看個關(guān)系圖:

就是在AbstractCalculator類中定義一個主方法calculate饱溢,calculate()調(diào)用spilt()等喧伞,Plus和Minus分別繼承AbstractCalculator類,通過對AbstractCalculator的調(diào)用實現(xiàn)對子類的調(diào)用,看下面的例子:

[java]view plaincopy

1.publicabstractclassAbstractCalculator?{

2.

3./*主方法潘鲫,實現(xiàn)對本類其它方法的調(diào)用*/

4.publicfinalintcalculate(String?exp,String?opt){

5.intarray[]?=?split(exp,opt);

6.returncalculate(array[0],array[1]);

7.}

8.

9./*被子類重寫的方法*/

10.abstractpublicintcalculate(intnum1,intnum2);

11.

12.publicint[]?split(String?exp,String?opt){

13.String?array[]?=?exp.split(opt);

14.intarrayInt[]?=newint[2];

15.arrayInt[0]?=?Integer.parseInt(array[0]);

16.arrayInt[1]?=?Integer.parseInt(array[1]);

17.returnarrayInt;

18.}

19.}

[java]view plaincopy

1.publicclassPlusextendsAbstractCalculator?{

2.

3.@Override

4.publicintcalculate(intnum1,intnum2)?{

5.returnnum1?+?num2;

6.}

7.}

測試類:

[java]view plaincopy

1.publicclassStrategyTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.String?exp?="8+8";

5.AbstractCalculator?cal?=newPlus();

6.intresult?=?cal.calculate(exp,"\\+");

7.System.out.println(result);

8.}

9.}

我跟蹤下這個小程序的執(zhí)行過程:首先將exp和"\\+"做參數(shù)翁逞,調(diào)用AbstractCalculator類里的calculate(String,String)方法,在calculate(String,String)里調(diào)用同類的split()溉仑,之后再調(diào)用calculate(int

,int)方法挖函,從這個方法進入到子類中,執(zhí)行完return num1 + num2后浊竟,將值返回到AbstractCalculator類怨喘,賦給result,打印出來振定。正好驗證了我們開頭的思路必怜。

類之間的關(guān)系

15、觀察者模式(Observer

包括這個模式在內(nèi)的接下來的四個模式后频,都是類和類之間的關(guān)系梳庆,不涉及到繼承,學(xué)的時候應(yīng)該記得歸納卑惜,記得本文最開始的那個圖膏执。觀察者模式很好理解,類似于郵件訂閱和RSS訂閱露久,當我們?yōu)g覽一些博客或wiki時更米,經(jīng)常會看到RSS圖標,就這的意思是抱环,當你訂閱了該文章壳快,如果后續(xù)有更新,會及時通知你镇草。其實眶痰,簡單來講就一句話:當一個對象變化時,其它依賴該對象的對象都會收到通知梯啤,并且隨著變化竖伯!對象之間是一種一對多的關(guān)系。先來看看關(guān)系圖:

我解釋下這些類的作用:MySubject類就是我們的主對象因宇,Observer1和Observer2是依賴于MySubject的對象七婴,當MySubject變化時,Observer1和Observer2必然變化察滑。AbstractSubject類中定義著需要監(jiān)控的對象列表打厘,可以對其進行修改:增加或刪除被監(jiān)控對象,且當MySubject變化時贺辰,負責(zé)通知在列表內(nèi)存在的對象户盯。我們看實現(xiàn)代碼:

一個Observer接口:

[java]view plaincopy

1.publicinterfaceObserver?{

2.publicvoidupdate();

3.}

兩個實現(xiàn)類:

[java]view plaincopy

1.publicclassObserver1implementsObserver?{

2.

3.@Override

4.publicvoidupdate()?{

5.System.out.println("observer1?has?received!");

6.}

7.}

[java]view plaincopy

1.publicclassObserver2implementsObserver?{

2.

3.@Override

4.publicvoidupdate()?{

5.System.out.println("observer2?has?received!");

6.}

7.

8.}

Subject接口及實現(xiàn)類:

[java]view plaincopy

1.publicinterfaceSubject?{

2.

3./*增加觀察者*/

4.publicvoidadd(Observer?observer);

5.

6./*刪除觀察者*/

7.publicvoiddel(Observer?observer);

8.

9./*通知所有的觀察者*/

10.publicvoidnotifyObservers();

11.

12./*自身的操作*/

13.publicvoidoperation();

14.}

[java]view plaincopy

1.publicabstractclassAbstractSubjectimplementsSubject?{

2.

3.privateVector?vector?=newVector();

4.@Override

5.publicvoidadd(Observer?observer)?{

6.vector.add(observer);

7.}

8.

9.@Override

10.publicvoiddel(Observer?observer)?{

11.vector.remove(observer);

12.}

13.

14.@Override

15.publicvoidnotifyObservers()?{

16.Enumeration?enumo?=?vector.elements();

17.while(enumo.hasMoreElements()){

18.enumo.nextElement().update();

19.}

20.}

21.}

[java]view plaincopy

1.publicclassMySubjectextendsAbstractSubject?{

2.

3.@Override

4.publicvoidoperation()?{

5.System.out.println("update?self!");

6.notifyObservers();

7.}

8.

9.}

測試類:

[java]view plaincopy

1.publicclassObserverTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Subject?sub?=newMySubject();

5.sub.add(newObserver1());

6.sub.add(newObserver2());

7.

8.sub.operation();

9.}

10.

11.}

輸出:

update self!

observer1 has received!

observer2 has received!

這些東西嵌施,其實不難,只是有些抽象莽鸭,不太容易整體理解吗伤,建議讀者:根據(jù)關(guān)系圖,新建項目硫眨,自己寫代碼(或者參考我的代碼),按照總體思路走一遍足淆,這樣才能體會它的思想,理解起來容易礁阁!

16巧号、迭代子模式(Iterator

顧名思義,迭代器模式就是順序訪問聚集中的對象姥闭,一般來說裂逐,集合中非常常見,如果對集合類比較熟悉的話泣栈,理解本模式會十分輕松卜高。這句話包含兩層意思:一是需要遍歷的對象,即聚集對象南片,二是迭代器對象掺涛,用于對聚集對象進行遍歷訪問。我們看下關(guān)系圖:

這個思路和我們常用的一模一樣疼进,MyCollection中定義了集合的一些操作薪缆,MyIterator中定義了一系列迭代操作,且持有Collection實例伞广,我們來看看實現(xiàn)代碼:

兩個接口:

[java]view plaincopy

1.publicinterfaceCollection?{

2.

3.publicIterator?iterator();

4.

5./*取得集合元素*/

6.publicObject?get(inti);

7.

8./*取得集合大小*/

9.publicintsize();

10.}

[java]view plaincopy

1.publicinterfaceIterator?{

2.//前移

3.publicObject?previous();

4.

5.//后移

6.publicObject?next();

7.publicbooleanhasNext();

8.

9.//取得第一個元素

10.publicObject?first();

11.}

兩個實現(xiàn):

[java]view plaincopy

1.publicclassMyCollectionimplementsCollection?{

2.

3.publicString?string[]?=?{"A","B","C","D","E"};

4.@Override

5.publicIterator?iterator()?{

6.returnnewMyIterator(this);

7.}

8.

9.@Override

10.publicObject?get(inti)?{

11.returnstring[i];

12.}

13.

14.@Override

15.publicintsize()?{

16.returnstring.length;

17.}

18.}

[java]view plaincopy

1.publicclassMyIteratorimplementsIterator?{

2.

3.privateCollection?collection;

4.privateintpos?=?-1;

5.

6.publicMyIterator(Collection?collection){

7.this.collection?=?collection;

8.}

9.

10.@Override

11.publicObject?previous()?{

12.if(pos?>0){

13.pos--;

14.}

15.returncollection.get(pos);

16.}

17.

18.@Override

19.publicObject?next()?{

20.if(pos

21.pos++;

22.}

23.returncollection.get(pos);

24.}

25.

26.@Override

27.publicbooleanhasNext()?{

28.if(pos

29.returntrue;

30.}else{

31.returnfalse;

32.}

33.}

34.

35.@Override

36.publicObject?first()?{

37.pos?=0;

38.returncollection.get(pos);

39.}

40.

41.}

測試類:

[java]view plaincopy

1.publicclassTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Collection?collection?=newMyCollection();

5.Iterator?it?=?collection.iterator();

6.

7.while(it.hasNext()){

8.System.out.println(it.next());

9.}

10.}

11.}

輸出:A B C D E

此處我們貌似模擬了一個集合類的過程拣帽,感覺是不是很爽?其實JDK中各個類也都是這些基本的東西嚼锄,加一些設(shè)計模式减拭,再加一些優(yōu)化放到一起的,只要我們把這些東西學(xué)會了区丑,掌握好了拧粪,我們也可以寫出自己的集合類,甚至框架沧侥!

17可霎、責(zé)任鏈模式(Chain of

Responsibility

接下來我們將要談?wù)勜?zé)任鏈模式,有多個對象宴杀,每個對象持有對下一個對象的引用癣朗,這樣就會形成一條鏈,請求在這條鏈上傳遞旺罢,直到某一對象決定處理該請求旷余。但是發(fā)出者并不清楚到底最終那個對象會處理該請求盾致,所以,責(zé)任鏈模式可以實現(xiàn)荣暮,在隱瞞客戶端的情況下,對系統(tǒng)進行動態(tài)的調(diào)整罩驻。先看看關(guān)系圖:

Abstracthandler類提供了get和set方法穗酥,方便MyHandle類設(shè)置和修改引用對象,MyHandle類是核心惠遏,實例化后生成一系列相互持有的對象砾跃,構(gòu)成一條鏈。

[java]view plaincopy

1.publicinterfaceHandler?{

2.publicvoidoperator();

3.}

[java]view plaincopy

1.publicabstractclassAbstractHandler?{

2.

3.privateHandler?handler;

4.

5.publicHandler?getHandler()?{

6.returnhandler;

7.}

8.

9.publicvoidsetHandler(Handler?handler)?{

10.this.handler?=?handler;

11.}

12.

13.}

[java]view plaincopy

1.publicclassMyHandlerextendsAbstractHandlerimplementsHandler?{

2.

3.privateString?name;

4.

5.publicMyHandler(String?name)?{

6.this.name?=?name;

7.}

8.

9.@Override

10.publicvoidoperator()?{

11.System.out.println(name+"deal!");

12.if(getHandler()!=null){

13.getHandler().operator();

14.}

15.}

16.}

[java]view plaincopy

1.publicclassTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.MyHandler?h1?=newMyHandler("h1");

5.MyHandler?h2?=newMyHandler("h2");

6.MyHandler?h3?=newMyHandler("h3");

7.

8.h1.setHandler(h2);

9.h2.setHandler(h3);

10.

11.h1.operator();

12.}

13.}

輸出:

h1deal!

h2deal!

h3deal!

此處強調(diào)一點就是节吮,鏈接上的請求可以是一條鏈抽高,可以是一個樹,還可以是一個環(huán)透绩,模式本身不約束這個翘骂,需要我們自己去實現(xiàn),同時帚豪,在一個時刻碳竟,命令只允許由一個對象傳給另一個對象,而不允許傳給多個對象狸臣。

18莹桅、命令模式(Command

命令模式很好理解,舉個例子烛亦,司令員下令讓士兵去干件事情诈泼,從整個事情的角度來考慮,司令員的作用是煤禽,發(fā)出口令铐达,口令經(jīng)過傳遞,傳到了士兵耳朵里檬果,士兵去執(zhí)行娶桦。這個過程好在,三者相互解耦汁汗,任何一方都不用去依賴其他人衷畦,只需要做好自己的事兒就行,司令員要的是結(jié)果知牌,不會去關(guān)注到底士兵是怎么實現(xiàn)的祈争。我們看看關(guān)系圖:

Invoker是調(diào)用者(司令員),Receiver是被調(diào)用者(士兵)角寸,MyCommand是命令菩混,實現(xiàn)了Command接口忿墅,持有接收對象,看實現(xiàn)代碼:

[java]view plaincopy

1.publicinterfaceCommand?{

2.publicvoidexe();

3.}

[java]view plaincopy

1.publicclassMyCommandimplementsCommand?{

2.

3.privateReceiver?receiver;

4.

5.publicMyCommand(Receiver?receiver)?{

6.this.receiver?=?receiver;

7.}

8.

9.@Override

10.publicvoidexe()?{

11.receiver.action();

12.}

13.}

[java]view plaincopy

1.publicclassReceiver?{

2.publicvoidaction(){

3.System.out.println("command?received!");

4.}

5.}

[java]view plaincopy

1.publicclassInvoker?{

2.

3.privateCommand?command;

4.

5.publicInvoker(Command?command)?{

6.this.command?=?command;

7.}

8.

9.publicvoidaction(){

10.command.exe();

11.}

12.}

[java]view plaincopy

1.publicclassTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Receiver?receiver?=newReceiver();

5.Command?cmd?=newMyCommand(receiver);

6.Invoker?invoker?=newInvoker(cmd);

7.invoker.action();

8.}

9.}

輸出:commandreceived!

這個很哈理解沮峡,命令模式的目的就是達到命令的發(fā)出者和執(zhí)行者之間解耦疚脐,實現(xiàn)請求和執(zhí)行分開,熟悉Struts的同學(xué)應(yīng)該知道邢疙,Struts其實就是一種將請求和呈現(xiàn)分離的技術(shù)棍弄,其中必然涉及命令模式的思想!

其實每個設(shè)計模式都是很重要的一種思想疟游,看上去很熟呼畸,其實是因為我們在學(xué)到的東西中都有涉及,盡管有時我們并不知道颁虐,其實在Java本身的設(shè)計之中處處都有體現(xiàn)蛮原,像AWT、JDBC另绩、集合類儒陨、IO管道或者是Web框架,里面設(shè)計模式無處不在笋籽。因為我們篇幅有限框全,很難講每一個設(shè)計模式都講的很詳細,不過我會盡我所能干签,盡量在有限的空間和篇幅內(nèi)津辩,把意思寫清楚了,更好讓大家明白容劳。本章不出意外的話喘沿,應(yīng)該是設(shè)計模式最后一講了,首先還是上一下上篇開頭的那個圖:

本章講講第三類和第四類竭贩。

類的狀態(tài)

19蚜印、備忘錄模式(Memento

主要目的是保存一個對象的某個狀態(tài),以便在適當?shù)臅r候恢復(fù)對象留量,個人覺得叫備份模式更形象些窄赋,通俗的講下:假設(shè)有原始類A,A中有各種屬性楼熄,A可以決定需要備份的屬性忆绰,備忘錄類B是用來存儲A的一些內(nèi)部狀態(tài),類C呢可岂,就是一個用來存儲備忘錄的错敢,且只能存儲,不能修改等操作缕粹。做個圖來分析一下:

Original類是原始類稚茅,里面有需要保存的屬性value及創(chuàng)建一個備忘錄類纸淮,用來保存value值。Memento類是備忘錄類亚享,Storage類是存儲備忘錄的類咽块,持有Memento類的實例,該模式很好理解欺税。直接看源碼:

[java]view plaincopy

1.publicclassOriginal?{

2.

3.privateString?value;

4.

5.publicString?getValue()?{

6.returnvalue;

7.}

8.

9.publicvoidsetValue(String?value)?{

10.this.value?=?value;

11.}

12.

13.publicOriginal(String?value)?{

14.this.value?=?value;

15.}

16.

17.publicMemento?createMemento(){

18.returnnewMemento(value);

19.}

20.

21.publicvoidrestoreMemento(Memento?memento){

22.this.value?=?memento.getValue();

23.}

24.}

[java]view plaincopy

1.publicclassMemento?{

2.

3.privateString?value;

4.

5.publicMemento(String?value)?{

6.this.value?=?value;

7.}

8.

9.publicString?getValue()?{

10.returnvalue;

11.}

12.

13.publicvoidsetValue(String?value)?{

14.this.value?=?value;

15.}

16.}

[java]view plaincopy

1.publicclassStorage?{

2.

3.privateMemento?memento;

4.

5.publicStorage(Memento?memento)?{

6.this.memento?=?memento;

7.}

8.

9.publicMemento?getMemento()?{

10.returnmemento;

11.}

12.

13.publicvoidsetMemento(Memento?memento)?{

14.this.memento?=?memento;

15.}

16.}

測試類:

[java]view plaincopy

1.publicclassTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.

5.//創(chuàng)建原始類

6.Original?origi?=newOriginal("egg");

7.

8.//創(chuàng)建備忘錄

9.Storage?storage?=newStorage(origi.createMemento());

10.

11.//修改原始類的狀態(tài)

12.System.out.println("初始化狀態(tài)為:"+?origi.getValue());

13.origi.setValue("niu");

14.System.out.println("修改后的狀態(tài)為:"+?origi.getValue());

15.

16.//回復(fù)原始類的狀態(tài)

17.origi.restoreMemento(storage.getMemento());

18.System.out.println("恢復(fù)后的狀態(tài)為:"+?origi.getValue());

19.}

20.}

輸出:

初始化狀態(tài)為:egg

修改后的狀態(tài)為:niu

恢復(fù)后的狀態(tài)為:egg

簡單描述下:新建原始類時侈沪,value被初始化為egg,后經(jīng)過修改魄衅,將value的值置為niu,最后倒數(shù)第二行進行恢復(fù)狀態(tài)塘辅,結(jié)果成功恢復(fù)了晃虫。其實我覺得這個模式叫“備份-恢復(fù)”模式最形象。

20扣墩、狀態(tài)模式(State

核心思想就是:當對象的狀態(tài)改變時哲银,同時改變其行為,很好理解呻惕!就拿QQ來說荆责,有幾種狀態(tài),在線亚脆、隱身做院、忙碌等,每個狀態(tài)對應(yīng)不同的操作濒持,而且你的好友也能看到你的狀態(tài)键耕,所以,狀態(tài)模式就兩點:1柑营、可以通過改變狀態(tài)來獲得不同的行為屈雄。2、你的好友能同時看到你的變化官套【颇蹋看圖:

State類是個狀態(tài)類,Context類可以實現(xiàn)切換奶赔,我們來看看代碼:

[java]view plaincopy

1.packagecom.xtfggef.dp.state;

2.

3./**

4.*狀態(tài)類的核心類

5.*?2012-12-1

6.*?@author?erqing

7.*

8.*/

9.publicclassState?{

10.

11.privateString?value;

12.

13.publicString?getValue()?{

14.returnvalue;

15.}

16.

17.publicvoidsetValue(String?value)?{

18.this.value?=?value;

19.}

20.

21.publicvoidmethod1(){

22.System.out.println("execute?the?first?opt!");

23.}

24.

25.publicvoidmethod2(){

26.System.out.println("execute?the?second?opt!");

27.}

28.}

[java]view plaincopy

1.packagecom.xtfggef.dp.state;

2.

3./**

4.*狀態(tài)模式的切換類2012-12-1

5.*?@author?erqing

6.*

7.*/

8.publicclassContext?{

9.

10.privateState?state;

11.

12.publicContext(State?state)?{

13.this.state?=?state;

14.}

15.

16.publicState?getState()?{

17.returnstate;

18.}

19.

20.publicvoidsetState(State?state)?{

21.this.state?=?state;

22.}

23.

24.publicvoidmethod()?{

25.if(state.getValue().equals("state1"))?{

26.state.method1();

27.}elseif(state.getValue().equals("state2"))?{

28.state.method2();

29.}

30.}

31.}

測試類:

[java]view plaincopy

1.publicclassTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.

5.State?state?=newState();

6.Context?context?=newContext(state);

7.

8.//設(shè)置第一種狀態(tài)

9.state.setValue("state1");

10.context.method();

11.

12.//設(shè)置第二種狀態(tài)

13.state.setValue("state2");

14.context.method();

15.}

16.}

輸出:

execute the first opt!

execute the second opt!

根據(jù)這個特性惋嚎,狀態(tài)模式在日常開發(fā)中用的挺多的,尤其是做網(wǎng)站的時候站刑,我們有時希望根據(jù)對象的某一屬性瘸彤,區(qū)別開他們的一些功能,比如說簡單的權(quán)限控制等笛钝。

通過中間類

21质况、訪問者模式(Visitor

訪問者模式把數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作解耦合愕宋,使得操作集合可相對自由地演化。訪問者模式適用于數(shù)據(jù)結(jié)構(gòu)相對穩(wěn)定算法又易變化的系統(tǒng)结榄。因為訪問者模式使得算法操作增加變得容易中贝。若系統(tǒng)數(shù)據(jù)結(jié)構(gòu)對象易于變化,經(jīng)常有新的數(shù)據(jù)對象增加進來臼朗,則不適合使用訪問者模式邻寿。訪問者模式的優(yōu)點是增加操作很容易,因為增加操作意味著增加新的訪問者视哑。訪問者模式將有關(guān)行為集中到一個訪問者對象中绣否,其改變不影響系統(tǒng)數(shù)據(jù)結(jié)構(gòu)。其缺點就是增加新的數(shù)據(jù)結(jié)構(gòu)很困難挡毅∷獯椋—— From百科

簡單來說,訪問者模式就是一種分離對象數(shù)據(jù)結(jié)構(gòu)與行為的方法跪呈,通過這種分離段磨,可達到為一個被訪問者動態(tài)添加新的操作而無需做其它的修改的效果。簡單關(guān)系圖:

來看看原碼:一個Visitor類耗绿,存放要訪問的對象苹支,

[java]view plaincopy

1.publicinterfaceVisitor?{

2.publicvoidvisit(Subject?sub);

3.}

[java]view plaincopy

1.publicclassMyVisitorimplementsVisitor?{

2.

3.@Override

4.publicvoidvisit(Subject?sub)?{

5.System.out.println("visit?the?subject:"+sub.getSubject());

6.}

7.}

Subject類,accept方法误阻,接受將要訪問它的對象债蜜,getSubject()獲取將要被訪問的屬性,

[java]view plaincopy

1.publicinterfaceSubject?{

2.publicvoidaccept(Visitor?visitor);

3.publicString?getSubject();

4.}

[java]view plaincopy

1.publicclassMySubjectimplementsSubject?{

2.

3.@Override

4.publicvoidaccept(Visitor?visitor)?{

5.visitor.visit(this);

6.}

7.

8.@Override

9.publicString?getSubject()?{

10.return"love";

11.}

12.}

測試:

[java]view plaincopy

1.publicclassTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.

5.Visitor?visitor?=newMyVisitor();

6.Subject?sub?=newMySubject();

7.sub.accept(visitor);

8.}

9.}

輸出:visit the

subject:love

該模式適用場景:如果我們想為一個現(xiàn)有的類增加新功能,不得不考慮幾個事情:1、新功能會不會與現(xiàn)有功能出現(xiàn)兼容性問題狈涮?2、以后會不會再需要添加特姐?3、如果類不允許修改代碼怎么辦黍氮?面對這些問題唐含,最好的解決方法就是使用訪問者模式,訪問者模式適用于數(shù)據(jù)結(jié)構(gòu)相對穩(wěn)定的系統(tǒng)沫浆,把數(shù)據(jù)結(jié)構(gòu)和算法解耦捷枯,

22、中介者模式(Mediator

中介者模式也是用來降低類類之間的耦合的专执,因為如果類類之間有依賴關(guān)系的話淮捆,不利于功能的拓展和維護,因為只要修改一個對象,其它關(guān)聯(lián)的對象都得進行修改攀痊。如果使用中介者模式桐腌,只需關(guān)心和Mediator類的關(guān)系,具體類類之間的關(guān)系及調(diào)度交給Mediator就行苟径,這有點像spring容器的作用案站。先看看圖:

User類統(tǒng)一接口,User1和User2分別是不同的對象棘街,二者之間有關(guān)聯(lián)蟆盐,如果不采用中介者模式,則需要二者相互持有引用遭殉,這樣二者的耦合度很高石挂,為了解耦,引入了Mediator類险污,提供統(tǒng)一接口痹愚,MyMediator為其實現(xiàn)類,里面持有User1和User2的實例罗心,用來實現(xiàn)對User1和User2的控制里伯。這樣User1和User2兩個對象相互獨立城瞎,他們只需要保持好和Mediator之間的關(guān)系就行渤闷,剩下的全由MyMediator類來維護!基本實現(xiàn):

[java]view plaincopy

1.publicinterfaceMediator?{

2.publicvoidcreateMediator();

3.publicvoidworkAll();

4.}

[java]view plaincopy

1.publicclassMyMediatorimplementsMediator?{

2.

3.privateUser?user1;

4.privateUser?user2;

5.

6.publicUser?getUser1()?{

7.returnuser1;

8.}

9.

10.publicUser?getUser2()?{

11.returnuser2;

12.}

13.

14.@Override

15.publicvoidcreateMediator()?{

16.user1?=newUser1(this);

17.user2?=newUser2(this);

18.}

19.

20.@Override

21.publicvoidworkAll()?{

22.user1.work();

23.user2.work();

24.}

25.}

[java]view plaincopy

1.publicabstractclassUser?{

2.

3.privateMediator?mediator;

4.

5.publicMediator?getMediator(){

6.returnmediator;

7.}

8.

9.publicUser(Mediator?mediator)?{

10.this.mediator?=?mediator;

11.}

12.

13.publicabstractvoidwork();

14.}

[java]view plaincopy

1.publicclassUser1extendsUser?{

2.

3.publicUser1(Mediator?mediator){

4.super(mediator);

5.}

6.

7.@Override

8.publicvoidwork()?{

9.System.out.println("user1?exe!");

10.}

11.}

[java]view plaincopy

1.publicclassUser2extendsUser?{

2.

3.publicUser2(Mediator?mediator){

4.super(mediator);

5.}

6.

7.@Override

8.publicvoidwork()?{

9.System.out.println("user2?exe!");

10.}

11.}

測試類:

[java]view plaincopy

1.publicclassTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.Mediator?mediator?=newMyMediator();

5.mediator.createMediator();

6.mediator.workAll();

7.}

8.}

輸出:

user1 exe!

user2 exe!

23脖镀、解釋器模式(Interpreter

解釋器模式是我們暫時的最后一講飒箭,一般主要應(yīng)用在OOP開發(fā)中的編譯器的開發(fā)中,所以適用面比較窄蜒灰。

Context類是一個上下文環(huán)境類弦蹂,Plus和Minus分別是用來計算的實現(xiàn),代碼如下:

view plaincopy

1.publicinterfaceExpression?{

2.publicintinterpret(Context?context);

3.}

[java]view plaincopy

1.publicclassPlusimplementsExpression?{

2.

3.@Override

4.publicintinterpret(Context?context)?{

5.returncontext.getNum1()+context.getNum2();

6.}

7.}

[java]view plaincopy

1.publicclassMinusimplementsExpression?{

2.

3.@Override

4.publicintinterpret(Context?context)?{

5.returncontext.getNum1()-context.getNum2();

6.}

7.}

[java]view plaincopy

1.publicclassContext?{

2.

3.privateintnum1;

4.privateintnum2;

5.

6.publicContext(intnum1,intnum2)?{

7.this.num1?=?num1;

8.this.num2?=?num2;

9.}

10.

11.publicintgetNum1()?{

12.returnnum1;

13.}

14.publicvoidsetNum1(intnum1)?{

15.this.num1?=?num1;

16.}

17.publicintgetNum2()?{

18.returnnum2;

19.}

20.publicvoidsetNum2(intnum2)?{

21.this.num2?=?num2;

22.}

23.

24.

25.}

[java]view plaincopy

1.publicclassTest?{

2.

3.publicstaticvoidmain(String[]?args)?{

4.

5.//計算9+2-8的值

6.intresult?=newMinus().interpret((newContext(newPlus()

7..interpret(newContext(9,2)),8)));

8.System.out.println(result);

9.}

10.}

最后輸出正確的結(jié)果:3

基本就這樣强窖,解釋器模式用來做各種各樣的解釋器凸椿,如正則表達式等的解釋器等等!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末翅溺,一起剝皮案震驚了整個濱河市脑漫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌咙崎,老刑警劉巖优幸,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異褪猛,居然都是意外死亡网杆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碳却,“玉大人队秩,你說我怎么就攤上這事∽烦牵” “怎么了刹碾?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長座柱。 經(jīng)常有香客問我迷帜,道長,這世上最難降的妖魔是什么色洞? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任戏锹,我火速辦了婚禮,結(jié)果婚禮上火诸,老公的妹妹穿的比我還像新娘锦针。我一直安慰自己,他們只是感情好置蜀,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布奈搜。 她就那樣靜靜地躺著,像睡著了一般盯荤。 火紅的嫁衣襯著肌膚如雪馋吗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天秋秤,我揣著相機與錄音宏粤,去河邊找鬼。 笑死灼卢,一個胖子當著我的面吹牛绍哎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鞋真,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼崇堰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涩咖?” 一聲冷哼從身側(cè)響起海诲,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抠藕,沒想到半個月后饿肺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡盾似,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年敬辣,在試婚紗的時候發(fā)現(xiàn)自己被綠了雪标。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡溉跃,死狀恐怖村刨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撰茎,我是刑警寧澤嵌牺,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站龄糊,受9級特大地震影響逆粹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜炫惩,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一僻弹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧他嚷,春花似錦蹋绽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至粘咖,卻和暖如春蚣抗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涂炎。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工忠聚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留设哗,地道東北人唱捣。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像网梢,于是被迫代替她去往敵國和親震缭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 原文鏈接:http://blog.csdn.net/zhangerqing http://www.cnblogs....
    孤獨雜貨鋪閱讀 1,508評論 0 3
  • 一战虏、設(shè)計模式的分類 總體來說設(shè)計模式分為三大類: 創(chuàng)建型模式拣宰,共五種:工廠方法模式、抽象工廠模式烦感、單例模式巡社、建造者...
    RamboLI閱讀 744評論 0 1
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法手趣,內(nèi)部類的語法晌该,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,587評論 18 399
  • 用思念作謎面 用愛你作謎底 直白的思慕會消解微妙 婉轉(zhuǎn)的相思令情絲綿長 上一秒想要做上天入地的仙女 下一秒想要做趴...
    zzvenus閱讀 304評論 1 0
  • 2017年誉帅,共享單車絕對是昆明街頭一道亮麗的風(fēng)景。仿佛一夜之間右莱,小黃車蚜锨,摩拜就擺滿了大街小巷。共享單車的出現(xiàn)慢蜓,喚醒...
    小荷苞閱讀 634評論 1 0