Java語(yǔ)言——設(shè)計(jì)模式

設(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)整體描述一下:


圖片.png

二窗市、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)系圖:


圖片.png

舉例如下:(我們舉一個(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)系圖:
圖片.png

::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)槌橄? 工廠不太好理解掌栅,我們先看看圖秩仆,然后就和代碼,就比較容易理解猾封。

圖片.png

請(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)的適配器模式

圖片.png

核心思想就是:有一個(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)題滞时。看圖:

圖片.png

只需要修改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)圖:

圖片.png

這個(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)系圖如下:

圖片.png

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)系圖:

圖片.png

根據(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ò)程為例)

圖片.png

我們先看下實(shí)現(xiàn)類(lèi):

  1. public class CPU {

  2. public void startup(){

  3. System.out.println(“cpu startup!”);

  4. }

  5. public void shutdown(){

  6. System.out.println(“cpu shutdown!”);

  7. }

  8. }

  9. public class Memory {

  10. public void startup(){

  11. System.out.println(“memory startup!”);

  12. }

  13. public void shutdown(){

  14. System.out.println(“memory shutdown!”);

  15. }

  16. }

  17. public class Disk {

  18. public void startup(){

  19. System.out.println(“disk startup!”);

  20. }

  21. public void shutdown(){

  22. System.out.println(“disk shutdown!”);

  23. }

  24. }

  25. public class Computer {

  26. private CPU cpu;

  27. private Memory memory;

  28. private Disk disk;

  29. public Computer(){

  30. cpu = new CPU();

  31. memory = new Memory();

  32. disk = new Disk();

  33. }

  34. public void startup(){

  35. System.out.println(“start the computer!”);

  36. cpu.startup();

  37. memory.startup();

  38. disk.startup();

  39. System.out.println(“start computer finished!”);

  40. }

  41. public void shutdown(){

  42. System.out.println(“begin to close the computer!”);

  43. cpu.shutdown();

  44. memory.shutdown();

  45. disk.shutdown();

  46. System.out.println(“computer closed!”);

  47. }

  48. }

User類(lèi)如下:

  1. public class User {

  2. public static void main(String[] args) {

  3. Computer computer = new Computer();

  4. computer.startup();

  5. computer.shutdown();

  6. }

  7. }

輸出:

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)系圖:

圖片.png

實(shí)現(xiàn)代碼:

先定義接口:

  1. public interface Sourceable {
  2. public void method();
  3. }

分別定義兩個(gè)實(shí)現(xiàn)類(lèi):

  1. public class SourceSub1 implements Sourceable {

  2. @Override

  3. public void method() {

  4. System.out.println(“this is the first sub!”);

  5. }

  6. }

  7. public class SourceSub2 implements Sourceable {

  8. @Override

  9. public void method() {

  10. System.out.println(“this is the second sub!”);

  11. }

  12. }

定義一個(gè)橋,持有Sourceable的一個(gè)實(shí)例:

  1. public abstract class Bridge {

  2. private Sourceable source;

  3. public void method(){

  4. source.method();

  5. }

  6. public Sourceable getSource() {

  7. return source;

  8. }

  9. public void setSource(Sourceable source) {

  10. this.source = source;

  11. }

  12. }

  13. public class MyBridge extends Bridge {

  14. public void method(){

  15. getSource().method();

  16. }

  17. }

測(cè)試類(lèi):

  1. public class BridgeTest {

  2. public static void main(String[] args) {

  3. Bridge bridge = new MyBridge();

  4. /調(diào)用第一個(gè)對(duì)象/

  5. Sourceable source1 = new SourceSub1();

  6. bridge.setSource(source1);

  7. bridge.method();

  8. /調(diào)用第二個(gè)對(duì)象/

  9. Sourceable source2 = new SourceSub2();

  10. bridge.setSource(source2);

  11. bridge.method();

  12. }

  13. }

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é)合就都懂了锐极。


圖片.png

11、組合模式(Composite)

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

圖片.png

直接來(lái)看代碼:

  1. public class TreeNode {

  2. private String name;

  3. private TreeNode parent;

  4. private Vector<TreeNode> children = new Vector<TreeNode>();

  5. public TreeNode(String name){

  6. this.name = name;

  7. }

  8. public String getName() {

  9. return name;

  10. }

  11. public void setName(String name) {

  12. this.name = name;

  13. }

  14. public TreeNode getParent() {

  15. return parent;

  16. }

  17. public void setParent(TreeNode parent) {

  18. this.parent = parent;

  19. }

  20. //添加孩子節(jié)點(diǎn)

  21. public void add(TreeNode node){

  22. children.add(node);

  23. }

  24. //刪除孩子節(jié)點(diǎn)

  25. public void remove(TreeNode node){

  26. children.remove(node);

  27. }

  28. //取得孩子節(jié)點(diǎn)

  29. public Enumeration<TreeNode> getChildren(){

  30. return children.elements();

  31. }

  32. }

  33. public class Tree {

  34. TreeNode root = null;

  35. public Tree(String name) {

  36. root = new TreeNode(name);

  37. }

  38. public static void main(String[] args) {

  39. Tree tree = new Tree(“A”);

  40. TreeNode nodeB = new TreeNode(“B”);

  41. TreeNode nodeC = new TreeNode(“C”);

  42. nodeB.add(nodeC);

  43. tree.root.add(nodeB);

  44. System.out.println(“build the tree finished!”);

  45. }

  46. }

使用場(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),通常與工廠模式一起使用罕拂。

圖片.png

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è)例子:

圖片.png

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

  1. public class ConnectionPool {

  2. private Vector<Connection> pool;

  3. /公有屬性/

  4. private String url = “jdbc:mysql://localhost:3306/test”;

  5. private String username = “root”;

  6. private String password = “root”;

  7. private String driverClassName = “com.mysql.jdbc.Driver”;

  8. private int poolSize = 100;

  9. private static ConnectionPool instance = null;

  10. Connection conn = null;

  11. /構(gòu)造方法广匙,做一些初始化工作/

  12. private ConnectionPool() {

  13. pool = new Vector<Connection>(poolSize);

  14. for (int i = 0; i < poolSize; i++) {

  15. try {

  16. Class.forName(driverClassName);

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

  18. pool.add(conn);

  19. } catch (ClassNotFoundException e) {

  20. e.printStackTrace();

  21. } catch (SQLException e) {

  22. e.printStackTrace();

  23. }

  24. }

  25. }

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

  27. public synchronized void release() {

  28. pool.add(conn);

  29. }

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

  31. public synchronized Connection getConnection() {

  32. if (pool.size() > 0) {

  33. Connection conn = pool.get(0);

  34. pool.remove(conn);

  35. return conn;

  36. } else {

  37. return null;

  38. }

  39. }

  40. }

通過(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)

圖片.png

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)系圖如下:

圖片.png

圖中ICalculator提供同意的方法, AbstractCalculator是輔助類(lèi)陋桂,提供輔助方法逆趣,接下來(lái),依次實(shí)現(xiàn)下每個(gè)類(lèi):

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

  1. public interface ICalculator {
  2. public int calculate(String exp);
  3. }

輔助類(lèi):

  1. public abstract class AbstractCalculator {

  2. public int[] split(String exp,String opt){

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

  4. int arrayInt[] = new int[2];

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

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

  7. return arrayInt;

  8. }

  9. }

三個(gè)實(shí)現(xiàn)類(lèi):

  1. public class Plus extends AbstractCalculator implements ICalculator {

  2. @Override

  3. public int calculate(String exp) {

  4. int arrayInt[] = split(exp,”\+”);

  5. return arrayInt[0]+arrayInt[1];

  6. }

  7. }

  8. public class Minus extends AbstractCalculator implements ICalculator {

  9. @Override

  10. public int calculate(String exp) {

  11. int arrayInt[] = split(exp,”-“);

  12. return arrayInt[0]-arrayInt[1];

  13. }

  14. }

  15. public class Multiply extends AbstractCalculator implements ICalculator {

  16. @Override

  17. public int calculate(String exp) {

  18. int arrayInt[] = split(exp,”\*”);

  19. return arrayInt[0]*arrayInt[1];

  20. }

  21. }

簡(jiǎn)單的測(cè)試類(lèi):

  1. public class StrategyTest {

  2. public static void main(String[] args) {

  3. String exp = “2+8″;

  4. ICalculator cal = new Plus();

  5. int result = cal.calculate(exp);

  6. System.out.println(result);

  7. }

  8. }

輸出: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)系圖:

圖片.png

就是在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)用充边,看下面的例子:

  1. public abstract class AbstractCalculator {

  2. /主方法,實(shí)現(xiàn)對(duì)本類(lèi)其它方法的調(diào)用/

  3. public final int calculate(String exp,String opt){

  4. int array[] = split(exp,opt);

  5. return calculate(array[0],array[1]);

  6. }

  7. /被子類(lèi)重寫(xiě)的方法/

  8. abstract public int calculate(int num1,int num2);

  9. public int[] split(String exp,String opt){

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

  11. int arrayInt[] = new int[2];

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

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

  14. return arrayInt;

  15. }

  16. }

  17. public class Plus extends AbstractCalculator {

  18. @Override

  19. public int calculate(int num1,int num2) {

  20. return num1 + num2;

  21. }

  22. }

測(cè)試類(lèi):

  1. public class StrategyTest {

  2. public static void main(String[] args) {

  3. String exp = “8+8″;

  4. AbstractCalculator cal = new Plus();

  5. int result = cal.calculate(exp, “\+”);

  6. System.out.println(result);

  7. }

  8. }

我跟蹤下這個(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)系圖:

圖片.png

我解釋下這些類(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接口:

  1. public interface Observer {
  2. public void update();
  3. }

兩個(gè)實(shí)現(xiàn)類(lèi):

  1. public class Observer1 implements Observer {

  2. @Override

  3. public void update() {

  4. System.out.println(“observer1 has received!”);

  5. }

  6. }

  7. public class Observer2 implements Observer {

  8. @Override

  9. public void update() {

  10. System.out.println(“observer2 has received!”);

  11. }

  12. }

Subject接口及實(shí)現(xiàn)類(lèi):

  1. public interface Subject {

  2. /增加觀察者/

  3. public void add(Observer observer);

  4. /刪除觀察者/

  5. public void del(Observer observer);

  6. /通知所有的觀察者/

  7. public void notifyObservers();

  8. /自身的操作/

  9. public void operation();

  10. }

  11. public abstract class AbstractSubject implements Subject {

  12. private Vector<Observer> vector = new Vector<Observer>();

  13. @Override

  14. public void add(Observer observer) {

  15. vector.add(observer);

  16. }

  17. @Override

  18. public void del(Observer observer) {

  19. vector.remove(observer);

  20. }

  21. @Override

  22. public void notifyObservers() {

  23. Enumeration<Observer> enumo = vector.elements();

  24. while(enumo.hasMoreElements()){

  25. enumo.nextElement().update();

  26. }

  27. }

  28. }

  29. public class MySubject extends AbstractSubject {

  30. @Override

  31. public void operation() {

  32. System.out.println(“update self!”);

  33. notifyObservers();

  34. }

  35. }

測(cè)試類(lèi):

  1. public class ObserverTest {

  2. public static void main(String[] args) {

  3. Subject sub = new MySubject();

  4. sub.add(new Observer1());

  5. sub.add(new Observer2());

  6. sub.operation();

  7. }

  8. }

輸出:

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)系圖:


圖片.png

這個(gè)思路和我們常用的一模一樣,MyCollection中定義了集合的一些操作删壮,MyIterator中定義了一系列迭代操作贪绘,且持有Collection實(shí)例,我們來(lái)看看實(shí)現(xiàn)代碼:

兩個(gè)接口:

  1. public interface Collection {

  2. public Iterator iterator();

  3. /取得集合元素/

  4. public Object get(int i);

  5. /取得集合大小/

  6. public int size();

  7. }

  8. public interface Iterator {

  9. //前移

  10. public Object previous();

  11. //后移

  12. public Object next();

  13. public boolean hasNext();

  14. //取得第一個(gè)元素

  15. public Object first();

  16. }

兩個(gè)實(shí)現(xiàn):

  1. public class MyCollection implements Collection {

  2. public String string[] = {“A”,”B”,”C”,”D”,”E”};

  3. @Override

  4. public Iterator iterator() {

  5. return new MyIterator(this);

  6. }

  7. @Override

  8. public Object get(int i) {

  9. return string[i];

  10. }

  11. @Override

  12. public int size() {

  13. return string.length;

  14. }

  15. }

  16. public class MyIterator implements Iterator {

  17. private Collection collection;

  18. private int pos = -1;

  19. public MyIterator(Collection collection){

  20. this.collection = collection;

  21. }

  22. @Override

  23. public Object previous() {

  24. if(pos > 0){

  25. pos–;

  26. }

  27. return collection.get(pos);

  28. }

  29. @Override

  30. public Object next() {

  31. if(pos<collection.size()-1){

  32. pos++;

  33. }

  34. return collection.get(pos);

  35. }

  36. @Override

  37. public boolean hasNext() {

  38. if(pos<collection.size()-1){

  39. return true;

  40. }else{

  41. return false;

  42. }

  43. }

  44. @Override

  45. public Object first() {

  46. pos = 0;

  47. return collection.get(pos);

  48. }

  49. }

測(cè)試類(lèi):

  1. public class Test {

  2. public static void main(String[] args) {

  3. Collection collection = new MyCollection();

  4. Iterator it = collection.iterator();

  5. while(it.hasNext()){

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

  7. }

  8. }

  9. }

輸出: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)系圖:


圖片.png

Abstracthandler類(lèi)提供了get和set方法猴抹,方便MyHandle類(lèi)設(shè)置和修改引用對(duì)象带族,MyHandle類(lèi)是核心,實(shí)例化后生成一系列相互持有的對(duì)象洽糟,構(gòu)成一條鏈炉菲。

  1. public interface Handler {

  2. public void operator();

  3. }

  4. public abstract class AbstractHandler {

  5. private Handler handler;

  6. public Handler getHandler() {

  7. return handler;

  8. }

  9. public void setHandler(Handler handler) {

  10. this.handler = handler;

  11. }

  12. }

  13. public class MyHandler extends AbstractHandler implements Handler {

  14. private String name;

  15. public MyHandler(String name) {

  16. this.name = name;

  17. }

  18. @Override

  19. public void operator() {

  20. System.out.println(name+”deal!”);

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

  22. getHandler().operator();

  23. }

  24. }

  25. }

  26. public class Test {

  27. public static void main(String[] args) {

  28. MyHandler h1 = new MyHandler(“h1″);

  29. MyHandler h2 = new MyHandler(“h2″);

  30. MyHandler h3 = new MyHandler(“h3″);

  31. h1.setHandler(h2);

  32. h2.setHandler(h3);

  33. h1.operator();

  34. }

  35. }

輸出:

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)系圖:

圖片.png

Invoker是調(diào)用者(司令員)姑宽,Receiver是被調(diào)用者(士兵)速种,MyCommand是命令,實(shí)現(xiàn)了Command接口低千,持有接收對(duì)象,看實(shí)現(xiàn)代碼:

  1. public interface Command {

  2. public void exe();

  3. }

  4. public class MyCommand implements Command {

  5. private Receiver receiver;

  6. public MyCommand(Receiver receiver) {

  7. this.receiver = receiver;

  8. }

  9. @Override

  10. public void exe() {

  11. receiver.action();

  12. }

  13. }

  14. public class Receiver {

  15. public void action(){

  16. System.out.println(“command received!”);

  17. }

  18. }

  19. public class Invoker {

  20. private Command command;

  21. public Invoker(Command command) {

  22. this.command = command;

  23. }

  24. public void action(){

  25. command.exe();

  26. }

  27. }

  28. public class Test {

  29. public static void main(String[] args) {

  30. Receiver receiver = new Receiver();

  31. Command cmd = new MyCommand(receiver);

  32. Invoker invoker = new Invoker(cmd);

  33. invoker.action();

  34. }

  35. }

輸出: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è)圖:

圖片.png

本章講講第三類(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)分析一下:

圖片.png

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í)例,該模式很好理解曲聂。直接看源碼:

  1. public class Original {

  2. private String value;

  3. public String getValue() {

  4. return value;

  5. }

  6. public void setValue(String value) {

  7. this.value = value;

  8. }

  9. public Original(String value) {

  10. this.value = value;

  11. }

  12. public Memento createMemento(){

  13. return new Memento(value);

  14. }

  15. public void restoreMemento(Memento memento){

  16. this.value = memento.getValue();

  17. }

  18. }

  19. public class Memento {

  20. private String value;

  21. public Memento(String value) {

  22. this.value = value;

  23. }

  24. public String getValue() {

  25. return value;

  26. }

  27. public void setValue(String value) {

  28. this.value = value;

  29. }

  30. }

  31. public class Storage {

  32. private Memento memento;

  33. public Storage(Memento memento) {

  34. this.memento = memento;

  35. }

  36. public Memento getMemento() {

  37. return memento;

  38. }

  39. public void setMemento(Memento memento) {

  40. this.memento = memento;

  41. }

  42. }

測(cè)試類(lèi):

  1. public class Test {

  2. public static void main(String[] args) {

  3. // 創(chuàng)建原始類(lèi)

  4. Original origi = new Original(“egg”);

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

  6. Storage storage = new Storage(origi.createMemento());

  7. // 修改原始類(lèi)的狀態(tài)

  8. System.out.println(“初始化狀態(tài)為:” + origi.getValue());

  9. origi.setValue(“niu”);

  10. System.out.println(“修改后的狀態(tài)為:” + origi.getValue());

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

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

  13. System.out.println(“恢復(fù)后的狀態(tài)為:” + origi.getValue());

  14. }

  15. }

輸出:

初始化狀態(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í)看到你的變化膳叨。看圖:


圖片.png

State類(lèi)是個(gè)狀態(tài)類(lèi)痘系,Context類(lèi)可以實(shí)現(xiàn)切換菲嘴,我們來(lái)看看代碼:

package com.xtfggef.dp.state;

  1. /**

    • 狀態(tài)類(lèi)的核心類(lèi)
    • 2012-12-1
    • @author erqing
  2. */

  3. public class State {

  4. private String value;

  5. public String getValue() {

  6. return value;

  7. }

  8. public void setValue(String value) {

  9. this.value = value;

  10. }

  11. public void method1(){

  12. System.out.println(“execute the first opt!”);

  13. }

  14. public void method2(){

  15. System.out.println(“execute the second opt!”);

  16. }

  17. }

  18. package com.xtfggef.dp.state;

  19. /**

    • 狀態(tài)模式的切換類(lèi) 2012-12-1
    • @author erqing
  20. */

  21. public class Context {

  22. private State state;

  23. public Context(State state) {

  24. this.state = state;

  25. }

  26. public State getState() {

  27. return state;

  28. }

  29. public void setState(State state) {

  30. this.state = state;

  31. }

  32. public void method() {

  33. if (state.getValue().equals(“state1″)) {

  34. state.method1();

  35. } else if (state.getValue().equals(“state2″)) {

  36. state.method2();

  37. }

  38. }

  39. }

測(cè)試類(lèi):

public class Test {

  1. public static void main(String[] args) {

  2. State state = new State();

  3. Context context = new Context(state);

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

  5. state.setValue(“state1″);

  6. context.method();

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

  8. state.setValue(“state2″);

  9. context.method();

  10. }

  11. }

輸出:

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)系圖:


圖片.png

來(lái)看看原碼:一個(gè)Visitor類(lèi)驼侠,存放要訪問(wèn)的對(duì)象,

  1. public interface Visitor {

  2. public void visit(Subject sub);

  3. }

  4. public class MyVisitor implements Visitor {

  5. @Override

  6. public void visit(Subject sub) {

  7. System.out.println(“visit the subject:”+sub.getSubject());

  8. }

  9. }

Subject類(lèi)谆吴,accept方法倒源,接受將要訪問(wèn)它的對(duì)象,getSubject()獲取將要被訪問(wèn)的屬性句狼,

  1. public interface Subject {

  2. public void accept(Visitor visitor);

  3. public String getSubject();

  4. }

  5. public class MySubject implements Subject {

  6. @Override

  7. public void accept(Visitor visitor) {

  8. visitor.visit(this);

  9. }

  10. @Override

  11. public String getSubject() {

  12. return “l(fā)ove”;

  13. }

  14. }

測(cè)試:

  1. public class Test {

  2. public static void main(String[] args) {

  3. Visitor visitor = new MyVisitor();

  4. Subject sub = new MySubject();

  5. sub.accept(visitor);

  6. }

  7. }

輸出: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容器的作 用锅知。先看看圖:

圖片.png

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):

  1. public interface Mediator {

  2. public void createMediator();

  3. public void workAll();

  4. }

  5. public class MyMediator implements Mediator {

  6. private User user1;

  7. private User user2;

  8. public User getUser1() {

  9. return user1;

  10. }

  11. public User getUser2() {

  12. return user2;

  13. }

  14. @Override

  15. public void createMediator() {

  16. user1 = new User1(this);

  17. user2 = new User2(this);

  18. }

  19. @Override

  20. public void workAll() {

  21. user1.work();

  22. user2.work();

  23. }

  24. }

  25. public abstract class User {

  26. private Mediator mediator;

  27. public Mediator getMediator(){

  28. return mediator;

  29. }

  30. public User(Mediator mediator) {

  31. this.mediator = mediator;

  32. }

  33. public abstract void work();

  34. }

  35. public class User1 extends User {

  36. public User1(Mediator mediator){

  37. super(mediator);

  38. }

  39. @Override

  40. public void work() {

  41. System.out.println(“user1 exe!”);

  42. }

  43. }

  44. public class User2 extends User {

  45. public User2(Mediator mediator){

  46. super(mediator);

  47. }

  48. @Override

  49. public void work() {

  50. System.out.println(“user2 exe!”);

  51. }

  52. }

測(cè)試類(lèi):

  1. public class Test {

  2. public static void main(String[] args) {

  3. Mediator mediator = new MyMediator();

  4. mediator.createMediator();

  5. mediator.workAll();

  6. }

  7. }

輸出:

user1 exe!
user2 exe!
23逢防、解釋器模式(Interpreter) 解釋器模式是我們暫時(shí)的最后一講叶沛,一般主要應(yīng)用在OOP開(kāi)發(fā)中的編譯器的開(kāi)發(fā)中,所以適用面比較窄胞四。

圖片.png

Context類(lèi)是一個(gè)上下文環(huán)境類(lèi)恬汁,Plus和Minus分別是用來(lái)計(jì)算的實(shí)現(xiàn),代碼如下:

  1. public interface Expression {

  2. public int interpret(Context context);

  3. }

  4. public class Plus implements Expression {

  5. @Override

  6. public int interpret(Context context) {

  7. return context.getNum1()+context.getNum2();

  8. }

  9. }

  10. public class Minus implements Expression {

  11. @Override

  12. public int interpret(Context context) {

  13. return context.getNum1()-context.getNum2();

  14. }

  15. }

  16. public class Context {

  17. private int num1;

  18. private int num2;

  19. public Context(int num1, int num2) {

  20. this.num1 = num1;

  21. this.num2 = num2;

  22. }

  23. public int getNum1() {

  24. return num1;

  25. }

  26. public void setNum1(int num1) {

  27. this.num1 = num1;

  28. }

  29. public int getNum2() {

  30. return num2;

  31. }

  32. public void setNum2(int num2) {

  33. this.num2 = num2;

  34. }

  35. }

  36. public class Test {

  37. public static void main(String[] args) {

  38. // 計(jì)算9+2-8的值

  39. int result = new Minus().interpret((new Context(new Plus()

  40. .interpret(new Context(9, 2)), 8)));

  41. System.out.println(result);

  42. }

  43. }

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辜伟,一起剝皮案震驚了整個(gè)濱河市氓侧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌导狡,老刑警劉巖约巷,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異旱捧,居然都是意外死亡独郎,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)枚赡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)氓癌,“玉大人,你說(shuō)我怎么就攤上這事贫橙√巴瘢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵卢肃,是天一觀的道長(zhǎng)疲迂。 經(jīng)常有香客問(wèn)我,道長(zhǎng)莫湘,這世上最難降的妖魔是什么尤蒿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮幅垮,結(jié)果婚禮上腰池,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好巩螃,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布演怎。 她就那樣靜靜地躺著,像睡著了一般避乏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上甘桑,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天拍皮,我揣著相機(jī)與錄音,去河邊找鬼跑杭。 笑死铆帽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的德谅。 我是一名探鬼主播爹橱,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼窄做!你這毒婦竟也來(lái)了愧驱?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤椭盏,失蹤者是張志新(化名)和其女友劉穎组砚,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體掏颊,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡糟红,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乌叶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盆偿。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖准浴,靈堂內(nèi)的尸體忽然破棺而出事扭,到底是詐尸還是另有隱情,我是刑警寧澤兄裂,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布句旱,位于F島的核電站,受9級(jí)特大地震影響晰奖,放射性物質(zhì)發(fā)生泄漏谈撒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一匾南、第九天 我趴在偏房一處隱蔽的房頂上張望啃匿。 院中可真熱鬧,春花似錦、人聲如沸溯乒。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)裆悄。三九已至矛纹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間光稼,已是汗流浹背或南。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留艾君,地道東北人采够。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像冰垄,于是被迫代替她去往敵國(guó)和親蹬癌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法虹茶,類(lèi)相關(guān)的語(yǔ)法逝薪,內(nèi)部類(lèi)的語(yǔ)法,繼承相關(guān)的語(yǔ)法写烤,異常的語(yǔ)法翼闽,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,587評(píng)論 18 399
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)洲炊,斷路器感局,智...
    卡卡羅2017閱讀 134,600評(píng)論 18 139
  • 山城的雨 應(yīng)該停留在下午三點(diǎn) 靜默的河水不會(huì)冰冷到死去 父親的煙卷不會(huì)燒到煙頭 無(wú)數(shù)個(gè)憤怒 重疊成了子彈 再往后兩...
    買(mǎi)火柴的老男孩閱讀 433評(píng)論 2 3
  • 你想減肥嗎询微?你想戒煙嗎?你想早起嗎狂巢?…… 答案是想撑毛,但做不到! 人與人之間最大的區(qū)別到底在哪里唧领?據(jù)調(diào)查統(tǒng)計(jì)藻雌,成功者...
    職場(chǎng)眼淚閱讀 414評(píng)論 0 1
  • 能感覺(jué)到中國(guó)青少年變化的大概只有青年人了胯杭。大人她們因?yàn)槊β档纳羁偸遣还苌踔练趴v自己的孩子,而青年人處于兩個(gè)年齡交...
    放棄的命運(yùn)閱讀 245評(píng)論 0 0