總體來說設(shè)計模式分為三大類:
創(chuàng)建型模式析苫,共五種:工廠方法模式兜叨、抽象工廠模式、單例模式衩侥、建造者模式国旷、原型模式。
結(jié)構(gòu)型模式茫死,共七種:適配器模式跪但、裝飾器模式、代理模式峦萎、外觀模式屡久、橋接模式、組合模式爱榔、享元模式被环。
行為型模式,共十一種:策略模式详幽、模板方法模式筛欢、觀察者模式浸锨、迭代子模式、責(zé)任鏈模式版姑、命令模式柱搜、備忘錄模式、狀態(tài)模式剥险、訪問者模式聪蘸、中介者模式、解釋器模式炒嘲。
其實還有兩類:并發(fā)型模式和線程池模式宇姚。用一個圖片來整體描述一下:
Principle)
開閉原則就是說對擴展開放夫凸,對修改關(guān)閉浑劳。在程序需要進行拓展的時候,不能去修改原有的代碼夭拌,而是要擴展原有代碼魔熏,實現(xiàn)一個熱插拔的效果。所以一句話概括就是:為了使程序的擴展性好鸽扁,易于維護和升級蒜绽。想要達到這樣的效果,我們需要使用接口和抽象類等桶现,后面的具體設(shè)計中我們會提到這點躲雅。
不要存在多于一個導(dǎo)致類變更的原因骡和,也就是說每個類應(yīng)該實現(xiàn)單一的職責(zé)相赁,如若不然,就應(yīng)該把類拆分慰于。
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)該隨便破壞它呕缭。
Inversion Principle)
這個是開閉原則的基礎(chǔ)修己,具體內(nèi)容:面向接口編程恢总,依賴于抽象而不依賴于具體。寫代碼時用到具體類時睬愤,不與具體類交互片仿,而與具體類的上層接口交互。
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)在類中雷激。
Reuse Principle)
原則是盡量首先使用合成/聚合的方式告私,而不是使用繼承屎暇。
從這一塊開始根悼,我們詳細介紹Java中23種設(shè)計模式的概念,應(yīng)用場景等情況蜀撑,并結(jié)合他們的特點及設(shè)計模式的原則進行分析挤巡。
首先,簡單工廠模式不屬于23中涉及模式酷麦,簡單工廠一般分為:普通簡單工廠矿卑、多方法簡單工廠、靜態(tài)方法簡單工廠贴铜。
簡單工廠模式模式分為三種:
就是建立一個工廠類绍坝,對實現(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!
是對普通工廠方法模式的改進把介,在普通工廠方法模式中勤讽,如果傳遞的字符串出錯,則不能正確創(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!
將上面的多個工廠方法模式里的方法置為靜態(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)工廠方法模式临庇。
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)成的代碼截亦。這樣做,拓展性較好柬讨!
工廠方法模式和抽象工廠模式不好分清楚袍啡,他們的區(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)線
單例對象(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)點倦畅,才能最好的解決問題!
原型模式雖然是創(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)的對象。
我們接著討論設(shè)計模式畜伐,上篇文章我講完了5種創(chuàng)建型模式,這章開始躺率,我將講下7種結(jié)構(gòu)型模式:適配器模式玛界、裝飾模式、代理模式悼吱、外觀模式慎框、橋接模式、組合模式后添、享元模式笨枯。其中對象的適配器模式是各種模式的起源,我們看下面的圖:
適配器模式將某個類的接口轉(zhuǎn)換成客戶端期望的另一個接口表示馅精,目的是消除由于接口不匹配所造成的類的兼容性問題。主要分為三類:類的適配器模式粱檀、對象的適配器模式洲敢、接口的適配器模式。
核心思想就是:有一個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類的功能。
基本思路和類的適配器模式相同缓醋,只是將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.}
輸出與第一種一樣抗俄,只是適配的方法不同而已。
第三種適配器模式是接口的適配器模式动雹,接口的適配器是這樣的:有時我們寫的一個接口中有多個抽象方法,當我們寫該接口的實現(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)所有方法荠列,我們寫別的類的時候,繼承抽象類即可载城。
顧名思義,裝飾模式就是給一個對象增加一些新的功能个曙,而且是動態(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)生過多相似的對象碱鳞,不易排錯!
其實每個模式名稱就表明了該模式的作用窿给,代理模式就是多一個代理類出來贵白,替原對象進行一些操作,比如我們在租房子的時候回去找中介崩泡,為什么呢禁荒?因為你對該地區(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é)果進行控制缴守。這種方法就是代理模式。
使用代理模式镇辉,可以將功能劃分的更加清晰屡穗,有助于后期維護!
外觀模式是為了解決類與類之家的依賴關(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類里,這樣就起到了解耦的作用舷夺,這苦酱,就是外觀模式!
橋接模式就是把事物和其具體實現(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é)合就都懂了娜庇。
組合模式有時又叫部分-整體模式在處理類似樹形結(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)的性能!
先來張圖俘闯,看看這11中模式的關(guān)系:
第一類:通過父類與子類的關(guān)系進行實現(xiàn)。
第二類:兩個類之間居触。
第三類:類的狀態(tài)。
第四類:通過中間類
策略模式定義了一系列算法,并將每個算法封裝起來瓮恭,使他們可以相互替換雄坪,且算法的變化不會影響到使用算法的客戶。需要設(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)中掀虎,外部用戶只需要決定用哪個算法即可凌盯。
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,打印出來振定。正好驗證了我們開頭的思路必怜。
包括這個模式在內(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)系圖,新建項目硫眨,自己寫代碼(或者參考我的代碼),按照總體思路走一遍足淆,這樣才能體會它的思想,理解起來容易礁阁!
顧名思義,迭代器模式就是順序訪問聚集中的對象姥闭,一般來說裂逐,集合中非常常見,如果對集合類比較熟悉的話泣栈,理解本模式會十分輕松卜高。這句話包含兩層意思:一是需要遍歷的對象,即聚集對象南片,二是迭代器對象掺涛,用于對聚集對象進行遍歷訪問。我們看下關(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é)會了区丑,掌握好了拧粪,我們也可以寫出自己的集合類,甚至框架沧侥!
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),同時帚豪,在一個時刻碳竟,命令只允許由一個對象傳給另一個對象,而不允許傳給多個對象狸臣。
命令模式很好理解,舉個例子烛亦,司令員下令讓士兵去干件事情诈泼,從整個事情的角度來考慮,司令員的作用是煤禽,發(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),以便在適當?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ù)”模式最形象。
核心思想就是:當對象的狀態(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)限控制等笛钝。
訪問者模式把數(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)和算法解耦捷枯,
中介者模式也是用來降低類類之間的耦合的专执,因為如果類類之間有依賴關(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!
解釋器模式是我們暫時的最后一講飒箭,一般主要應(yīng)用在OOP開發(fā)中的編譯器的開發(fā)中,所以適用面比較窄蜒灰。
Context類是一個上下文環(huán)境類弦蹂,Plus和Minus分別是用來計算的實現(xiàn),代碼如下:
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