文章部分內(nèi)容轉(zhuǎn)載自:http://blog.csdn.net/zhangerqing
一肩袍、設(shè)計(jì)模式的分類(lèi)
總體來(lái)說(shuō)設(shè)計(jì)模式分為三大類(lèi):
創(chuàng)建型模式圃伶,共五種:工廠方法模式悲关、抽象工廠模式、單例模式撤逢、建造者模式膛锭、原型模式。
結(jié)構(gòu)型模式蚊荣,共七種:適配器模式初狰、裝飾器模式、代理模式互例、外觀模式奢入、橋接模式、組合模式媳叨、享元模式腥光。
行為型模式,共十一種:策略模式糊秆、模板方法模式武福、觀察者模式、迭代子模式痘番、責(zé)任鏈模式捉片、命令模式、備忘錄模式汞舱、狀態(tài)模式伍纫、訪問(wèn)者模式、中介者模式昂芜、解釋器模式莹规。
其實(shí)還有兩類(lèi):并發(fā)型模式和線程池模式。用一個(gè)圖片來(lái)整體描述一下:
企業(yè)級(jí)項(xiàng)目實(shí)戰(zhàn)(帶源碼)地址:http://zz563143188.iteye.com/blog/1825168
23種模式j(luò)ava實(shí)現(xiàn)源碼下載地址http://pan.baidu.com/share/link?shareid=372668&uk=4076915866#dir/path=%2F%E5%AD%A6%E4%B9%A0%E6%96%87%E4%BB%B6
總原則:開(kāi)閉原則(Open Close Principle)
開(kāi)閉原則就是說(shuō)對(duì)擴(kuò)展開(kāi)放良漱,對(duì)修改關(guān)閉。在程序需要進(jìn)行拓展的時(shí)候腻扇,不能去修改原有的代碼债热,而是要擴(kuò)展原有代碼砾嫉,實(shí)現(xiàn)一個(gè)熱插拔的效果幼苛。所以一句話概括就是:為了使程序的擴(kuò)展性好,易于維護(hù)和升級(jí)焕刮。想要達(dá)到這樣的效果舶沿,我們需要使用接口和抽象類(lèi)等墙杯,后面的具體設(shè)計(jì)中我們會(huì)提到這點(diǎn)。
不要存在多于一個(gè)導(dǎo)致類(lèi)變更的原因高镐,也就是說(shuō)每個(gè)類(lèi)應(yīng)該實(shí)現(xiàn)單一的職責(zé),如若不然畸冲,就應(yīng)該把類(lèi)拆分嫉髓。
2、里氏替換原則(Liskov Substitution Principle)
里氏代換原則(Liskov Substitution Principle LSP)面向?qū)ο笤O(shè)計(jì)的基本原則之一邑闲。 里氏代換原則中說(shuō)算行,任何基類(lèi)可以出現(xiàn)的地方,子類(lèi)一定可以出現(xiàn)苫耸。 LSP是繼承復(fù)用的基石州邢,只有當(dāng)衍生類(lèi)可以替換掉基類(lèi),軟件單位的功能不受到影響時(shí)褪子,基類(lèi)才能真正被復(fù)用量淌,而衍生類(lèi)也能夠在基類(lèi)的基礎(chǔ)上增加新的行為。里氏代換原則是對(duì)“開(kāi)-閉”原則的補(bǔ)充嫌褪。實(shí)現(xiàn)“開(kāi)-閉”原則的關(guān)鍵步驟就是抽象化呀枢。而基類(lèi)與子類(lèi)的繼承關(guān)系就是抽象化的具體實(shí)現(xiàn),所以里氏代換原則是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范渔扎×蚰—— From Baidu 百科
歷史替換原則中,子類(lèi)對(duì)父類(lèi)的方法盡量不要重寫(xiě)和重載晃痴。因?yàn)楦割?lèi)代表了定義好的結(jié)構(gòu)残吩,通過(guò)這個(gè)規(guī)范的接口與外界交互,子類(lèi)不應(yīng)該隨便破壞它倘核。
3泣侮、依賴(lài)倒轉(zhuǎn)原則(Dependence Inversion Principle)
這個(gè)是開(kāi)閉原則的基礎(chǔ),具體內(nèi)容:面向接口編程紧唱,依賴(lài)于抽象而不依賴(lài)于具體活尊。寫(xiě)代碼時(shí)用到具體類(lèi)時(shí),不與具體類(lèi)交互漏益,而與具體類(lèi)的上層接口交互蛹锰。
4、接口隔離原則(Interface Segregation Principle)
這個(gè)原則的意思是:每個(gè)接口中不存在子類(lèi)用不到卻必須實(shí)現(xiàn)的方法绰疤,如果不然铜犬,就要將接口拆分。使用多個(gè)隔離的接口,比使用單個(gè)接口(多個(gè)接口方法集合到一個(gè)的接口)要好癣猾。
5敛劝、迪米特法則(最少知道原則)(Demeter Principle)
就是說(shuō):一個(gè)類(lèi)對(duì)自己依賴(lài)的類(lèi)知道的越少越好。也就是說(shuō)無(wú)論被依賴(lài)的類(lèi)多么復(fù)雜纷宇,都應(yīng)該將邏輯封裝在方法的內(nèi)部夸盟,通過(guò)public方法提供給外部。這樣當(dāng)被依賴(lài)的類(lèi)變化時(shí)像捶,才能最小的影響該類(lèi)上陕。
最少知道原則的另一個(gè)表達(dá)方式是:只與直接的朋友通信。類(lèi)之間只要有耦合關(guān)系拓春,就叫朋友關(guān)系唆垃。耦合分為依賴(lài)、關(guān)聯(lián)痘儡、聚合辕万、組合等。我們稱(chēng)出現(xiàn)為成員變量沉删、方法參數(shù)渐尿、方法返回值中的類(lèi)為直接朋友。局部變量矾瑰、臨時(shí)變量則不是直接的朋友砖茸。我們要求陌生的類(lèi)不要作為局部變量出現(xiàn)在類(lèi)中。
6殴穴、合成復(fù)用原則(Composite Reuse Principle)
原則是盡量首先使用合成/聚合的方式凉夯,而不是使用繼承。
從這一塊開(kāi)始,我們?cè)敿?xì)介紹Java中23種設(shè)計(jì)模式的概念休傍,應(yīng)用場(chǎng)景等情況征绎,并結(jié)合他們的特點(diǎn)及設(shè)計(jì)模式的原則進(jìn)行分析。
首先磨取,簡(jiǎn)單工廠模式不屬于23中涉及模式人柿,簡(jiǎn)單工廠一般分為:普通簡(jiǎn)單工廠、多方法簡(jiǎn)單工廠忙厌、靜態(tài)方法簡(jiǎn)單工廠凫岖。
簡(jiǎn)單工廠模式模式分為三種:
就是建立一個(gè)工廠類(lèi)哥放,對(duì)實(shí)現(xiàn)了同一接口的一些類(lèi)進(jìn)行實(shí)例的創(chuàng)建扎阶。首先看下關(guān)系圖:
舉例如下:(我們舉一個(gè)發(fā)送郵件和短信的例子)
首先,創(chuàng)建二者的共同接口:
[java]view plaincopy
public?interface?Sender?{??
public?void?Send();??
}??
其次婶芭,創(chuàng)建實(shí)現(xiàn)類(lèi):
[java]view plaincopy
public?class?MailSender?implements?Sender?{??
@Override??
public?void?Send()?{??
System.out.println("this?is?mailsender!");??
????}??
}??
[java]view plaincopy
public?class?SmsSender?implements?Sender?{??
@Override??
public?void?Send()?{??
System.out.println("this?is?sms?sender!");??
????}??
}??
最后,建工廠類(lèi):
[java]view plaincopy
public?class?SendFactory?{??
public?Sender?produce(String?type)?{??
if?("mail".equals(type))?{??
return?new?MailSender();??
}else?if?("sms".equals(type))?{??
return?new?SmsSender();??
}else?{??
System.out.println("請(qǐng)輸入正確的類(lèi)型!");??
return?null;??
????????}??
????}??
}??
我們來(lái)測(cè)試下:
public?class?FactoryTest?{??
public?static?void?main(String[]?args)?{??
SendFactory?factory?=new?SendFactory();??
Sender?sender?=?factory.produce("sms");??
????????sender.Send();??
????}??
}??
輸出:this is sms sender!
是對(duì)普通工廠方法模式的改進(jìn)铣焊,在普通工廠方法模式中坯约,如果傳遞的字符串出錯(cuò),則不能正確創(chuàng)建對(duì)象,而多個(gè)工廠方法模式是提供多個(gè)工廠方法引几,分別創(chuàng)建對(duì)象。關(guān)系圖:
將上面的代碼做下修改裳仆,改動(dòng)下SendFactory類(lèi)就行敲才,如下:
[java]view plaincopypublic?class?SendFactory?{ ?
? ?public?Sender?produceMail(){ ?
return?new?MailSender();??
????}??
public?Sender?produceSms(){??
return?new?SmsSender();??
????}??
}??
測(cè)試類(lèi)如下:
[java]view plaincopy
public?class?FactoryTest?{??
public?static?void?main(String[]?args)?{??
SendFactory?factory?=new?SendFactory();??
????????Sender?sender?=?factory.produceMail();??
????????sender.Send();??
????}??
}??
輸出:this is mailsender!
將上面的多個(gè)工廠方法模式里的方法置為靜態(tài)的挪拟,不需要?jiǎng)?chuàng)建實(shí)例挨务,直接調(diào)用即可。
[java]view plaincopy
public?class?SendFactory?{??
public?static?Sender?produceMail(){??
return?new?MailSender();??
????}??
public?static?Sender?produceSms(){??
return?new?SmsSender();??
????}??
}??
[java]view plaincopy
public?class?FactoryTest?{??
public?static?void?main(String[]?args)?{??????
????????Sender?sender?=?SendFactory.produceMail();??
????????sender.Send();??
????}??
}??
輸出:this is mailsender!
總體來(lái)說(shuō)玉组,工廠模式適合:凡是出現(xiàn)了大量的產(chǎn)品需要?jiǎng)?chuàng)建谎柄,并且具有共同的接口時(shí),可以通過(guò)工廠方法模式進(jìn)行創(chuàng)建惯雳。在以上的三種模式中朝巫,第一種如果傳入的字符串有誤,不能正確創(chuàng)建對(duì)象石景,第三種相對(duì)于第二種劈猿,不需要實(shí)例化工廠類(lèi),所以潮孽,大多數(shù)情況下揪荣,我們會(huì)選用第三種——靜態(tài)工廠方法模式。
簡(jiǎn)單工廠模式有一個(gè)問(wèn)題就是变逃,類(lèi)的創(chuàng)建依賴(lài)工廠類(lèi),也就是說(shuō)怠堪,如果想要拓展程序揽乱,必須對(duì)工廠類(lèi)進(jìn)行修改,這違背了閉包原則粟矿,所以凰棉,從設(shè)計(jì)角度考慮,有一定的問(wèn)題陌粹,如何解決撒犀?就用到工廠方法模式,創(chuàng)建一個(gè)工廠接口和創(chuàng)建多個(gè)工廠實(shí)現(xiàn)類(lèi),這樣一旦需要增加新的功能或舞,直接增加新的工廠類(lèi)就可以了荆姆,不需要修改之前的代碼。
請(qǐng)看例子:
[java]view plaincopy
public?interface?Sender?{??
public?void?Send();??
}??
兩個(gè)實(shí)現(xiàn)類(lèi):
[java]view plaincopy
public?class?MailSender?implements?Sender?{??
@Override??
public?void?Send()?{??
System.out.println("this?is?mailsender!");??
????}??
}??
[java]view plaincopy
public?class?SmsSender?implements?Sender?{??
@Override??
public?void?Send()?{??
System.out.println("this?is?sms?sender!");??
????}??
}??
兩個(gè)工廠類(lèi):
[java]view plaincopy
public?class?SendMailFactory?implements?Provider?{??
@Override??
public?Sender?produce(){??
return?new?MailSender();??
????}??
}??
[java]view plaincopy
public?class?SendSmsFactory?implements?Provider{??
@Override??
public?Sender?produce()?{??
return?new?SmsSender();??
????}??
}??
在提供一個(gè)接口:
[java]view plaincopy
public?interface?Provider?{??
public?Sender?produce();??
}??
測(cè)試類(lèi):
[java]view plaincopy
public?class?Test?{??
public?static?void?main(String[]?args)?{??
Provider?provider?=new?SendMailFactory();??
????????Sender?sender?=?provider.produce();??
????????sender.Send();??
????}??
}??
其實(shí)這個(gè)模式的好處就是映凳,如果你現(xiàn)在想增加一個(gè)功能:發(fā)及時(shí)信息胆筒,則只需做一個(gè)實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)Sender接口诈豌,同時(shí)做一個(gè)工廠類(lèi)仆救,實(shí)現(xiàn)Provider接口,就OK了矫渔,無(wú)需去改動(dòng)現(xiàn)成的代碼彤蔽。這樣做,拓展性較好庙洼!
工廠方法模式和抽象工廠模式不好分清楚,他們的區(qū)別如下:
工廠方法模式:
一個(gè)抽象產(chǎn)品類(lèi)油够,可以派生出多個(gè)具體產(chǎn)品類(lèi)员魏。?
一個(gè)抽象工廠類(lèi),可以派生出多個(gè)具體工廠類(lèi)叠聋。?
每個(gè)具體工廠類(lèi)只能創(chuàng)建一個(gè)具體產(chǎn)品類(lèi)的實(shí)例撕阎。
抽象工廠模式:
多個(gè)抽象產(chǎn)品類(lèi),每個(gè)抽象產(chǎn)品類(lèi)可以派生出多個(gè)具體產(chǎn)品類(lèi)碌补。?
一個(gè)抽象工廠類(lèi)虏束,可以派生出多個(gè)具體工廠類(lèi)。?
每個(gè)具體工廠類(lèi)可以創(chuàng)建多個(gè)具體產(chǎn)品類(lèi)的實(shí)例厦章,也就是創(chuàng)建的是一個(gè)產(chǎn)品線下的多個(gè)產(chǎn)品镇匀。?
區(qū)別:
工廠方法模式只有一個(gè)抽象產(chǎn)品類(lèi),而抽象工廠模式有多個(gè)袜啃。?
工廠方法模式的具體工廠類(lèi)只能創(chuàng)建一個(gè)具體產(chǎn)品類(lèi)的實(shí)例汗侵,而抽象工廠模式可以創(chuàng)建多個(gè)。
工廠方法創(chuàng)建 "一種" 產(chǎn)品群发,他的著重點(diǎn)在于"怎么創(chuàng)建"晰韵,也就是說(shuō)如果你開(kāi)發(fā),你的大量代碼很可能?chē)@著這種產(chǎn)品的構(gòu)造熟妓,初始化這些細(xì)節(jié)上面雪猪。也因?yàn)槿绱耍?lèi)似的產(chǎn)品之間有很多可以復(fù)用的特征起愈,所以會(huì)和模版方法相隨只恨。?
抽象工廠需要?jiǎng)?chuàng)建一些列產(chǎn)品译仗,著重點(diǎn)在于"創(chuàng)建哪些"產(chǎn)品上,也就是說(shuō)官觅,如果你開(kāi)發(fā)纵菌,你的主要任務(wù)是劃分不同差異的產(chǎn)品線,并且盡量保持每條產(chǎn)品線接口一致休涤,從而可以從同一個(gè)抽象工廠繼承咱圆。
對(duì)于java來(lái)說(shuō),你能見(jiàn)到的大部分抽象工廠模式都是這樣的:
---它的里面是一堆工廠方法滑绒,每個(gè)工廠方法返回某種類(lèi)型的對(duì)象。
比如說(shuō)工廠可以生產(chǎn)鼠標(biāo)和鍵盤(pán)隘膘。那么抽象工廠的實(shí)現(xiàn)類(lèi)(它的某個(gè)具體子類(lèi))的對(duì)象都可以生產(chǎn)鼠標(biāo)和鍵盤(pán)疑故,但可能工廠A生產(chǎn)的是羅技的鍵盤(pán)和鼠標(biāo),工廠B是微軟的弯菊。
這樣A和B就是工廠纵势,對(duì)應(yīng)于抽象工廠;
每個(gè)工廠生產(chǎn)的鼠標(biāo)和鍵盤(pán)就是產(chǎn)品管钳,對(duì)應(yīng)于工廠方法钦铁;
用了工廠方法模式,你替換生成鍵盤(pán)的工廠方法才漆,就可以把鍵盤(pán)從羅技換到微軟牛曹。但是用了抽象工廠模式,你只要換家工廠醇滥,就可以同時(shí)替換鼠標(biāo)和鍵盤(pán)一套黎比。如果你要的產(chǎn)品有幾十個(gè),當(dāng)然用抽象工廠模式一次替換全部最方便(這個(gè)工廠會(huì)替你用相應(yīng)的工廠方法)
所以說(shuō)抽象工廠就像工廠鸳玩,而工廠方法則像是工廠的一種產(chǎn)品生產(chǎn)線
單例對(duì)象(Singleton)是一種常用的設(shè)計(jì)模式。在Java應(yīng)用中不跟,單例對(duì)象能保證在一個(gè)JVM中颓帝,該對(duì)象只有一個(gè)實(shí)例存在。這樣的模式有幾個(gè)好處:
1窝革、某些類(lèi)創(chuàng)建比較頻繁购城,對(duì)于一些大型的對(duì)象,這是一筆很大的系統(tǒng)開(kāi)銷(xiāo)虐译。
2工猜、省去了new操作符,降低了系統(tǒng)內(nèi)存的使用頻率菱蔬,減輕GC壓力篷帅。
3史侣、有些類(lèi)如交易所的核心交易引擎,控制著交易流程魏身,如果該類(lèi)可以創(chuàng)建多個(gè)的話惊橱,系統(tǒng)完全亂了。(比如一個(gè)軍隊(duì)出現(xiàn)了多個(gè)司令員同時(shí)指揮箭昵,肯定會(huì)亂成一團(tuán))税朴,所以只有使用單例模式,才能保證核心交易服務(wù)器獨(dú)立控制整個(gè)流程家制。
首先我們寫(xiě)一個(gè)簡(jiǎn)單的單例類(lèi):
[java]view plaincopy
public?class?Singleton?{??
/*?持有私有靜態(tài)實(shí)例正林,防止被引用,此處賦值為null颤殴,目的是實(shí)現(xiàn)延遲加載?*/??
private?static?Singleton?instance?=?null;??
/*?私有構(gòu)造方法觅廓,防止被實(shí)例化?*/??
private?Singleton()?{??
????}??
/*?靜態(tài)工程方法,創(chuàng)建實(shí)例?*/??
public?static?Singleton?getInstance()?{??
if?(instance?==?null)?{??
instance?=new?Singleton();??
????????}??
return?instance;??
????}??
/*?如果該對(duì)象被用于序列化涵但,可以保證對(duì)象在序列化前后保持一致?*/??
public?Object?readResolve()?{??
return?instance;??
????}??
}??
這個(gè)類(lèi)可以滿足基本要求杈绸,但是,像這樣毫無(wú)線程安全保護(hù)的類(lèi)矮瘟,如果我們把它放入多線程的環(huán)境下瞳脓,肯定就會(huì)出現(xiàn)問(wèn)題了,如何解決澈侠?我們首先會(huì)想到對(duì)getInstance方法加synchronized關(guān)鍵字劫侧,如下:
[java]view plaincopy
public?static?synchronized?Singleton?getInstance()?{??
if?(instance?==?null)?{??
instance?=new?Singleton();??
????????}??
return?instance;??
????}??
但是,synchronized關(guān)鍵字鎖住的是這個(gè)對(duì)象哨啃,這樣的用法板辽,在性能上會(huì)有所下降,因?yàn)槊看握{(diào)用getInstance()棘催,都要對(duì)對(duì)象上鎖劲弦,事實(shí)上,只有在第一次創(chuàng)建對(duì)象的時(shí)候需要加鎖醇坝,之后就不需要了邑跪,所以,這個(gè)地方需要改進(jìn)呼猪。我們改成下面這個(gè):
[java]view plaincopy
public?static?Singleton?getInstance()?{??
if?(instance?==?null)?{??
synchronized?(instance)?{??
if?(instance?==?null)?{??
instance?=new?Singleton();??
????????????????}??
????????????}??
????????}??
return?instance;??
????}??
似乎解決了之前提到的問(wèn)題画畅,將synchronized關(guān)鍵字加在了內(nèi)部,也就是說(shuō)當(dāng)調(diào)用的時(shí)候是不需要加鎖的宋距,只有在instance為null轴踱,并創(chuàng)建對(duì)象的時(shí)候才需要加鎖,性能有一定的提升谚赎。但是淫僻,這樣的情況诱篷,還是有可能有問(wèn)題的,看下面的情況:在Java指令中創(chuàng)建對(duì)象和賦值操作是分開(kāi)進(jìn)行的雳灵,也就是說(shuō)instance = new Singleton();語(yǔ)句是分兩步執(zhí)行的棕所。但是JVM并不保證這兩個(gè)操作的先后順序,也就是說(shuō)有可能JVM會(huì)為新的Singleton實(shí)例分配空間悯辙,然后直接賦值給instance成員琳省,然后再去初始化這個(gè)Singleton實(shí)例。這樣就可能出錯(cuò)了躲撰,我們以A针贬、B兩個(gè)線程為例:
a>A、B線程同時(shí)進(jìn)入了第一個(gè)if判斷
b>A首先進(jìn)入synchronized塊拢蛋,由于instance為null桦他,所以它執(zhí)行instance = new Singleton();
c>由于JVM內(nèi)部的優(yōu)化機(jī)制,JVM先畫(huà)出了一些分配給Singleton實(shí)例的空白內(nèi)存瓤狐,并賦值給instance成員(注意此時(shí)JVM沒(méi)有開(kāi)始初始化這個(gè)實(shí)例)瞬铸,然后A離開(kāi)了synchronized塊批幌。
d>B進(jìn)入synchronized塊础锐,由于instance此時(shí)不是null,因此它馬上離開(kāi)了synchronized塊并將結(jié)果返回給調(diào)用該方法的程序荧缘。
e>此時(shí)B線程打算使用Singleton實(shí)例皆警,卻發(fā)現(xiàn)它沒(méi)有被初始化,于是錯(cuò)誤發(fā)生了截粗。
所以程序還是有可能發(fā)生錯(cuò)誤信姓,其實(shí)程序在運(yùn)行過(guò)程是很復(fù)雜的,從這點(diǎn)我們就可以看出绸罗,尤其是在寫(xiě)多線程環(huán)境下的程序更有難度意推,有挑戰(zhàn)性。我們對(duì)該程序做進(jìn)一步優(yōu)化:
[java]view plaincopy
private?static?class?SingletonFactory{???????????
private?static?Singleton?instance?=?new?Singleton();???????????
????}???????????
public?static?Singleton?getInstance(){???????????
return?SingletonFactory.instance;???????????
????}???
實(shí)際情況是珊蟀,單例模式使用內(nèi)部類(lèi)來(lái)維護(hù)單例的實(shí)現(xiàn)菊值,JVM內(nèi)部的機(jī)制能夠保證當(dāng)一個(gè)類(lèi)被加載的時(shí)候,這個(gè)類(lèi)的加載過(guò)程是線程互斥的育灸。這樣當(dāng)我們第一次調(diào)用getInstance的時(shí)候腻窒,JVM能夠幫我們保證instance只被創(chuàng)建一次,并且會(huì)保證把賦值給instance的內(nèi)存初始化完畢磅崭,這樣我們就不用擔(dān)心上面的問(wèn)題儿子。同時(shí)該方法也只會(huì)在第一次調(diào)用的時(shí)候使用互斥機(jī)制,這樣就解決了低性能問(wèn)題砸喻。這樣我們暫時(shí)總結(jié)一個(gè)完美的單例模式:
[java]view plaincopy
public?class?Singleton?{??
/*?私有構(gòu)造方法柔逼,防止被實(shí)例化?*/??
private?Singleton()?{??
????}??
/*?此處使用一個(gè)內(nèi)部類(lèi)來(lái)維護(hù)單例?*/??
private?static?class?SingletonFactory?{??
private?static?Singleton?instance?=?new?Singleton();??
????}??
/*?獲取實(shí)例?*/??
public?static?Singleton?getInstance()?{??
return?SingletonFactory.instance;??
????}??
/*?如果該對(duì)象被用于序列化蒋譬,可以保證對(duì)象在序列化前后保持一致?*/??
public?Object?readResolve()?{??
return?getInstance();??
????}??
}??
其實(shí)說(shuō)它完美,也不一定卒落,如果在構(gòu)造函數(shù)中拋出異常羡铲,實(shí)例將永遠(yuǎn)得不到創(chuàng)建,也會(huì)出錯(cuò)儡毕。所以說(shuō)也切,十分完美的東西是沒(méi)有的,我們只能根據(jù)實(shí)際情況腰湾,選擇最適合自己應(yīng)用場(chǎng)景的實(shí)現(xiàn)方法雷恃。也有人這樣實(shí)現(xiàn):因?yàn)槲覀冎恍枰趧?chuàng)建類(lèi)的時(shí)候進(jìn)行同步,所以只要將創(chuàng)建和getInstance()分開(kāi)费坊,單獨(dú)為創(chuàng)建加synchronized關(guān)鍵字倒槐,也是可以的:
[java]view plaincopy
public?class?SingletonTest?{??
private?static?SingletonTest?instance?=?null;??
private?SingletonTest()?{??
????}??
private?static?synchronized?void?syncInit()?{??
if?(instance?==?null)?{??
instance?=new?SingletonTest();??
????????}??
????}??
public?static?SingletonTest?getInstance()?{??
if?(instance?==?null)?{??
????????????syncInit();??
????????}??
return?instance;??
????}??
}??
考慮性能的話,整個(gè)程序只需創(chuàng)建一次實(shí)例附井,所以性能也不會(huì)有什么影響讨越。
補(bǔ)充:采用"影子實(shí)例"的辦法為單例對(duì)象的屬性同步更新
[java]view plaincopy
public?class?SingletonTest?{??
private?static?SingletonTest?instance?=?null;??
private?Vector?properties?=?null;??
public?Vector?getProperties()?{??
return?properties;??
????}??
private?SingletonTest()?{??
????}??
private?static?synchronized?void?syncInit()?{??
if?(instance?==?null)?{??
instance?=new?SingletonTest();??
????????}??
????}??
public?static?SingletonTest?getInstance()?{??
if?(instance?==?null)?{??
????????????syncInit();??
????????}??
return?instance;??
????}??
public?void?updateProperties()?{??
SingletonTest?shadow?=new?SingletonTest();??
????????properties?=?shadow.getProperties();??
????}??
}??
通過(guò)單例模式的學(xué)習(xí)告訴我們:
1、單例模式理解起來(lái)簡(jiǎn)單永毅,但是具體實(shí)現(xiàn)起來(lái)還是有一定的難度把跨。
2、synchronized關(guān)鍵字鎖定的是對(duì)象沼死,在用的時(shí)候着逐,一定要在恰當(dāng)?shù)牡胤绞褂茫ㄗ⒁庑枰褂面i的對(duì)象和過(guò)程,可能有的時(shí)候并不是整個(gè)對(duì)象及整個(gè)過(guò)程都需要鎖)意蛀。
到這兒耸别,單例模式基本已經(jīng)講完了,結(jié)尾處县钥,筆者突然想到另一個(gè)問(wèn)題秀姐,就是采用類(lèi)的靜態(tài)方法,實(shí)現(xiàn)單例模式的效果若贮,也是可行的省有,此處二者有什么不同?
首先兜看,靜態(tài)類(lèi)不能實(shí)現(xiàn)接口锥咸。(從類(lèi)的角度說(shuō)是可以的,但是那樣就破壞了靜態(tài)了细移。因?yàn)榻涌谥胁辉试S有static修飾的方法搏予,所以即使實(shí)現(xiàn)了也是非靜態(tài)的)
其次,單例可以被延遲初始化弧轧,靜態(tài)類(lèi)一般在第一次加載是初始化雪侥。之所以延遲加載碗殷,是因?yàn)橛行╊?lèi)比較龐大,所以延遲加載有助于提升性能速缨。
再次锌妻,單例類(lèi)可以被繼承貌矿,他的方法可以被覆寫(xiě)垮耳。但是靜態(tài)類(lèi)內(nèi)部方法都是static,無(wú)法被覆寫(xiě)崭添。
最后一點(diǎn)原茅,單例類(lèi)比較靈活吭历,畢竟從實(shí)現(xiàn)上只是一個(gè)普通的Java類(lèi),只要滿足單例的基本需求擂橘,你可以在里面隨心所欲的實(shí)現(xiàn)一些其它功能晌区,但是靜態(tài)類(lèi)不行。從上面這些概括中通贞,基本可以看出二者的區(qū)別朗若,但是,從另一方面講昌罩,我們上面最后實(shí)現(xiàn)的那個(gè)單例模式哭懈,內(nèi)部就是用一個(gè)靜態(tài)類(lèi)來(lái)實(shí)現(xiàn)的,所以峡迷,二者有很大的關(guān)聯(lián)银伟,只是我們考慮問(wèn)題的層面不同罷了你虹。兩種思想的結(jié)合绘搞,才能造就出完美的解決方案,就像HashMap采用數(shù)組+鏈表來(lái)實(shí)現(xiàn)一樣傅物,其實(shí)生活中很多事情都是這樣夯辖,單用不同的方法來(lái)處理問(wèn)題,總是有優(yōu)點(diǎn)也有缺點(diǎn)董饰,最完美的方法是蒿褂,結(jié)合各個(gè)方法的優(yōu)點(diǎn),才能最好的解決問(wèn)題卒暂!
原型模式雖然是創(chuàng)建型的模式也祠,但是與工程模式?jīng)]有關(guān)系昙楚,從名字即可看出,該模式的思想就是將一個(gè)對(duì)象作為原型诈嘿,對(duì)其進(jìn)行復(fù)制堪旧、克隆削葱,產(chǎn)生一個(gè)和原對(duì)象類(lèi)似的新對(duì)象。本小結(jié)會(huì)通過(guò)對(duì)象的復(fù)制淳梦,進(jìn)行講解析砸。在Java中,復(fù)制對(duì)象是通過(guò)clone()實(shí)現(xiàn)的爆袍,先創(chuàng)建一個(gè)原型類(lèi):
[java]view plaincopy
public?class?Prototype?implements?Cloneable?{??
public?Object?clone()?throws?CloneNotSupportedException?{??
Prototype?proto?=?(Prototype)super.clone();??
return?proto;??
????}??
}??
很簡(jiǎn)單首繁,一個(gè)原型類(lèi),只需要實(shí)現(xiàn)Cloneable接口陨囊,覆寫(xiě)clone方法蛮瞄,此處clone方法可以改成任意的名稱(chēng),因?yàn)镃loneable接口是個(gè)空接口谆扎,你可以任意定義實(shí)現(xiàn)類(lèi)的方法名挂捅,如cloneA或者cloneB,因?yàn)榇颂幍闹攸c(diǎn)是super.clone()這句話堂湖,super.clone()調(diào)用的是Object的clone()方法闲先,而在Object類(lèi)中,clone()是native的无蜂,具體怎么實(shí)現(xiàn)伺糠,我會(huì)在另一篇文章中,關(guān)于解讀Java中本地方法的調(diào)用斥季,此處不再深究训桶。在這兒,我將結(jié)合對(duì)象的淺復(fù)制和深復(fù)制來(lái)說(shuō)一下酣倾,首先需要了解對(duì)象深舵揭、淺復(fù)制的概念:
淺復(fù)制:將一個(gè)對(duì)象復(fù)制后,基本數(shù)據(jù)類(lèi)型的變量都會(huì)重新創(chuàng)建躁锡,而引用類(lèi)型午绳,指向的還是原對(duì)象所指向的。
深復(fù)制:將一個(gè)對(duì)象復(fù)制后映之,不論是基本數(shù)據(jù)類(lèi)型還有引用類(lèi)型拦焚,都是重新創(chuàng)建的。簡(jiǎn)單來(lái)說(shuō)杠输,就是深復(fù)制進(jìn)行了完全徹底的復(fù)制赎败,而淺復(fù)制不徹底。
此處蠢甲,寫(xiě)一個(gè)深淺復(fù)制的例子:
[java]view plaincopy
public?class?Prototype?implements?Cloneable,?Serializable?{??
private?static?final?long?serialVersionUID?=?1L;??
private?String?string;??
private?SerializableObject?obj;??
/*?淺復(fù)制?*/??
public?Object?clone()?throws?CloneNotSupportedException?{??
Prototype?proto?=?(Prototype)super.clone();??
return?proto;??
????}??
/*?深復(fù)制?*/??
public?Object?deepClone()?throws?IOException,?ClassNotFoundException?{??
/*?寫(xiě)入當(dāng)前對(duì)象的二進(jìn)制流?*/??
ByteArrayOutputStream?bos?=new?ByteArrayOutputStream();??
ObjectOutputStream?oos?=new?ObjectOutputStream(bos);??
oos.writeObject(this);??
/*?讀出二進(jìn)制流產(chǎn)生的新對(duì)象?*/??
ByteArrayInputStream?bis?=new?ByteArrayInputStream(bos.toByteArray());??
ObjectInputStream?ois?=new?ObjectInputStream(bis);??
return?ois.readObject();??
????}??
public?String?getString()?{??
return?string;??
????}??
public?void?setString(String?string)?{??
this.string?=?string;??
????}??
public?SerializableObject?getObj()?{??
return?obj;??
????}??
public?void?setObj(SerializableObject?obj)?{??
this.obj?=?obj;??
????}??
}??
class?SerializableObject?implements?Serializable?{??
private?static?final?long?serialVersionUID?=?1L;??
}??
要實(shí)現(xiàn)深復(fù)制僵刮,需要采用流的形式讀入當(dāng)前對(duì)象的二進(jìn)制輸入,再寫(xiě)出二進(jìn)制數(shù)據(jù)對(duì)應(yīng)的對(duì)象。
我們接著討論設(shè)計(jì)模式若河,上篇文章我講完了5種創(chuàng)建型模式,這章開(kāi)始寞宫,我將講下7種結(jié)構(gòu)型模式:適配器模式萧福、裝飾模式、代理模式辈赋、外觀模式鲫忍、橋接模式、組合模式钥屈、享元模式悟民。其中對(duì)象的適配器模式是各種模式的起源,我們看下面的圖:
?適配器模式將某個(gè)類(lèi)的接口轉(zhuǎn)換成客戶端期望的另一個(gè)接口表示射亏,目的是消除由于接口不匹配所造成的類(lèi)的兼容性問(wèn)題。主要分為三類(lèi):類(lèi)的適配器模式竭业、對(duì)象的適配器模式智润、接口的適配器模式。
核心思想就是:有一個(gè)Source類(lèi)窟绷,擁有一個(gè)方法,待適配咐柜,目標(biāo)接口是Targetable兼蜈,通過(guò)Adapter類(lèi),將Source的功能擴(kuò)展到Targetable里拙友,看代碼:
[java]view plaincopy
public?class?Source?{??
public?void?method1()?{??
System.out.println("this?is?original?method!");??
????}??
}??
[java]view plaincopy
public?interface?Targetable?{??
/*?與原類(lèi)中的方法相同?*/??
public?void?method1();??
/*?新類(lèi)的方法?*/??
public?void?method2();??
}??
[java]view plaincopy
public?class?Adapter?extends?Source?implements?Targetable?{??
@Override??
public?void?method2()?{??
System.out.println("this?is?the?targetable?method!");??
????}??
}??
Adapter類(lèi)繼承Source類(lèi)为狸,實(shí)現(xiàn)Targetable接口,下面是測(cè)試類(lèi):
[java]view plaincopy
public?class?AdapterTest?{??
public?static?void?main(String[]?args)?{??
Targetable?target?=new?Adapter();??
????????target.method1();??
????????target.method2();??
????}??
}??
輸出:
this is original method!
this is the targetable method!
這樣Targetable接口的實(shí)現(xiàn)類(lèi)就具有了Source類(lèi)的功能献宫。
基本思路和類(lèi)的適配器模式相同实撒,只是將Adapter類(lèi)作修改姊途,這次不繼承Source類(lèi),而是持有Source類(lèi)的實(shí)例知态,以達(dá)到解決兼容性的問(wèn)題捷兰。看圖:
只需要修改Adapter類(lèi)的源碼即可:
[java]view plaincopy
public?class?Wrapper?implements?Targetable?{??
private?Source?source;??
public?Wrapper(Source?source){??
super();??
this.source?=?source;??
????}??
@Override??
public?void?method2()?{??
System.out.println("this?is?the?targetable?method!");??
????}??
@Override??
public?void?method1()?{??
????????source.method1();??
????}??
}??
測(cè)試類(lèi):
[java]view plaincopy
public?class?AdapterTest?{??
public?static?void?main(String[]?args)?{??
Source?source?=new?Source();??
Targetable?target?=new?Wrapper(source);??
????????target.method1();??
????????target.method2();??
????}??
}??
輸出與第一種一樣负敏,只是適配的方法不同而已贡茅。
第三種適配器模式是接口的適配器模式,接口的適配器是這樣的:有時(shí)我們寫(xiě)的一個(gè)接口中有多個(gè)抽象方法顶考,當(dāng)我們寫(xiě)該接口的實(shí)現(xiàn)類(lèi)時(shí)赁还,必須實(shí)現(xiàn)該接口的所有方法,這明顯有時(shí)比較浪費(fèi)驹沿,因?yàn)椴⒉皇撬械姆椒ǘ际俏覀冃枰乃也撸袝r(shí)只需要某一些,此處為了解決這個(gè)問(wèn)題渊季,我們引入了接口的適配器模式朋蔫,借助于一個(gè)抽象類(lèi),該抽象類(lèi)實(shí)現(xiàn)了該接口却汉,實(shí)現(xiàn)了所有的方法驯妄,而我們不和原始的接口打交道,只和該抽象類(lèi)取得聯(lián)系合砂,所以我們寫(xiě)一個(gè)類(lèi)青扔,繼承該抽象類(lèi),重寫(xiě)我們需要的方法就行翩伪∈昱常看一下類(lèi)圖:
這個(gè)很好理解,在實(shí)際開(kāi)發(fā)中幻工,我們也常會(huì)遇到這種接口中定義了太多的方法励两,以致于有時(shí)我們?cè)谝恍?shí)現(xiàn)類(lèi)中并不是都需要∧衣看代碼:
[java]view plaincopy
public?interface?Sourceable?{??
public?void?method1();??
public?void?method2();??
}??
抽象類(lèi)Wrapper2:
[java]view plaincopy
public?abstract?class?Wrapper2?implements?Sourceable{??
public?void?method1(){}??
public?void?method2(){}??
}??
[java]view plaincopy
public?class?SourceSub1?extends?Wrapper2?{??
public?void?method1(){??
System.out.println("the?sourceable?interface's?first?Sub1!");??
????}??
}??
[java]view plaincopy
public?class?SourceSub2?extends?Wrapper2?{??
public?void?method2(){??
System.out.println("the?sourceable?interface's?second?Sub2!");??
????}??
}??
[java]view plaincopy
public?class?WrapperTest?{??
public?static?void?main(String[]?args)?{??
Sourceable?source1?=new?SourceSub1();??
Sourceable?source2?=new?SourceSub2();??
????????source1.method1();??
????????source1.method2();??
????????source2.method1();??
????????source2.method2();??
????}??
}??
測(cè)試輸出:
the sourceable interface's first Sub1!
the sourceable interface's second Sub2!
達(dá)到了我們的效果当悔!
?講了這么多,總結(jié)一下三種適配器模式的應(yīng)用場(chǎng)景:
類(lèi)的適配器模式:當(dāng)希望將一個(gè)類(lèi)轉(zhuǎn)換成滿足另一個(gè)新接口的類(lèi)時(shí)踢代,可以使用類(lèi)的適配器模式盲憎,創(chuàng)建一個(gè)新類(lèi),繼承原有的類(lèi)胳挎,實(shí)現(xiàn)新的接口即可饼疙。
對(duì)象的適配器模式:當(dāng)希望將一個(gè)對(duì)象轉(zhuǎn)換成滿足另一個(gè)新接口的對(duì)象時(shí),可以創(chuàng)建一個(gè)Wrapper類(lèi)慕爬,持有原類(lèi)的一個(gè)實(shí)例窑眯,在Wrapper類(lèi)的方法中,調(diào)用實(shí)例的方法就行医窿。
接口的適配器模式:當(dāng)不希望實(shí)現(xiàn)一個(gè)接口中所有的方法時(shí)磅甩,可以創(chuàng)建一個(gè)抽象類(lèi)Wrapper,實(shí)現(xiàn)所有方法姥卢,我們寫(xiě)別的類(lèi)的時(shí)候卷要,繼承抽象類(lèi)即可渣聚。
顧名思義僧叉,裝飾模式就是給一個(gè)對(duì)象增加一些新的功能奕枝,而且是動(dòng)態(tài)的,要求裝飾對(duì)象和被裝飾對(duì)象實(shí)現(xiàn)同一個(gè)接口瓶堕,裝飾對(duì)象持有被裝飾對(duì)象的實(shí)例倍权,關(guān)系圖如下:
Source類(lèi)是被裝飾類(lèi),Decorator類(lèi)是一個(gè)裝飾類(lèi)捞烟,可以為Source類(lèi)動(dòng)態(tài)的添加一些功能薄声,代碼如下:
[java]view plaincopy
public?interface?Sourceable?{??
public?void?method();??
}??
[java]view plaincopy
public?class?Source?implements?Sourceable?{??
@Override??
public?void?method()?{??
System.out.println("the?original?method!");??
????}??
}??
[java]view plaincopy
public?class?Decorator?implements?Sourceable?{??
private?Sourceable?source;??
public?Decorator(Sourceable?source){??
super();??
this.source?=?source;??
????}??
@Override??
public?void?method()?{??
System.out.println("before?decorator!");??
????????source.method();??
System.out.println("after?decorator!");??
????}??
}??
測(cè)試類(lèi):
[java]view plaincopy
public?class?DecoratorTest?{??
public?static?void?main(String[]?args)?{??
Sourceable?source?=new?Source();??
Sourceable?obj?=new?Decorator(source);??
????????obj.method();??
????}??
}??
輸出:
before decorator!
the original method!
after decorator!
裝飾器模式的應(yīng)用場(chǎng)景:
1、需要擴(kuò)展一個(gè)類(lèi)的功能题画。
2默辨、動(dòng)態(tài)的為一個(gè)對(duì)象增加功能,而且還能動(dòng)態(tài)撤銷(xiāo)苍息。(繼承不能做到這一點(diǎn)缩幸,繼承的功能是靜態(tài)的,不能動(dòng)態(tài)增刪竞思。)
缺點(diǎn):產(chǎn)生過(guò)多相似的對(duì)象表谊,不易排錯(cuò)!
其實(shí)每個(gè)模式名稱(chēng)就表明了該模式的作用爆办,代理模式就是多一個(gè)代理類(lèi)出來(lái),替原對(duì)象進(jìn)行一些操作课梳,比如我們?cè)谧夥孔拥臅r(shí)候回去找中介距辆,為什么呢?因?yàn)槟銓?duì)該地區(qū)房屋的信息掌握的不夠全面暮刃,希望找一個(gè)更熟悉的人去幫你做跨算,此處的代理就是這個(gè)意思。再如我們有的時(shí)候打官司椭懊,我們需要請(qǐng)律師诸蚕,因?yàn)槁蓭熢诜煞矫嬗袑?zhuān)長(zhǎng),可以替我們進(jìn)行操作氧猬,表達(dá)我們的想法背犯。先來(lái)看看關(guān)系圖:
根據(jù)上文的闡述,代理模式就比較容易的理解了狂窑,我們看下代碼:
[java]view plaincopy
public?interface?Sourceable?{??
public?void?method();??
}??
[java]view plaincopy
public?class?Source?implements?Sourceable?{??
@Override??
public?void?method()?{??
System.out.println("the?original?method!");??
????}??
}??
[java]view plaincopy
public?class?Proxy?implements?Sourceable?{??
private?Source?source;??
public?Proxy(){??
super();??
this.source?=?new?Source();??
????}??
@Override??
public?void?method()?{??
????????before();??
????????source.method();??
????????atfer();??
????}??
private?void?atfer()?{??
System.out.println("after?proxy!");??
????}??
private?void?before()?{??
System.out.println("before?proxy!");??
????}??
}??
測(cè)試類(lèi):
[java]view plaincopy
public?class?ProxyTest?{??
public?static?void?main(String[]?args)?{??
Sourceable?source?=new?Proxy();??
????????source.method();??
????}??
}??
輸出:
before proxy!
the original method!
after proxy!
代理模式的應(yīng)用場(chǎng)景:
如果已有的方法在使用的時(shí)候需要對(duì)原有的方法進(jìn)行改進(jìn)媳板,此時(shí)有兩種辦法:
1、修改原有的方法來(lái)適應(yīng)泉哈。這樣違反了“對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉”的原則。
2丛晦、就是采用一個(gè)代理類(lèi)調(diào)用原有的方法奕纫,且對(duì)產(chǎn)生的結(jié)果進(jìn)行控制。這種方法就是代理模式烫沙。
使用代理模式匹层,可以將功能劃分的更加清晰,有助于后期維護(hù)锌蓄!
外觀模式是為了解決類(lèi)與類(lèi)之家的依賴(lài)關(guān)系的,像spring一樣瘸爽,可以將類(lèi)和類(lèi)之間的關(guān)系配置到配置文件中您访,而外觀模式就是將他們的關(guān)系放在一個(gè)Facade類(lèi)中,降低了類(lèi)類(lèi)之間的耦合度剪决,該模式中沒(méi)有涉及到接口灵汪,看下類(lèi)圖:(我們以一個(gè)計(jì)算機(jī)的啟動(dòng)過(guò)程為例)
我們先看下實(shí)現(xiàn)類(lèi):
[java]view plaincopy
public?class?CPU?{??
public?void?startup(){??
System.out.println("cpu?startup!");??
????}??
public?void?shutdown(){??
System.out.println("cpu?shutdown!");??
????}??
}??
[java]view plaincopy
public?class?Memory?{??
public?void?startup(){??
System.out.println("memory?startup!");??
????}??
public?void?shutdown(){??
System.out.println("memory?shutdown!");??
????}??
}??
[java]view plaincopy
public?class?Disk?{??
public?void?startup(){??
System.out.println("disk?startup!");??
????}??
public?void?shutdown(){??
System.out.println("disk?shutdown!");??
????}??
}??
[java]view plaincopy
public?class?Computer?{??
private?CPU?cpu;??
private?Memory?memory;??
private?Disk?disk;??
public?Computer(){??
cpu?=new?CPU();??
memory?=new?Memory();??
disk?=new?Disk();??
????}??
public?void?startup(){??
System.out.println("start?the?computer!");??
????????cpu.startup();??
????????memory.startup();??
????????disk.startup();??
System.out.println("start?computer?finished!");??
????}??
public?void?shutdown(){??
System.out.println("begin?to?close?the?computer!");??
????????cpu.shutdown();??
????????memory.shutdown();??
????????disk.shutdown();??
System.out.println("computer?closed!");??
????}??
}??
User類(lèi)如下:
[java]view plaincopy
public?class?User?{??
public?static?void?main(String[]?args)?{??
Computer?computer?=new?Computer();??
????????computer.startup();??
????????computer.shutdown();??
????}??
}??
輸出:
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!
如果我們沒(méi)有Computer類(lèi),那么柑潦,CPU享言、Memory、Disk他們之間將會(huì)相互持有實(shí)例渗鬼,產(chǎn)生關(guān)系览露,這樣會(huì)造成嚴(yán)重的依賴(lài),修改一個(gè)類(lèi)譬胎,可能會(huì)帶來(lái)其他類(lèi)的修改肛循,這不是我們想要看到的,有了Computer類(lèi)银择,他們之間的關(guān)系被放在了Computer類(lèi)里多糠,這樣就起到了解耦的作用,這浩考,就是外觀模式夹孔!
橋接模式就是把事物和其具體實(shí)現(xiàn)分開(kāi)析孽,使他們可以各自獨(dú)立的變化搭伤。橋接的用意是:將抽象化與實(shí)現(xiàn)化解耦,使得二者可以獨(dú)立變化袜瞬,像我們常用的JDBC橋DriverManager一樣怜俐,JDBC進(jìn)行連接數(shù)據(jù)庫(kù)的時(shí)候,在各個(gè)數(shù)據(jù)庫(kù)之間進(jìn)行切換邓尤,基本不需要?jiǎng)犹嗟拇a拍鲤,甚至絲毫不用動(dòng)贴谎,原因就是JDBC提供統(tǒng)一接口,每個(gè)數(shù)據(jù)庫(kù)提供各自的實(shí)現(xiàn)季稳,用一個(gè)叫做數(shù)據(jù)庫(kù)驅(qū)動(dòng)的程序來(lái)橋接就行了擅这。我們來(lái)看看關(guān)系圖:
實(shí)現(xiàn)代碼:
先定義接口:
[java]view plaincopy
public?interface?Sourceable?{??
public?void?method();??
}??
分別定義兩個(gè)實(shí)現(xiàn)類(lèi):
[java]view plaincopy
public?class?SourceSub1?implements?Sourceable?{??
@Override??
public?void?method()?{??
System.out.println("this?is?the?first?sub!");??
????}??
}??
[java]view plaincopy
public?class?SourceSub2?implements?Sourceable?{??
@Override??
public?void?method()?{??
System.out.println("this?is?the?second?sub!");??
????}??
}??
定義一個(gè)橋,持有Sourceable的一個(gè)實(shí)例:
[java]view plaincopy
public?abstract?class?Bridge?{??
private?Sourceable?source;??
public?void?method(){??
????????source.method();??
????}??
public?Sourceable?getSource()?{??
return?source;??
????}??
public?void?setSource(Sourceable?source)?{??
this.source?=?source;??
????}??
}??
[java]view plaincopy
public?class?MyBridge?extends?Bridge?{??
public?void?method(){??
????????getSource().method();??
????}??
}??
測(cè)試類(lèi):
[java]view plaincopy
public?class?BridgeTest?{??
public?static?void?main(String[]?args)?{??
Bridge?bridge?=new?MyBridge();??
/*調(diào)用第一個(gè)對(duì)象*/??
Sourceable?source1?=new?SourceSub1();??
????????bridge.setSource(source1);??
????????bridge.method();??
/*調(diào)用第二個(gè)對(duì)象*/??
Sourceable?source2?=new?SourceSub2();??
????????bridge.setSource(source2);??
????????bridge.method();??
????}??
}??
output:
this is the first sub!
this is the second sub!
這樣景鼠,就通過(guò)對(duì)Bridge類(lèi)的調(diào)用仲翎,實(shí)現(xiàn)了對(duì)接口Sourceable的實(shí)現(xiàn)類(lèi)SourceSub1和SourceSub2的調(diào)用。接下來(lái)我再畫(huà)個(gè)圖铛漓,大家就應(yīng)該明白了溯香,因?yàn)檫@個(gè)圖是我們JDBC連接的原理,有數(shù)據(jù)庫(kù)學(xué)習(xí)基礎(chǔ)的浓恶,一結(jié)合就都懂了玫坛。
組合模式有時(shí)又叫部分-整體模式在處理類(lèi)似樹(shù)形結(jié)構(gòu)的問(wèn)題時(shí)比較方便问顷,看看關(guān)系圖:
直接來(lái)看代碼:
[java]view plaincopy
public?class?TreeNode?{??
private?String?name;??
private?TreeNode?parent;??
private?Vector?children?=?new?Vector();??
public?TreeNode(String?name){??
this.name?=?name;??
????}??
public?String?getName()?{??
return?name;??
????}??
public?void?setName(String?name)?{??
this.name?=?name;??
????}??
public?TreeNode?getParent()?{??
return?parent;??
????}??
public?void?setParent(TreeNode?parent)?{??
this.parent?=?parent;??
????}??
//添加孩子節(jié)點(diǎn)??
public?void?add(TreeNode?node){??
????????children.add(node);??
????}??
//刪除孩子節(jié)點(diǎn)??
public?void?remove(TreeNode?node){??
????????children.remove(node);??
????}??
//取得孩子節(jié)點(diǎn)??
public?Enumeration?getChildren(){??
return?children.elements();??
????}??
}??
[java]view plaincopy
public?class?Tree?{??
TreeNode?root?=null;??
public?Tree(String?name)?{??
root?=new?TreeNode(name);??
????}??
public?static?void?main(String[]?args)?{??
Tree?tree?=new?Tree("A");??
TreeNode?nodeB?=new?TreeNode("B");??
TreeNode?nodeC?=new?TreeNode("C");??
????????nodeB.add(nodeC);??
????????tree.root.add(nodeB);??
System.out.println("build?the?tree?finished!");??
????}??
}??
使用場(chǎng)景:將多個(gè)對(duì)象組合在一起進(jìn)行操作昂秃,常用于表示樹(shù)形結(jié)構(gòu)中,例如二叉樹(shù)杜窄,數(shù)等肠骆。
12、享元模式(Flyweight)
享元模式的主要目的是實(shí)現(xiàn)對(duì)象的共享塞耕,即共享池蚀腿,當(dāng)系統(tǒng)中對(duì)象多的時(shí)候可以減少內(nèi)存的開(kāi)銷(xiāo),通常與工廠模式一起使用。
FlyWeightFactory負(fù)責(zé)創(chuàng)建和管理享元單元,當(dāng)一個(gè)客戶端請(qǐng)求時(shí)苛茂,工廠需要檢查當(dāng)前對(duì)象池中是否有符合條件的對(duì)象,如果有磁玉,就返回已經(jīng)存在的對(duì)象,如果沒(méi)有驾讲,則創(chuàng)建一個(gè)新對(duì)象蚊伞,F(xiàn)lyWeight是超類(lèi)。一提到共享池吮铭,我們很容易聯(lián)想到Java里面的JDBC連接池时迫,想想每個(gè)連接的特點(diǎn),我們不難總結(jié)出:適用于作共享的一些個(gè)對(duì)象谓晌,他們有一些共有的屬性掠拳,就拿數(shù)據(jù)庫(kù)連接池來(lái)說(shuō),url纸肉、driverClassName溺欧、username喊熟、password及dbname,這些屬性對(duì)于每個(gè)連接來(lái)說(shuō)都是一樣的胧奔,所以就適合用享元模式來(lái)處理逊移,建一個(gè)工廠類(lèi)预吆,將上述類(lèi)似屬性作為內(nèi)部數(shù)據(jù)龙填,其它的作為外部數(shù)據(jù),在方法調(diào)用時(shí)拐叉,當(dāng)做參數(shù)傳進(jìn)來(lái)岩遗,這樣就節(jié)省了空間,減少了實(shí)例的數(shù)量凤瘦。
看個(gè)例子:
看下數(shù)據(jù)庫(kù)連接池的代碼:
[java]view plaincopy
public?class?ConnectionPool?{??
private?Vector?pool;??
/*公有屬性*/??
private?String?url?=?"jdbc:mysql://localhost:3306/test";??
private?String?username?=?"root";??
private?String?password?=?"root";??
private?String?driverClassName?=?"com.mysql.jdbc.Driver";??
private?int?poolSize?=?100;??
private?static?ConnectionPool?instance?=?null;??
Connection?conn?=null;??
/*構(gòu)造方法宿礁,做一些初始化工作*/??
private?ConnectionPool()?{??
pool?=new?Vector(poolSize);??
for?(int?i?=?0;?i?<?poolSize;?i++)?{??
try?{??
????????????????Class.forName(driverClassName);??
????????????????conn?=?DriverManager.getConnection(url,?username,?password);??
????????????????pool.add(conn);??
}catch?(ClassNotFoundException?e)?{??
????????????????e.printStackTrace();??
}catch?(SQLException?e)?{??
????????????????e.printStackTrace();??
????????????}??
????????}??
????}??
/*?返回連接到連接池?*/??
public?synchronized?void?release()?{??
????????pool.add(conn);??
????}??
/*?返回連接池中的一個(gè)數(shù)據(jù)庫(kù)連接?*/??
public?synchronized?Connection?getConnection()?{??
if?(pool.size()?>?0)?{??
Connection?conn?=?pool.get(0);??
????????????pool.remove(conn);??
return?conn;??
}else?{??
return?null;??
????????}??
????}??
}??
通過(guò)連接池的管理,實(shí)現(xiàn)了數(shù)據(jù)庫(kù)連接的共享蔬芥,不需要每一次都重新創(chuàng)建連接梆靖,節(jié)省了數(shù)據(jù)庫(kù)重新創(chuàng)建的開(kāi)銷(xiāo),提升了系統(tǒng)的性能笔诵!
先來(lái)張圖,看看這11中模式的關(guān)系:
第一類(lèi):通過(guò)父類(lèi)與子類(lèi)的關(guān)系進(jìn)行實(shí)現(xiàn)乎婿。
第二類(lèi):兩個(gè)類(lèi)之間测僵。
第三類(lèi):類(lèi)的狀態(tài)。
第四類(lèi):通過(guò)中間類(lèi)
策略模式定義了一系列算法捍靠,并將每個(gè)算法封裝起來(lái),使他們可以相互替換森逮,且算法的變化不會(huì)影響到使用算法的客戶榨婆。需要設(shè)計(jì)一個(gè)接口,為一系列實(shí)現(xiàn)類(lèi)提供統(tǒng)一的方法褒侧,多個(gè)實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)該接口良风,設(shè)計(jì)一個(gè)抽象類(lèi)(可有可無(wú),屬于輔助類(lèi))璃搜,提供輔助函數(shù)拖吼,關(guān)系圖如下:
圖中ICalculator提供同意的方法,
AbstractCalculator是輔助類(lèi)这吻,提供輔助方法吊档,接下來(lái),依次實(shí)現(xiàn)下每個(gè)類(lèi):
首先統(tǒng)一接口:
[java]view plaincopy
public?interface?ICalculator?{??
public?int?calculate(String?exp);??
}??
輔助類(lèi):
[java]view plaincopy
public?abstract?class?AbstractCalculator?{??
public?int[]?split(String?exp,String?opt){??
????????String?array[]?=?exp.split(opt);??
int?arrayInt[]?=?new?int[2];??
arrayInt[0]?=?Integer.parseInt(array[0]);??
arrayInt[1]?=?Integer.parseInt(array[1]);??
return?arrayInt;??
????}??
}??
三個(gè)實(shí)現(xiàn)類(lèi):
[java]view plaincopy
public?class?Plus?extends?AbstractCalculator?implements?ICalculator?{??
@Override??
public?int?calculate(String?exp)?{??
int?arrayInt[]?=?split(exp,"\\+");??
return?arrayInt[0]+arrayInt[1];??
????}??
}??
[java]view plaincopy
public?class?Minus?extends?AbstractCalculator?implements?ICalculator?{??
@Override??
public?int?calculate(String?exp)?{??
int?arrayInt[]?=?split(exp,"-");??
return?arrayInt[0]-arrayInt[1];??
????}??
}??
[java]view plaincopy
public?class?Multiply?extends?AbstractCalculator?implements?ICalculator?{??
@Override??
public?int?calculate(String?exp)?{??
int?arrayInt[]?=?split(exp,"\\*");??
return?arrayInt[0]*arrayInt[1];??
????}??
}??
簡(jiǎn)單的測(cè)試類(lèi):
[java]view plaincopy
public?class?StrategyTest?{??
public?static?void?main(String[]?args)?{??
String?exp?="2+8";??
ICalculator?cal?=new?Plus();??
int?result?=?cal.calculate(exp);??
????????System.out.println(result);??
????}??
}??
輸出:10
策略模式的決定權(quán)在用戶唾糯,系統(tǒng)本身提供不同算法的實(shí)現(xiàn)怠硼,新增或者刪除算法鬼贱,對(duì)各種算法做封裝。因此香璃,策略模式多用在算法決策系統(tǒng)中这难,外部用戶只需要決定用哪個(gè)算法即可。
解釋一下模板方法模式姻乓,就是指:一個(gè)抽象類(lèi)中,有一個(gè)主方法眯牧,再定義1...n個(gè)方法蹋岩,可以是抽象的,也可以是實(shí)際的方法学少,定義一個(gè)類(lèi)剪个,繼承該抽象類(lèi),重寫(xiě)抽象方法版确,通過(guò)調(diào)用抽象類(lèi)扣囊,實(shí)現(xiàn)對(duì)子類(lèi)的調(diào)用,先看個(gè)關(guān)系圖:
就是在AbstractCalculator類(lèi)中定義一個(gè)主方法calculate绒疗,calculate()調(diào)用spilt()等侵歇,Plus和Minus分別繼承AbstractCalculator類(lèi),通過(guò)對(duì)AbstractCalculator的調(diào)用實(shí)現(xiàn)對(duì)子類(lèi)的調(diào)用忌堂,看下面的例子:
[java]view plaincopy
public?abstract?class?AbstractCalculator?{??
/*主方法盒至,實(shí)現(xiàn)對(duì)本類(lèi)其它方法的調(diào)用*/??
public?final?int?calculate(String?exp,String?opt){??
int?array[]?=?split(exp,opt);??
return?calculate(array[0],array[1]);??
????}??
/*被子類(lèi)重寫(xiě)的方法*/??
abstract?public?int?calculate(int?num1,int?num2);??
public?int[]?split(String?exp,String?opt){??
????????String?array[]?=?exp.split(opt);??
int?arrayInt[]?=?new?int[2];??
arrayInt[0]?=?Integer.parseInt(array[0]);??
arrayInt[1]?=?Integer.parseInt(array[1]);??
return?arrayInt;??
????}??
}??
[java]view plaincopy
public?class?Plus?extends?AbstractCalculator?{??
@Override??
public?int?calculate(int?num1,int?num2)?{??
return?num1?+?num2;??
????}??
}??
測(cè)試類(lèi):
[java]view plaincopy
public?class?StrategyTest?{??
public?static?void?main(String[]?args)?{??
String?exp?="8+8";??
AbstractCalculator?cal?=new?Plus();??
int?result?=?cal.calculate(exp,?"\\+");??
????????System.out.println(result);??
????}??
}??
我跟蹤下這個(gè)小程序的執(zhí)行過(guò)程:首先將exp和"\\+"做參數(shù),調(diào)用AbstractCalculator類(lèi)里的calculate(String,String)方法士修,在calculate(String,String)里調(diào)用同類(lèi)的split()枷遂,之后再調(diào)用calculate(int ,int)方法,從這個(gè)方法進(jìn)入到子類(lèi)中棋嘲,執(zhí)行完return num1 + num2后酒唉,將值返回到AbstractCalculator類(lèi),賦給result沸移,打印出來(lái)痪伦。正好驗(yàn)證了我們開(kāi)頭的思路。
包括這個(gè)模式在內(nèi)的接下來(lái)的四個(gè)模式网沾,都是類(lèi)和類(lèi)之間的關(guān)系,不涉及到繼承蕊爵,學(xué)的時(shí)候應(yīng)該 記得歸納辉哥,記得本文最開(kāi)始的那個(gè)圖。觀察者模式很好理解,類(lèi)似于郵件訂閱和RSS訂閱醋旦,當(dāng)我們?yōu)g覽一些博客或wiki時(shí)恒水,經(jīng)常會(huì)看到RSS圖標(biāo),就這的意思是饲齐,當(dāng)你訂閱了該文章钉凌,如果后續(xù)有更新,會(huì)及時(shí)通知你捂人。其實(shí)御雕,簡(jiǎn)單來(lái)講就一句話:當(dāng)一個(gè)對(duì)象變化時(shí),其它依賴(lài)該對(duì)象的對(duì)象都會(huì)收到通知先慷,并且隨著變化饮笛!對(duì)象之間是一種一對(duì)多的關(guān)系咨察。先來(lái)看看關(guān)系圖:
我解釋下這些類(lèi)的作用:MySubject類(lèi)就是我們的主對(duì)象论熙,Observer1和Observer2是依賴(lài)于MySubject的對(duì)象,當(dāng)MySubject變化時(shí)摄狱,Observer1和Observer2必然變化脓诡。AbstractSubject類(lèi)中定義著需要監(jiān)控的對(duì)象列表,可以對(duì)其進(jìn)行修改:增加或刪除被監(jiān)控對(duì)象媒役,且當(dāng)MySubject變化時(shí)祝谚,負(fù)責(zé)通知在列表內(nèi)存在的對(duì)象。我們看實(shí)現(xiàn)代碼:
一個(gè)Observer接口:
[java]view plaincopy
public?interface?Observer?{??
public?void?update();??
}??
兩個(gè)實(shí)現(xiàn)類(lèi):
[java]view plaincopy
public?class?Observer1?implements?Observer?{??
@Override??
public?void?update()?{??
System.out.println("observer1?has?received!");??
????}??
}??
[java]view plaincopy
public?class?Observer2?implements?Observer?{??
@Override??
public?void?update()?{??
System.out.println("observer2?has?received!");??
????}??
}??
Subject接口及實(shí)現(xiàn)類(lèi):
[java]view plaincopy
public?interface?Subject?{??
/*增加觀察者*/??
public?void?add(Observer?observer);??
/*刪除觀察者*/??
public?void?del(Observer?observer);??
/*通知所有的觀察者*/??
public?void?notifyObservers();??
/*自身的操作*/??
public?void?operation();??
}??
[java]view plaincopy
public?abstract?class?AbstractSubject?implements?Subject?{??
private?Vector?vector?=?new?Vector();??
@Override??
public?void?add(Observer?observer)?{??
????????vector.add(observer);??
????}??
@Override??
public?void?del(Observer?observer)?{??
????????vector.remove(observer);??
????}??
@Override??
public?void?notifyObservers()?{??
????????Enumeration?enumo?=?vector.elements();??
while(enumo.hasMoreElements()){??
????????????enumo.nextElement().update();??
????????}??
????}??
}??
[java]view plaincopy
public?class?MySubject?extends?AbstractSubject?{??
@Override??
public?void?operation()?{??
System.out.println("update?self!");??
????????notifyObservers();??
????}??
}??
測(cè)試類(lèi):
[java]view plaincopy
public?class?ObserverTest?{??
public?static?void?main(String[]?args)?{??
Subject?sub?=new?MySubject();??
sub.add(new?Observer1());??
sub.add(new?Observer2());??
????????sub.operation();??
????}??
}??
輸出:
update self!
observer1 has received!
observer2 has received!
這些東西酣衷,其實(shí)不難交惯,只是有些抽象,不太容易整體理解穿仪,建議讀者:根據(jù)關(guān)系圖席爽,新建項(xiàng)目,自己寫(xiě)代碼(或者參考我的代碼),按照總體思路走一遍啊片,這樣才能體會(huì)它的思想只锻,理解起來(lái)容易!
顧名思義齐饮,迭代器模式就是順序訪問(wèn)聚集中的對(duì)象,一般來(lái)說(shuō)笤昨,集合中非常常見(jiàn)祖驱,如果對(duì)集合類(lèi)比較熟悉的話,理解本模式會(huì)十分輕松瞒窒。這句話包含兩層意思:一是需要遍歷的對(duì)象捺僻,即聚集對(duì)象,二是迭代器對(duì)象根竿,用于對(duì)聚集對(duì)象進(jìn)行遍歷訪問(wèn)陵像。我們看下關(guān)系圖:
這個(gè)思路和我們常用的一模一樣就珠,MyCollection中定義了集合的一些操作,MyIterator中定義了一系列迭代操作醒颖,且持有Collection實(shí)例妻怎,我們來(lái)看看實(shí)現(xiàn)代碼:
兩個(gè)接口:
[java]view plaincopy
public?interface?Collection?{??
public?Iterator?iterator();??
/*取得集合元素*/??
public?Object?get(int?i);??
/*取得集合大小*/??
public?int?size();??
}??
[java]view plaincopy
public?interface?Iterator?{??
//前移??
public?Object?previous();??
//后移??
public?Object?next();??
public?boolean?hasNext();??
//取得第一個(gè)元素??
public?Object?first();??
}??
兩個(gè)實(shí)現(xiàn):
[java]view plaincopy
public?class?MyCollection?implements?Collection?{??
public?String?string[]?=?{"A","B","C","D","E"};??
@Override??
public?Iterator?iterator()?{??
return?new?MyIterator(this);??
????}??
@Override??
public?Object?get(int?i)?{??
return?string[i];??
????}??
@Override??
public?int?size()?{??
return?string.length;??
????}??
}??
[java]view plaincopy
public?class?MyIterator?implements?Iterator?{??
private?Collection?collection;??
private?int?pos?=?-1;??
public?MyIterator(Collection?collection){??
this.collection?=?collection;??
????}??
@Override??
public?Object?previous()?{??
if(pos?>?0){??
????????????pos--;??
????????}??
return?collection.get(pos);??
????}??
@Override??
public?Object?next()?{??
if(pos
????????????pos++;??
????????}??
return?collection.get(pos);??
????}??
@Override??
public?boolean?hasNext()?{??
if(pos
return?true;??
}else{??
return?false;??
????????}??
????}??
@Override??
public?Object?first()?{??
pos?=0;??
return?collection.get(pos);??
????}??
}??
測(cè)試類(lèi):
[java]view plaincopy
public?class?Test?{??
public?static?void?main(String[]?args)?{??
Collection?collection?=new?MyCollection();??
????????Iterator?it?=?collection.iterator();??
while(it.hasNext()){??
????????????System.out.println(it.next());??
????????}??
????}??
}??
輸出:A B C D E
此處我們貌似模擬了一個(gè)集合類(lèi)的過(guò)程,感覺(jué)是不是很爽泞歉?其實(shí)JDK中各個(gè)類(lèi)也都是這些基本的東西逼侦,加一些設(shè)計(jì)模式,再加一些優(yōu)化放到一起的腰耙,只要我們把這些東西學(xué)會(huì)了榛丢,掌握好了,我們也可以寫(xiě)出自己的集合類(lèi)挺庞,甚至框架晰赞!
17、責(zé)任鏈模式(Chain of Responsibility)
接下來(lái)我們將要談?wù)勜?zé)任鏈模式选侨,有多個(gè)對(duì)象掖鱼,每個(gè)對(duì)象持有對(duì)下一個(gè)對(duì)象的引用,這樣就會(huì)形成一條鏈援制,請(qǐng)求在這條鏈上傳遞戏挡,直到某一對(duì)象決定處理該請(qǐng)求。但是發(fā)出者并不清楚到底最終那個(gè)對(duì)象會(huì)處理該請(qǐng)求晨仑,所以褐墅,責(zé)任鏈模式可以實(shí)現(xiàn),在隱瞞客戶端的情況下洪己,對(duì)系統(tǒng)進(jìn)行動(dòng)態(tài)的調(diào)整妥凳。先看看關(guān)系圖:
Abstracthandler類(lèi)提供了get和set方法,方便MyHandle類(lèi)設(shè)置和修改引用對(duì)象码泛,MyHandle類(lèi)是核心猾封,實(shí)例化后生成一系列相互持有的對(duì)象,構(gòu)成一條鏈噪珊。
[java]view plaincopy
public?interface?Handler?{??
public?void?operator();??
}??
[java]view plaincopy
public?abstract?class?AbstractHandler?{??
private?Handler?handler;??
public?Handler?getHandler()?{??
return?handler;??
????}??
public?void?setHandler(Handler?handler)?{??
this.handler?=?handler;??
????}??
}??
[java]view plaincopy
public?class?MyHandler?extends?AbstractHandler?implements?Handler?{??
private?String?name;??
public?MyHandler(String?name)?{??
this.name?=?name;??
????}??
@Override??
public?void?operator()?{??
System.out.println(name+"deal!");??
if(getHandler()!=null){??
????????????getHandler().operator();??
????????}??
????}??
}??
[java]view plaincopy
public?class?Test?{??
public?static?void?main(String[]?args)?{??
MyHandler?h1?=new?MyHandler("h1");??
MyHandler?h2?=new?MyHandler("h2");??
MyHandler?h3?=new?MyHandler("h3");??
????????h1.setHandler(h2);??
????????h2.setHandler(h3);??
????????h1.operator();??
????}??
}??
輸出:
h1deal!
h2deal!
h3deal!
此處強(qiáng)調(diào)一點(diǎn)就是晌缘,鏈接上的請(qǐng)求可以是一條鏈,可以是一個(gè)樹(shù)痢站,還可以是一個(gè)環(huán)磷箕,模式本身不約束這個(gè),需要我們自己去實(shí)現(xiàn)阵难,同時(shí)岳枷,在一個(gè)時(shí)刻,命令只允許由一個(gè)對(duì)象傳給另一個(gè)對(duì)象,而不允許傳給多個(gè)對(duì)象空繁。
命令模式很好理解,舉個(gè)例子盛泡,司令員下令讓士兵去干件事情闷祥,從整個(gè)事情的角度來(lái)考慮,司令員的作用是傲诵,發(fā)出口令凯砍,口令經(jīng)過(guò)傳遞,傳到了士兵耳朵里拴竹,士兵去執(zhí)行悟衩。這個(gè)過(guò)程好在,三者相互解耦栓拜,任何一方都不用去依賴(lài)其他人座泳,只需要做好自己的事兒就行,司令員要的是結(jié)果菱属,不會(huì)去關(guān)注到底士兵是怎么實(shí)現(xiàn)的钳榨。我們看看關(guān)系圖:
Invoker是調(diào)用者(司令員),Receiver是被調(diào)用者(士兵)纽门,MyCommand是命令,實(shí)現(xiàn)了Command接口营罢,持有接收對(duì)象赏陵,看實(shí)現(xiàn)代碼:
[java]view plaincopy
public?interface?Command?{??
public?void?exe();??
}??
[java]view plaincopy
public?class?MyCommand?implements?Command?{??
private?Receiver?receiver;??
public?MyCommand(Receiver?receiver)?{??
this.receiver?=?receiver;??
????}??
@Override??
public?void?exe()?{??
????????receiver.action();??
????}??
}??
[java]view plaincopy
public?class?Receiver?{??
public?void?action(){??
System.out.println("command?received!");??
????}??
}??
[java]view plaincopy
public?class?Invoker?{??
private?Command?command;??
public?Invoker(Command?command)?{??
this.command?=?command;??
????}??
public?void?action(){??
????????command.exe();??
????}??
}??
[java]view plaincopy
public?class?Test?{??
public?static?void?main(String[]?args)?{??
Receiver?receiver?=new?Receiver();??
Command?cmd?=new?MyCommand(receiver);??
Invoker?invoker?=new?Invoker(cmd);??
????????invoker.action();??
????}??
}??
輸出:command received!
這個(gè)很哈理解,命令模式的目的就是達(dá)到命令的發(fā)出者和執(zhí)行者之間解耦饲漾,實(shí)現(xiàn)請(qǐng)求和執(zhí)行分開(kāi)蝙搔,熟悉Struts的同學(xué)應(yīng)該知道,Struts其實(shí)就是一種將請(qǐng)求和呈現(xiàn)分離的技術(shù)考传,其中必然涉及命令模式的思想吃型!
其實(shí)每個(gè)設(shè)計(jì)模式都是很重要的一種思想,看上去很熟僚楞,其實(shí)是因?yàn)槲覀冊(cè)趯W(xué)到的東西中都有涉及勤晚,盡管有時(shí)我們并不知道,其實(shí)在Java本身的設(shè)計(jì)之中處處都有體現(xiàn)泉褐,像AWT赐写、JDBC、集合類(lèi)膜赃、IO管道或者是Web框架挺邀,里面設(shè)計(jì)模式無(wú)處不在。因?yàn)槲覀兤邢蓿茈y講每一個(gè)設(shè)計(jì)模式都講的很詳細(xì)端铛,不過(guò)我會(huì)盡我所能泣矛,盡量在有限的空間和篇幅內(nèi),把意思寫(xiě)清楚了禾蚕,更好讓大家明白乳蓄。本章不出意外的話,應(yīng)該是設(shè)計(jì)模式最后一講了夕膀,首先還是上一下上篇開(kāi)頭的那個(gè)圖:
本章講講第三類(lèi)和第四類(lèi)虚倒。
主要目的是保存一個(gè)對(duì)象的某個(gè)狀態(tài)产舞,以便在適當(dāng)?shù)臅r(shí)候恢復(fù)對(duì)象魂奥,個(gè)人覺(jué)得叫備份模式更形象些,通俗的講下:假設(shè)有原始類(lèi)A易猫,A中有各種屬性耻煤,A可以決定需要備份的屬性,備忘錄類(lèi)B是用來(lái)存儲(chǔ)A的一些內(nèi)部狀態(tài)准颓,類(lèi)C呢哈蝇,就是一個(gè)用來(lái)存儲(chǔ)備忘錄的,且只能存儲(chǔ)攘已,不能修改等操作炮赦。做個(gè)圖來(lái)分析一下:
Original類(lèi)是原始類(lèi),里面有需要保存的屬性value及創(chuàng)建一個(gè)備忘錄類(lèi)样勃,用來(lái)保存value值吠勘。Memento類(lèi)是備忘錄類(lèi),Storage類(lèi)是存儲(chǔ)備忘錄的類(lèi)峡眶,持有Memento類(lèi)的實(shí)例剧防,該模式很好理解。直接看源碼:
[java]view plaincopy
public?class?Original?{??
private?String?value;??
public?String?getValue()?{??
return?value;??
????}??
public?void?setValue(String?value)?{??
this.value?=?value;??
????}??
public?Original(String?value)?{??
this.value?=?value;??
????}??
public?Memento?createMemento(){??
return?new?Memento(value);??
????}??
public?void?restoreMemento(Memento?memento){??
this.value?=?memento.getValue();??
????}??
}??
[java]view plaincopy
public?class?Memento?{??
private?String?value;??
public?Memento(String?value)?{??
this.value?=?value;??
????}??
public?String?getValue()?{??
return?value;??
????}??
public?void?setValue(String?value)?{??
this.value?=?value;??
????}??
}??
[java]view plaincopy
public?class?Storage?{??
private?Memento?memento;??
public?Storage(Memento?memento)?{??
this.memento?=?memento;??
????}??
public?Memento?getMemento()?{??
return?memento;??
????}??
public?void?setMemento(Memento?memento)?{??
this.memento?=?memento;??
????}??
}??
測(cè)試類(lèi):
[java]view plaincopy
public?class?Test?{??
public?static?void?main(String[]?args)?{??
//?創(chuàng)建原始類(lèi)??
Original?origi?=new?Original("egg");??
//?創(chuàng)建備忘錄??
Storage?storage?=new?Storage(origi.createMemento());??
//?修改原始類(lèi)的狀態(tài)??
System.out.println("初始化狀態(tài)為:"?+?origi.getValue());??
origi.setValue("niu");??
System.out.println("修改后的狀態(tài)為:"?+?origi.getValue());??
//?回復(fù)原始類(lèi)的狀態(tài)??
????????origi.restoreMemento(storage.getMemento());??
System.out.println("恢復(fù)后的狀態(tài)為:"?+?origi.getValue());??
????}??
}??
輸出:
初始化狀態(tài)為:egg
修改后的狀態(tài)為:niu
恢復(fù)后的狀態(tài)為:egg
簡(jiǎn)單描述下:新建原始類(lèi)時(shí)辫樱,value被初始化為egg峭拘,后經(jīng)過(guò)修改,將value的值置為niu狮暑,最后倒數(shù)第二行進(jìn)行恢復(fù)狀態(tài)鸡挠,結(jié)果成功恢復(fù)了。其實(shí)我覺(jué)得這個(gè)模式叫“備份-恢復(fù)”模式最形象心例。
核心思想就是:當(dāng)對(duì)象的狀態(tài)改變時(shí),同時(shí)改變其行為止后,很好理解瞎惫!就拿QQ來(lái)說(shuō)溜腐,有幾種狀態(tài),在線瓜喇、隱身挺益、忙碌等,每個(gè)狀態(tài)對(duì)應(yīng)不同的操作乘寒,而且你的好友也能看到你的狀態(tài)望众,所以伞辛,狀態(tài)模式就兩點(diǎn):1蚤氏、可以通過(guò)改變狀態(tài)來(lái)獲得不同的行為甘耿。2佳恬、你的好友能同時(shí)看到你的變化』俅校看圖:
State類(lèi)是個(gè)狀態(tài)類(lèi),Context類(lèi)可以實(shí)現(xiàn)切換柱告,我們來(lái)看看代碼:
[java]view plaincopy
package?com.xtfggef.dp.state;??
/**
?*?狀態(tài)類(lèi)的核心類(lèi)
?*?2012-12-1
?*?@author?erqing
?*
?*/??
public?class?State?{??
private?String?value;??
public?String?getValue()?{??
return?value;??
????}??
public?void?setValue(String?value)?{??
this.value?=?value;??
????}??
public?void?method1(){??
System.out.println("execute?the?first?opt!");??
????}??
public?void?method2(){??
System.out.println("execute?the?second?opt!");??
????}??
}??
[java]view plaincopy
package?com.xtfggef.dp.state;??
/**
?*?狀態(tài)模式的切換類(lèi)???2012-12-1
?*?@author?erqing
?*?
?*/??
public?class?Context?{??
private?State?state;??
public?Context(State?state)?{??
this.state?=?state;??
????}??
public?State?getState()?{??
return?state;??
????}??
public?void?setState(State?state)?{??
this.state?=?state;??
????}??
public?void?method()?{??
if?(state.getValue().equals("state1"))?{??
????????????state.method1();??
}else?if?(state.getValue().equals("state2"))?{??
????????????state.method2();??
????????}??
????}??
}??
測(cè)試類(lèi):
[java]view plaincopy
public?class?Test?{??
public?static?void?main(String[]?args)?{??
State?state?=new?State();??
Context?context?=new?Context(state);??
//設(shè)置第一種狀態(tài)??
state.setValue("state1");??
????????context.method();??
//設(shè)置第二種狀態(tài)??
state.setValue("state2");??
????????context.method();??
????}??
}??
輸出:
execute the first opt!
execute the second opt!
根據(jù)這個(gè)特性涵妥,狀態(tài)模式在日常開(kāi)發(fā)中用的挺多的,尤其是做網(wǎng)站的時(shí)候帆锋,我們有時(shí)希望根據(jù)對(duì)象的某一屬性锯厢,區(qū)別開(kāi)他們的一些功能捺氢,比如說(shuō)簡(jiǎn)單的權(quán)限控制等摄乒。
訪問(wèn)者模式把數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作解耦合拭荤,使得操作集合可相對(duì)自由地演化。訪問(wèn)者模式適用于數(shù)據(jù)結(jié)構(gòu)相對(duì)穩(wěn)定算法又易變化的系統(tǒng)歇终。因?yàn)樵L問(wèn)者模式使得算法操作增加變得容易。若系統(tǒng)數(shù)據(jù)結(jié)構(gòu)對(duì)象易于變化,經(jīng)常有新的數(shù)據(jù)對(duì)象增加進(jìn)來(lái),則不適合使用訪問(wèn)者模式日杈。訪問(wèn)者模式的優(yōu)點(diǎn)是增加操作很容易,因?yàn)樵黾硬僮饕馕吨黾有碌脑L問(wèn)者涨冀。訪問(wèn)者模式將有關(guān)行為集中到一個(gè)訪問(wèn)者對(duì)象中扁眯,其改變不影響系統(tǒng)數(shù)據(jù)結(jié)構(gòu)。其缺點(diǎn)就是增加新的數(shù)據(jù)結(jié)構(gòu)很困難〗┩蓿—— From 百科
簡(jiǎn)單來(lái)說(shuō),訪問(wèn)者模式就是一種分離對(duì)象數(shù)據(jù)結(jié)構(gòu)與行為的方法,通過(guò)這種分離痕檬,可達(dá)到為一個(gè)被訪問(wèn)者動(dòng)態(tài)添加新的操作而無(wú)需做其它的修改的效果。簡(jiǎn)單關(guān)系圖:
來(lái)看看原碼:一個(gè)Visitor類(lèi)唁桩,存放要訪問(wèn)的對(duì)象怔檩,
[java]view plaincopy
public?interface?Visitor?{??
public?void?visit(Subject?sub);??
}??
[java]view plaincopy
public?class?MyVisitor?implements?Visitor?{??
@Override??
public?void?visit(Subject?sub)?{??
System.out.println("visit?the?subject:"+sub.getSubject());??
????}??
}??
Subject類(lèi)滋捶,accept方法衣屏,接受將要訪問(wèn)它的對(duì)象膨疏,getSubject()獲取將要被訪問(wèn)的屬性,
[java]view plaincopy
public?interface?Subject?{??
public?void?accept(Visitor?visitor);??
public?String?getSubject();??
}??
[java]view plaincopy
public?class?MySubject?implements?Subject?{??
@Override??
public?void?accept(Visitor?visitor)?{??
visitor.visit(this);??
????}??
@Override??
public?String?getSubject()?{??
return?"love";??
????}??
}??
測(cè)試:
[java]view plaincopy
public?class?Test?{??
public?static?void?main(String[]?args)?{??
Visitor?visitor?=new?MyVisitor();??
Subject?sub?=new?MySubject();??
????????sub.accept(visitor);??????
????}??
}??
輸出:visit the subject:love
該模式適用場(chǎng)景:如果我們想為一個(gè)現(xiàn)有的類(lèi)增加新功能瘤泪,不得不考慮幾個(gè)事情:1赦邻、新功能會(huì)不會(huì)與現(xiàn)有功能出現(xiàn)兼容性問(wèn)題?2恬吕、以后會(huì)不會(huì)再需要添加侨颈?3哈垢、如果類(lèi)不允許修改代碼怎么辦?面對(duì)這些問(wèn)題央渣,最好的解決方法就是使用訪問(wèn)者模式,訪問(wèn)者模式適用于數(shù)據(jù)結(jié)構(gòu)相對(duì)穩(wěn)定的系統(tǒng)咕村,把數(shù)據(jù)結(jié)構(gòu)和算法解耦,
中介者模式也是用來(lái)降低類(lèi)類(lèi)之間的耦合的价匠,因?yàn)槿绻?lèi)類(lèi)之間有依賴(lài)關(guān)系的話,不利于功能的拓展和維護(hù),因?yàn)橹灰薷囊粋€(gè)對(duì)象,其它關(guān)聯(lián)的對(duì)象都得進(jìn)行修改伙狐。如果使用中介者模式艘虎,只需關(guān)心和Mediator類(lèi)的關(guān)系属划,具體類(lèi)類(lèi)之間的關(guān)系及調(diào)度交給Mediator就行,這有點(diǎn)像spring容器的作用。先看看圖:
User類(lèi)統(tǒng)一接口,User1和User2分別是不同的對(duì)象晤愧,二者之間有關(guān)聯(lián)烙丛,如果不采用中介者模式钠右,則需要二者相互持有引用,這樣二者的耦合度很高,為了解耦,引入了Mediator類(lèi)献酗,提供統(tǒng)一接口漓帅,MyMediator為其實(shí)現(xiàn)類(lèi)器予,里面持有User1和User2的實(shí)例,用來(lái)實(shí)現(xiàn)對(duì)User1和User2的控制萌丈。這樣User1和User2兩個(gè)對(duì)象相互獨(dú)立月劈,他們只需要保持好和Mediator之間的關(guān)系就行惭墓,剩下的全由MyMediator類(lèi)來(lái)維護(hù)毅人!基本實(shí)現(xiàn):
[java]view plaincopy
public?interface?Mediator?{??
public?void?createMediator();??
public?void?workAll();??
}??
[java]view plaincopy
public?class?MyMediator?implements?Mediator?{??
private?User?user1;??
private?User?user2;??
public?User?getUser1()?{??
return?user1;??
????}??
public?User?getUser2()?{??
return?user2;??
????}??
@Override??
public?void?createMediator()?{??
user1?=new?User1(this);??
user2?=new?User2(this);??
????}??
@Override??
public?void?workAll()?{??
????????user1.work();??
????????user2.work();??
????}??
}??
[java]view plaincopy
public?abstract?class?User?{??
private?Mediator?mediator;??
public?Mediator?getMediator(){??
return?mediator;??
????}??
public?User(Mediator?mediator)?{??
this.mediator?=?mediator;??
????}??
public?abstract?void?work();??
}??
[java]view plaincopy
public?class?User1?extends?User?{??
public?User1(Mediator?mediator){??
super(mediator);??
????}??
@Override??
public?void?work()?{??
System.out.println("user1?exe!");??
????}??
}??
[java]view plaincopy
public?class?User2?extends?User?{??
public?User2(Mediator?mediator){??
super(mediator);??
????}??
@Override??
public?void?work()?{??
System.out.println("user2?exe!");??
????}??
}??
測(cè)試類(lèi):
[java]view plaincopy
public?class?Test?{??
public?static?void?main(String[]?args)?{??
Mediator?mediator?=new?MyMediator();??
????????mediator.createMediator();??
????????mediator.workAll();??
????}??
}??
輸出:
user1 exe!
user2 exe!
解釋器模式是我們暫時(shí)的最后一講蟹略,一般主要應(yīng)用在OOP開(kāi)發(fā)中的編譯器的開(kāi)發(fā)中状婶,所以適用面比較窄草姻。
Context類(lèi)是一個(gè)上下文環(huán)境類(lèi),Plus和Minus分別是用來(lái)計(jì)算的實(shí)現(xiàn)澳迫,代碼如下:
[java]view plaincopy
public?interface?Expression?{??
public?int?interpret(Context?context);??
}??
[java]view plaincopy
public?class?Plus?implements?Expression?{??
@Override??
public?int?interpret(Context?context)?{??
return?context.getNum1()+context.getNum2();??
????}??
}??
[java]view plaincopy
public?class?Minus?implements?Expression?{??
@Override??
public?int?interpret(Context?context)?{??
return?context.getNum1()-context.getNum2();??
????}??
}??
[java]view plaincopy
public?class?Context?{??
private?int?num1;??
private?int?num2;??
public?Context(int?num1,?int?num2)?{??
this.num1?=?num1;??
this.num2?=?num2;??
????}??
public?int?getNum1()?{??
return?num1;??
????}??
public?void?setNum1(int?num1)?{??
this.num1?=?num1;??
????}??
public?int?getNum2()?{??
return?num2;??
????}??
public?void?setNum2(int?num2)?{??
this.num2?=?num2;??
????}??
}??
[java]view plaincopy
public?class?Test?{??
public?static?void?main(String[]?args)?{??
//?計(jì)算9+2-8的值??
int?result?=?new?Minus().interpret((new?Context(new?Plus()??
.interpret(new?Context(9,?2)),?8)));??
????????System.out.println(result);??
????}??
}??
最后輸出正確的結(jié)果:3。
基本就這樣,解釋器模式用來(lái)做各種各樣的解釋器,如正則表達(dá)式等的解釋器等等!