Java中23種設(shè)計(jì)模式詳解

文章部分內(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

二泌神、設(shè)計(jì)模式的六大原則

總原則:開(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)。

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

不要存在多于一個(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)

原則是盡量首先使用合成/聚合的方式凉夯,而不是使用繼承。

三采幌、Java的23中設(shè)計(jì)模式

A劲够、創(chuàng)建模式

從這一塊開(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)單工廠凫岖。

0、簡(jiǎn)單工廠模式

簡(jiǎn)單工廠模式模式分為三種:

01逢净、普通

就是建立一個(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!

02着饥、多個(gè)方法

是對(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!

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

將上面的多個(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)工廠方法模式。

1往史、工廠方法模式(Factory Method)

簡(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)成的代碼彤蔽。這樣做,拓展性較好庙洼!

2顿痪、抽象工廠模式

工廠方法模式和抽象工廠模式不好分清楚,他們的區(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)線

3阅虫、單例模式(Singleton)

單例對(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)題卒暂!

4啄栓、建造者模式(Builder)

5、原型模式(Prototype)

原型模式雖然是創(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ì)象。

B妓笙、結(jié)構(gòu)模式(7種)

我們接著討論設(shè)計(jì)模式若河,上篇文章我講完了5種創(chuàng)建型模式,這章開(kāi)始寞宫,我將講下7種結(jié)構(gòu)型模式:適配器模式萧福、裝飾模式、代理模式辈赋、外觀模式鲫忍、橋接模式、組合模式钥屈、享元模式悟民。其中對(duì)象的適配器模式是各種模式的起源,我們看下面的圖:

6篷就、適配器模式

?適配器模式將某個(gè)類(lèi)的接口轉(zhuǎn)換成客戶端期望的另一個(gè)接口表示射亏,目的是消除由于接口不匹配所造成的類(lèi)的兼容性問(wèn)題。主要分為三類(lèi):類(lèi)的適配器模式竭业、對(duì)象的適配器模式智润、接口的適配器模式。

01未辆、類(lèi)的適配器模式

核心思想就是:有一個(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)的功能献宫。

02钥平、對(duì)象的適配器模式

基本思路和類(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();??

????}??

}??

輸出與第一種一樣负敏,只是適配的方法不同而已贡茅。

03、接口的適配器模式

第三種適配器模式是接口的適配器模式,接口的適配器是這樣的:有時(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)即可渣聚。

7、裝飾模式(Decorator)

顧名思義僧叉,裝飾模式就是給一個(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ò)!

8盖喷、代理模式(Proxy)

其實(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ù)锌蓄!

9升筏、外觀模式(Facade)

外觀模式是為了解決類(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)里多糠,這樣就起到了解耦的作用,這浩考,就是外觀模式夹孔!

10、橋接模式(Bridge)

橋接模式就是把事物和其具體實(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é)合就都懂了玫坛。

11、組合模式(Composite)

組合模式有時(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)的性能笔诵!


?C返吻、關(guān)系模式(11種)

先來(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)

父類(lèi)與子類(lèi)關(guān)系

13谢翎、策略模式(strategy)

策略模式定義了一系列算法捍靠,并將每個(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è)算法即可。

14葡秒、模板方法模式(Template Method)

解釋一下模板方法模式姻乓,就是指:一個(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)頭的思路。

類(lèi)之間的關(guān)系

15雹锣、觀察者模式(Observer)

包括這個(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)容易!

16紫谷、迭代子模式(Iterator)

顧名思義齐饮,迭代器模式就是順序訪問(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ì)象空繁。

?18殿衰、命令模式(Command)

命令模式很好理解,舉個(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)虚倒。

類(lèi)的狀態(tài)

19、備忘錄模式(Memento)

主要目的是保存一個(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ù)”模式最形象心例。

20宵凌、狀態(tài)模式(State)

核心思想就是:當(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)限控制等摄乒。

通過(guò)中間類(lèi)

21、訪問(wèn)者模式(Visitor)

訪問(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)和算法解耦,

22、中介者模式(Mediator)

中介者模式也是用來(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!

23缔俄、解釋器模式(Interpreter)

解釋器模式是我們暫時(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á)式等的解釋器等等!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吧史,老刑警劉巖及塘,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異翎迁,居然都是意外死亡肃拜,警方通過(guò)查閱死者的電腦和手機(jī)士聪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)区岗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)纠炮。 經(jīng)常有香客問(wèn)我灯蝴,道長(zhǎng)恢口,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任穷躁,我火速辦了婚禮,結(jié)果婚禮上问潭,老公的妹妹穿的比我還像新娘猿诸。我一直安慰自己,他們只是感情好狡忙,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布两芳。 她就那樣靜靜地躺著,像睡著了一般去枷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上是复,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天删顶,我揣著相機(jī)與錄音,去河邊找鬼淑廊。 笑死逗余,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的季惩。 我是一名探鬼主播录粱,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼画拾!你這毒婦竟也來(lái)了啥繁?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤青抛,失蹤者是張志新(化名)和其女友劉穎旗闽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蜜另,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡适室,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了举瑰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捣辆。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖此迅,靈堂內(nèi)的尸體忽然破棺而出汽畴,到底是詐尸還是另有隱情旧巾,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布整袁,位于F島的核電站菠齿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏坐昙。R本人自食惡果不足惜绳匀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望炸客。 院中可真熱鬧疾棵,春花似錦、人聲如沸痹仙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)开仰。三九已至拟枚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間众弓,已是汗流浹背恩溅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谓娃,地道東北人脚乡。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像滨达,于是被迫代替她去往敵國(guó)和親奶稠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345