設(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)整體描述一下:
二窗市、Java的23中設(shè)計(jì)模式
從這一塊開(kāi)始,我們?cè)敿?xì)介紹Java中23種設(shè)計(jì)模式的概念饮笛,應(yīng)用場(chǎng)景等情況咨察,并結(jié)合他們的特點(diǎn)及設(shè)計(jì)模式的原則進(jìn)行分析。
1福青、工廠方法模式(Factory Method)
工廠方法模式分為三種:
1摄狱、普通工廠模式,
就是建立一個(gè)工廠類(lèi)无午,對(duì)實(shí)現(xiàn)了同一接口的一些類(lèi)進(jìn)行實(shí)例的創(chuàng)建媒役。首先看下關(guān)系圖:
舉例如下:(我們舉一個(gè)發(fā)送郵件和短信的例子)
首先,創(chuàng)建二者的共同接口:
public interface Sender {
public void Send();
}
//其次指厌,創(chuàng)建實(shí)現(xiàn)類(lèi):
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println(“this is mailsender!”);
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println(“this is sms sender!”);
}
}
//最后刊愚,建工廠類(lèi):
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!
2踊跟、多個(gè)工廠方法模式踩验,
是對(duì)普通工廠方法模式的改進(jìn),在普通工廠方法模式中商玫,如果傳遞的字符串出錯(cuò)箕憾,則不能正確創(chuàng)建對(duì)象,而多個(gè)工廠方法模式是提供多個(gè)工廠方法拳昌,分別創(chuàng)建對(duì)象袭异。關(guān)系圖:::IHACKLOG_REMOTE_IMAGE_AUTODOWN_BLOCK::2
將上面的代碼做下修改,改動(dòng)下SendFactory類(lèi)就行炬藤,如下:
public class SendFactory {
public Sender produceMail(){
return new MailSender();
}
public Sender produceSms(){
return new SmsSender();
}
}
//測(cè)試類(lèi)如下:
public class FactoryTest {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produceMail();
sender.Send();
}
}
輸出:this is mailsender!
33御铃、靜態(tài)工廠方法模式,
將上面的多個(gè)工廠方法模式里的方法置為靜態(tài)的沈矿,不需要?jiǎng)?chuàng)建實(shí)例上真,直接調(diào)用即可。
public class SendFactory {
public static Sender produceMail(){
return new MailSender();
}
public static Sender produceSms(){
return new SmsSender();
}
}
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)工廠方法模式冕广。
2、抽象工廠模式(Abstract Factory)
工廠方法模式有一個(gè)問(wèn)題就是偿洁,類(lèi)的創(chuàng)建依賴(lài)工廠類(lèi)撒汉,也就是說(shuō),如果想要拓展程序涕滋,必須對(duì)工廠類(lèi)進(jìn)行修改睬辐,這違背了閉包原則,所以宾肺,從設(shè)計(jì)角度考慮溯饵, 有一定的問(wèn)題,如何解決锨用?就用到抽象工廠模式丰刊,創(chuàng)建多個(gè)工廠類(lèi),這樣一旦需要增加新的功能增拥,直接增加新的工廠類(lèi)就可以了啄巧,不需要修改之前的代碼。因?yàn)槌橄? 工廠不太好理解掌栅,我們先看看圖秩仆,然后就和代碼,就比較容易理解猾封。
請(qǐng)看例子:
public interface Sender {
public void Send();
}
//兩個(gè)實(shí)現(xiàn)類(lèi):
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println(“this is mailsender!”);
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println(“this is sms sender!”);
}
}
//兩個(gè)工廠類(lèi):
public class SendMailFactory implements Provider {
@Override
public Sender produce(){
return new MailSender();
}
}
public class SendSmsFactory implements Provider{
@Override
public Sender produce() {
return new SmsSender();
}
}
//在提供一個(gè)接口:
public interface Provider {
public Sender produce();
}
//測(cè)試類(lèi):
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)成的代碼。這樣做搀捷,拓展性較好星掰!
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è)的話(huà),系統(tǒng)完全亂了钳榨。(比如一個(gè)軍隊(duì)出現(xiàn)了多個(gè)司令員同時(shí)指揮舰罚,肯定會(huì)亂成一團(tuán)),所以只有使用單例模式薛耻,才能保證核心交易服務(wù)器獨(dú)立控制整個(gè)流程营罢。
首先我們寫(xiě)一個(gè)簡(jiǎn)單的單例類(lèi):
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)可以滿(mǎn)足基本要求杂瘸,但是倒淫,像這樣毫無(wú)線程安全保護(hù)的類(lèi),
如果我們把它放入多線程的環(huán)境下败玉,肯定就會(huì)出現(xiàn)問(wèn)題了敌土,如何解決?
我們首先會(huì)想到對(duì)getInstance方法加synchronized關(guān)鍵字运翼,如下:*/
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è):*/
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)化:
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è)完美的單例模式:
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)鍵字,也是可以的:*/
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;
}
}
考慮性能的話(huà)际度,整個(gè)程序只需創(chuàng)建一次實(shí)例葵袭,所以性能也不會(huì)有什么影響。
補(bǔ)充:采用****”****影子實(shí)例****”****的辦法為單例對(duì)象的屬性同步更新
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)辽俗,只要滿(mǎn)足單例的基本需求,你可以在里面隨心所欲的實(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)
工廠類(lèi)模式提供的是創(chuàng)建單個(gè)類(lèi)的模式,而建造者模式則是將各種產(chǎn)品集中起來(lái)進(jìn)行管理僵娃,用來(lái)創(chuàng)建復(fù)合對(duì)象羡藐,所謂復(fù)合對(duì)象就是指某個(gè)類(lèi)具有不同的屬性,其實(shí)建造者模式就是前面抽象工廠模式和最后的Test結(jié)合起來(lái)得到的悯许。我們看一下代碼:
還和前面一樣仆嗦,一個(gè)Sender接口,兩個(gè)實(shí)現(xiàn)類(lèi)MailSender和SmsSender先壕。最后瘩扼,建造者類(lèi)如下:
public class Builder {
private List<Sender> list = new ArrayList<Sender>();
public void produceMailSender(int count){
for(int i=0; i<count; i++){
list.add(new MailSender());
}
}
public void produceSmsSender(int count){
for(int i=0; i<count; i++){
list.add(new SmsSender());
}
}
}
//測(cè)試類(lèi):
public class Test {
public static void main(String[] args) {
Builder builder = new Builder();
builder.produceMailSender(10);
}
}
從這點(diǎn)看出,建造者模式將很多功能集成到一個(gè)類(lèi)里垃僚,這個(gè)類(lèi)可以創(chuàng)造出比較復(fù)雜的東西集绰。所以與工程模式的區(qū)別就是:工廠模式關(guān)注的是創(chuàng)建單個(gè)產(chǎn)品,而建造者模式則關(guān)注創(chuàng)建符合對(duì)象谆棺,多個(gè)部分栽燕。因此,是選擇工廠模式還是建造者模式改淑,依實(shí)際情況而定碍岔。
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):
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()這句 話(huà),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ù)制的例子:
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ì)象鬼癣。
七種結(jié)構(gòu)型模式:適配器模式、裝飾模式啤贩、代理模式待秃、外觀模式、橋接模式痹屹、組合模式章郁、享元模式。
6適配器模式
適配器模式將某個(gè)類(lèi)的接口轉(zhuǎn)換成客戶(hù)端期望的另一個(gè)接口表示志衍,目的是消除由于接口不匹配所造成的類(lèi)的兼容性問(wèn)題暖庄。主要分為三類(lèi):類(lèi)的適配器模式、對(duì)象的適配器模式楼肪、接口的適配器模式培廓。首先,我們來(lái)看看類(lèi)的適配器模式春叫,先看類(lèi)圖:
6.1類(lèi)的適配器模式
核心思想就是:有一個(gè)Source類(lèi)肩钠,擁有一個(gè)方法泣港,待適配,目標(biāo)接口時(shí)Targetable价匠,通過(guò)Adapter類(lèi)当纱,將Source的功能擴(kuò)展到Targetable里,看代碼:
public class Source {
public void method1() {
System.out.println(“this is original method!”);
}
}
public interface Targetable {
/* 與原類(lèi)中的方法相同 */
public void method1();
/* 新類(lèi)的方法 */
public void method2();
}
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):
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)的功能。
6.2對(duì)象的適配器模式
基本思路和類(lèi)的適配器模式相同洋腮,只是將Adapter類(lèi)作修改箫柳,這次不繼承Source類(lèi),而是持有Source類(lèi)的實(shí)例徐矩,以達(dá)到解決兼容性的問(wèn)題滞时。看圖:
只需要修改Adapter類(lèi)的源碼即可:
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):
public class AdapterTest {
public static void main(String[] args) {
Source source = new Source();
Targetable target = new Wrapper(source);
target.method1();
target.method2();
}
}
輸出與第一種一樣滤灯,只是適配的方法不同而已坪稽。
6.3接口的適配器模式
第三種適配器模式是接口的適配器模式,接口的適配器是這樣的:有時(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)中并不是都需要⊙蹋看代碼:
public interface Sourceable {
public void method1();
public void method2();
}
//抽象類(lèi)Wrapper2:
public abstract class Wrapper2 implements Sourceable{
public void method1(){}
public void method2(){}
}
public class SourceSub1 extends Wrapper2 {
public void method1(){
System.out.println(“the sourceable interface’s first Sub1!”);
}
}
public class SourceSub2 extends Wrapper2 {
public void method2(){
System.out.println(“the sourceable interface’s second Sub2!”);
}
}
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)換成滿(mǎn)足另一個(gè)新接口的類(lèi)時(shí)贯吓,可以使用類(lèi)的適配器模式懈凹,創(chuàng)建一個(gè)新類(lèi),繼承原有的類(lèi)悄谐,實(shí)現(xiàn)新的接口即可介评。
對(duì)象的適配器模式:當(dāng)希望將一個(gè)對(duì)象轉(zhuǎn)換成滿(mǎ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)的添加一些功能,代碼如下:
public interface Sourceable {
public void method();
}
public class Source implements Sourceable {
@Override
public void method() {
System.out.println(“the original method!”);
}
}
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):
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ù)上文的闡述,代理模式就比較容易的理解了划煮,我們看下代碼:
public interface Sourceable {
public void method();
}
public class Source implements Sourceable {
@Override
public void method() {
System.out.println(“the original method!”);
}
}
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):
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):
public class CPU {
public void startup(){
System.out.println(“cpu startup!”);
}
public void shutdown(){
System.out.println(“cpu shutdown!”);
}
}
public class Memory {
public void startup(){
System.out.println(“memory startup!”);
}
public void shutdown(){
System.out.println(“memory shutdown!”);
}
}
public class Disk {
public void startup(){
System.out.println(“disk startup!”);
}
public void shutdown(){
System.out.println(“disk shutdown!”);
}
}
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)如下:
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)代碼:
先定義接口:
- public interface Sourceable {
- public void method();
- }
分別定義兩個(gè)實(shí)現(xiàn)類(lèi):
public class SourceSub1 implements Sourceable {
@Override
public void method() {
System.out.println(“this is the first sub!”);
}
}
public class SourceSub2 implements Sourceable {
@Override
public void method() {
System.out.println(“this is the second sub!”);
}
}
定義一個(gè)橋,持有Sourceable的一個(gè)實(shí)例:
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;
}
}
public class MyBridge extends Bridge {
public void method(){
getSource().method();
}
}
測(cè)試類(lèi):
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)看代碼:
public class TreeNode {
private String name;
private TreeNode parent;
private Vector<TreeNode> children = new Vector<TreeNode>();
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<TreeNode> getChildren(){
return children.elements();
}
}
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è)客戶(hù)端請(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ù)連接池的代碼:
public class ConnectionPool {
private Vector<Connection> 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<Connection>(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)的性能!本章講解了7種結(jié)構(gòu)型模式分唾,因?yàn)槠膯?wèn)題抗碰,剩下的11種行為型模式,
本章是關(guān)于設(shè)計(jì)模式的最后一講绽乔,會(huì)講到第三種設(shè)計(jì)模式——行為型模式弧蝇,共11種:策略模式、模板方法模式、觀察者模式看疗、迭代子模式沙峻、責(zé)任鏈模式、命 令模式两芳、備忘錄模式摔寨、狀態(tài)模式、訪問(wèn)者模式怖辆、中介者模式是复、解釋器模式。這段時(shí)間一直在寫(xiě)關(guān)于設(shè)計(jì)模式的東西竖螃,終于寫(xiě)到一半了淑廊,寫(xiě)博文是個(gè)很費(fèi)時(shí)間的東西, 因?yàn)槲业脼樽x者負(fù)責(zé)斑鼻,不論是圖還是代碼還是表述蒋纬,都希望能盡量寫(xiě)清楚,以便讀者理解坚弱,我想不論是我還是讀者,都希望看到高質(zhì)量的博文出來(lái)关摇,從我本人出發(fā)荒叶, 我會(huì)一直堅(jiān)持下去,不斷更新输虱,源源動(dòng)力來(lái)自于讀者朋友們的不斷支持些楣,我會(huì)盡自己的努力,寫(xiě)好每一篇文章宪睹!希望大家能不斷給出意見(jiàn)和建議愁茁,共同打造完美的博 文!
先來(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)
13促煮、策略模式(strategy)
策略模式定義了一系列算法,并將每個(gè)算法封裝起來(lái)整袁,使他們可以相互替換菠齿,且算法的變化不會(huì)影響到使用算法的客戶(hù)。需要設(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)一接口:
- public interface ICalculator {
- public int calculate(String exp);
- }
輔助類(lèi):
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):
public class Plus extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp,”\+”);
return arrayInt[0]+arrayInt[1];
}
}
public class Minus extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp,”-“);
return arrayInt[0]-arrayInt[1];
}
}
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):
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)在用戶(hù)嗜历,系統(tǒng)本身提供不同算法的實(shí)現(xiàn)宣渗,新增或者刪除算法,對(duì)各種算法做封裝梨州。因此痕囱,策略模式多用在算法決策系統(tǒng)中,外部用戶(hù)只需要決定用哪個(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)用充边,看下面的例子:
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;
}
}
public class Plus extends AbstractCalculator {
@Override
public int calculate(int num1,int num2) {
return num1 + num2;
}
}
測(cè)試類(lèi):
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)頭的思路。
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)講就一句話(huà):當(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接口:
- public interface Observer {
- public void update();
- }
兩個(gè)實(shí)現(xiàn)類(lèi):
public class Observer1 implements Observer {
@Override
public void update() {
System.out.println(“observer1 has received!”);
}
}
public class Observer2 implements Observer {
@Override
public void update() {
System.out.println(“observer2 has received!”);
}
}
Subject接口及實(shí)現(xiàn)類(lèi):
public interface Subject {
/增加觀察者/
public void add(Observer observer);
/刪除觀察者/
public void del(Observer observer);
/通知所有的觀察者/
public void notifyObservers();
/自身的操作/
public void operation();
}
public abstract class AbstractSubject implements Subject {
private Vector<Observer> vector = new Vector<Observer>();
@Override
public void add(Observer observer) {
vector.add(observer);
}
@Override
public void del(Observer observer) {
vector.remove(observer);
}
@Override
public void notifyObservers() {
Enumeration<Observer> enumo = vector.elements();
while(enumo.hasMoreElements()){
enumo.nextElement().update();
}
}
}
public class MySubject extends AbstractSubject {
@Override
public void operation() {
System.out.println(“update self!”);
notifyObservers();
}
}
測(cè)試類(lèi):
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à),理解本模式會(huì)十分輕松甩挫。這句話(huà)包含兩層意思:一是需要遍歷的對(duì)象贴硫,即聚集對(duì)象,二是迭代器對(duì)象伊者,用于對(duì)聚集對(duì)象進(jìn)行遍歷訪問(wèn)英遭。我們看下關(guān)系圖:
這個(gè)思路和我們常用的一模一樣,MyCollection中定義了集合的一些操作删壮,MyIterator中定義了一系列迭代操作贪绘,且持有Collection實(shí)例,我們來(lái)看看實(shí)現(xiàn)代碼:
兩個(gè)接口:
public interface Collection {
public Iterator iterator();
/取得集合元素/
public Object get(int i);
/取得集合大小/
public int size();
}
public interface Iterator {
//前移
public Object previous();
//后移
public Object next();
public boolean hasNext();
//取得第一個(gè)元素
public Object first();
}
兩個(gè)實(shí)現(xiàn):
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;
}
}
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<collection.size()-1){
pos++;
}
return collection.get(pos);
}
@Override
public boolean hasNext() {
if(pos<collection.size()-1){
return true;
}else{
return false;
}
}
@Override
public Object first() {
pos = 0;
return collection.get(pos);
}
}
測(cè)試類(lèi):
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)策吠,在隱瞞客戶(hù)端的情況下逛裤,對(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)成一條鏈炉菲。
public interface Handler {
public void operator();
}
public abstract class AbstractHandler {
private Handler handler;
public Handler getHandler() {
return handler;
}
public void setHandler(Handler handler) {
this.handler = handler;
}
}
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();
}
}
}
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)代碼:
public interface Command {
public void exe();
}
public class MyCommand implements Command {
private Receiver receiver;
public MyCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void exe() {
receiver.action();
}
}
public class Receiver {
public void action(){
System.out.println(“command received!”);
}
}
public class Invoker {
private Command command;
public Invoker(Command command) {
this.command = command;
}
public void action(){
command.exe();
}
}
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ě)清楚了忽媒,更好讓大家明白。本章不出意外的話(huà)腋粥,應(yīng)該是設(shè)計(jì)模式最后一講了晦雨,首先還是上一下上篇開(kāi)頭的那 個(gè)圖:
本章講講第三類(lèi)和第四類(lè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í)例,該模式很好理解曲聂。直接看源碼:
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();
}
}
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;
}
}
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):
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)看看代碼:
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!”);
}
}
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):
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)限控制等健田。
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ì)象,
public interface Visitor {
public void visit(Subject sub);
}
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)的屬性句狼,
public interface Subject {
public void accept(Visitor visitor);
public String getSubject();
}
public class MySubject implements Subject {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public String getSubject() {
return “l(fā)ove”;
}
}
測(cè)試:
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)系的話(huà),不利于功能的拓展和維護(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):
public interface Mediator {
public void createMediator();
public void workAll();
}
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();
}
}
public abstract class User {
private Mediator mediator;
public Mediator getMediator(){
return mediator;
}
public User(Mediator mediator) {
this.mediator = mediator;
}
public abstract void work();
}
public class User1 extends User {
public User1(Mediator mediator){
super(mediator);
}
@Override
public void work() {
System.out.println(“user1 exe!”);
}
}
public class User2 extends User {
public User2(Mediator mediator){
super(mediator);
}
@Override
public void work() {
System.out.println(“user2 exe!”);
}
}
測(cè)試類(lèi):
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),代碼如下:
public interface Expression {
public int interpret(Context context);
}
public class Plus implements Expression {
@Override
public int interpret(Context context) {
return context.getNum1()+context.getNum2();
}
}
public class Minus implements Expression {
@Override
public int interpret(Context context) {
return context.getNum1()-context.getNum2();
}
}
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;
}
}
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