大話設計模式、UML、設計模式Java版完全總結

 

此篇部落格為閱讀大話設計模式後的筆記記錄,注意是筆記形式,優先適合於對設計模式有一定了解的讀者,希望短時間快速溫習的讀者,同時也對所有設計模式新增了完整程式碼詮釋與註釋,方便初學者的理解,另外,文章末尾有對所有設計模式的總結,讀者若對部分設計模式容易混淆,可以到文章末尾進行了解,其中文章有些內容我覺得不方便展開的,會附上我認為的比較優秀的部落格地址進行講解。

轉載請註明出處哦~https://blog.csdn.net/qq_35642036/article/details/79663378

 

設計模式分為三大類:

建立型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。

結構型模式,共七種:介面卡模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。

行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代器模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、直譯器模式。

其實還有兩類:併發型模式和執行緒池模式。

 

UML講解

詳細參考網址:http://blog.csdn.net/garfielder007/article/details/54427742

UML即Unified Model Language,是一種建模語言,也是標準建模語言。

 

常見的有以下幾種關係: 泛化(Generalization),  實現(Realization), 關聯(Association), 聚合(Aggregation), 組合(Composition),依賴(Dependency)

 

1.    泛化(繼承非抽象類):帶空心三角形的直線表示

2.    實現(繼承抽象類,類實現介面):帶空心三角形的虛線表示

3.    依賴:類與類之間最弱的關係,依賴可以簡單的理解一個類使用了另一個類:帶箭頭的虛線表示依賴

    例如 :Person類使用了car類裡面的speed()(一般在方法引數)

4.    關聯:一個類和另一類有聯絡:帶箭頭的實線表示

    例如:Person類裡面有Address類屬性(一般在成員變數)

    關聯關係是一種包含關係,在UML中用一個帶箭頭的實線表示,箭頭指向被包含類。在UML類中有如下幾種。

    1..1:表示另一個類的一個物件只與該類的一個物件有關係

    0..*:表示另一個類的一個物件與該類的零個或多個物件有關係

    1..*:表示另一個類的一個物件與該類的一個或多個物件有關係

    0..1:表示另一個類的一個物件沒有或只與該類的一個物件有關係

    *  :任意多個物件關聯

5.    聚合:表示整體與部分的關係,但是部分可以脫離整體而存在:帶空心菱形的直線加箭頭表示,has-a關係

    Person類裡面有car類屬性

6.    組合:部分和整體的關係,但是部分存活週期受到整體的影響,若整體不存在則部分也將不存在。此時部分需在整體的構造方法中建立:帶實心菱形的直線加箭頭表示。Contains-a關係

    person類裡面有finger類屬性

 

關係所表現的強弱程度依次為:組合>聚合>關聯>依賴;

聚合跟組合其實都屬於關聯 只不過它們是兩種特殊的關聯

 

 

設計模式原則

總原則-開閉原則OCP

對擴充套件開放,對修改封閉。在程式需要進行拓展的時候,不能去修改原有的程式碼,而是要擴充套件原有程式碼,實現一個熱插拔的效果。所以一句話概括就是:為了使程式的擴充套件性好,易於維護和升級。

 

設計模式的六大原則:

1、單一職責原則

不要存在多於一個導致類變更的原因,也就是說每個類應該實現單一的職責,否則就應該把類拆分。

 

2、里氏替換原則(Liskov Substitution Principle)

任何基類可以出現的地方,子類一定可以出現。

 

3、依賴倒置原則(Dependence Inversion Principle)

面向介面程式設計,依賴於抽象而不依賴於具體。寫程式碼時用到具體類時,不與具體類互動,而與具體類的上層介面互動。

 

4、介面隔離原則(Interface Segregation Principle)

每個介面中不存在子類用不到卻必須實現的方法,如果不然,就要將介面拆分。使用多個隔離的

 

5、迪米特法則(最少知道原則)(Demeter Principle)

一個類對自己依賴的類知道的越少越好。無論被依賴的類多麼複雜,都應該將邏輯封裝在方法的內部,通過public方法提供給外部。這樣當被依賴的類變化時,才能最小的影響該類。介面,比使用單個介面(多個介面方法集合到一個的介面)要好。

 

6、合成複用原則(Composite Reuse Principle)

儘量首先使用合成/聚合的方式,而不是使用繼承。

 

 

設計模式

策略模式(Strategy)

多個演算法可實現類似功能,若將所有方法寫在一個sortUtils裡面會導致難以維護,程式碼複雜。所以策略模式考慮如何讓演算法和物件分開來,使得演算法可以獨立於使用它的客戶而變化。具體的方案是把一個類中經常改變或者將來可能改變的部分提取出來,作為一個介面,然後在類中包含這個物件的例項,這樣類的例項在執行時就可以隨意呼叫實現了這個介面的類的行為。

策略模式就是用來封裝演算法的,但在實踐中,我們發現可以用它來封裝幾乎任何型別的規則,只要在分析過程中聽到需要在不同時間應用不同的業務規則,就可以考慮使用策略模式處理這種變化的可能性。

優點

1、可以動態的改變物件的行為

缺點

1、客戶端必須知道所有的策略類,並自行決定使用哪一個策略類

2、策略模式將造成產生很多策略類

     

輸出:
策略A的具體演算法實現
策略B的具體演算法實現
策略C的具體演算法實現

 

 

簡單工廠模式(Simple Factory) 工廠方法(FactoryMethod)

 

工廠模式可以分為三類:

1)簡單工廠模式(Simple Factory):不符合開放-封閉原則

2)工廠方法(Factory Method):生產單一產品

3)抽象工廠模式(Abstract Factory):生產一個產品體系

簡單工廠模式有一個具體的工廠類

工廠方法模式只有一個抽象產品類,而抽象工廠模式有多個。  

工廠方法模式的具體工廠類只能建立一個具體產品類的例項,而抽象工廠模式可以建立多個。

簡單工廠模式:一個上帝類,能夠生產A車,若有一種B車需要生產,則需要更改上帝類工廠程式碼

   

 

工廠方法模式:去掉了簡單工廠模式中工廠方法的靜態屬性,使得它可以被子類繼承。由於簡單工廠模式不僅對擴充套件開放了,也對修改開放了(每新增一個類,就得去生成例項的工廠方法中增加新的分支),違背了“開放-封閉原則”。工廠方法把簡單工廠的內部邏輯判斷轉移到了客戶端程式碼來進行。缺點:每加一個產品,需要額外加一個產品工廠的類,增加了額外的開銷。

 

 

 

 

工廠模式用於處理 如何獲取例項物件問題,建造者模式用於處理如何建造例項物件 問題。

工廠方法的實現並不能減少工作量,但是它能夠在必須處理新情況時,避免使已經很複雜的程式碼更加複雜

 

通常設計應該是從工廠方法開始,當設計者發現需要更大的靈活性時,設計便會向其他建立型模式演化。當設計者在設計標準之間進行權衡的時候,瞭解多個建立型模式可以給設計者更多的選擇餘地

 

 

 

裝飾模式(Decorator)

 

動態給一個物件新增一些額外的職責,使用Decorator模式相比用生成子類方式達到功能的擴充顯得更為靈活。

設計初衷:通常可以使用繼承來實現功能的拓展,如果這些需要拓展的功能的種類很繁多,那麼勢必生成很多子類,增加系統的複雜性,同時,使用繼承實現功能拓展,我們必須可預見這些拓展功能,這些功能是編譯時就確定了,是靜態的。

 

裝飾者模式:

裝飾者模式必然有一個公共的介面或抽象類,用來作為物件的傳遞。你需要根據介面實現基本的被裝飾類(Person),以及裝飾類的公共介面(Decorator ),以後所有的裝飾類都是繼承自公共的裝飾類介面,內部實現。

 

       

輸出:
具體物件的操作
具體裝飾物件A的操作
B中的新增行為 具體裝飾物件B的操作
C沒有特殊行為 具體裝飾物件C的操作

 

裝飾者模式也可用於給一個方法新增開始時間,結束時間,或者新增事務,提交事務等  

 

核心要點:有一個抽象物件類或物件介面,而具體的物件和裝飾類都要繼承類或實現這個介面

 

 

裝飾程式碼二:

 
public class CodingTask implements Runnable {
  private final int employeeId;
  public CodingTask(int employeeId) {
    this.employeeId = employeeId;
  }
  @Override
  public void run() {
    System.out.println("Employee "   employeeId
          " started writing code.");
    try {
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    System.out.println("Employee "   employeeId
          " finished writing code.");
  }
}
public class TransactionalRunnable implements Runnable {
  private final Runnable innerRunnable;
  public TransactionalRunnable(Runnable innerRunnable) {
    this.innerRunnable = innerRunnable;
  }
  @Override
  public void run() {
    boolean shouldRollback = false;
    try {
      beginTransaction();
      innerRunnable.run();
    } catch (Exception e) {
      shouldRollback = true;
      throw e;
    } finally {
      if (shouldRollback) {
        rollback();
      } else {
        commit();
      }
    }
  }
  private void commit() {
    System.out.println("commit");
  }
  private void rollback() {
    System.out.println("rollback");
  }
  private void beginTransaction() {
    System.out.println("beginTransaction");
  }
}
public class LoggingRunnable implements Runnable {
  private final Runnable innerRunnable;
  public LoggingRunnable(Runnable innerRunnable) {
    this.innerRunnable = innerRunnable;
  }
  @Override
  public void run() {
    long startTime = System.currentTimeMillis();
    System.out.println("Task started at "
          startTime);
    innerRunnable.run();
    System.out.println("Task finished. Elapsed time: "
          (System.currentTimeMillis() - startTime));
  }
}
public class Client {
  public static void main(String[] args) {
    new LoggingRunnable(
        new TransactionalRunnable(
            new CodingTask(0))).run();
  }
}
輸出
Task started at 1522227362358
beginTransaction
Employee 0 started writing code. 
Employee 0 finished writing code. 
commit
Task finished. Elapsed time: 5012

 

 

代理模式(Proxy)

代理模式:為其他物件提供一種代理以控制對這個物件的訪問,使得其他物件無法直接接觸目標物件

 

 

 

輸出:真實物件的請求

 

原型模式(Prototype)

原型模式:用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。

涉及了淺拷貝與深拷貝

參考學習網址:https://www.cnblogs.com/Qian123/p/5710533.html#_label3

 

原型模式其實就是從一個物件再建立另外一個可定製的物件,而且不需要知道任何建立的細節

優點:建立相應數目的原型並克隆它們通常比每次用合適的狀態手工例項化該類更方便一些

 

 

輸出:
>>>>>>淺度拷貝:
大鳥  男  25
工作經歷: 1999-2002,  ZZ公司
大鳥  男  25
工作經歷: 1999-2002,  ZZ公司
大鳥  男  25
工作經歷: 1999-2002,  ZZ公司
==================================
>>>>>>深度拷貝:
大鳥  男  25
工作經歷: 1999-2002,  XX公司
大鳥  男  25
工作經歷: 1999-2002,  YY公司
大鳥  男  25
工作經歷: 1999-2002,  ZZ公司

 

 

 

模板方法模式(Template)

模板方法模式:定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟

 

 

輸出:
具體類A的方法1實現
具體類A的方法2實現
模板方法結束
 

具體類B的方法1實現
具體類B的方法2實現
模板方法結束
 

 

比如 AsyncTask 裡面的四個方法 onPreExecute 、 doInBackground 、 onProgressUpdate 、 onPostExecute

還有 Android裡面的Activity 也應用了模板方法模式

onCreate 、 onStart 、 onResume 、 onPause 、 onStop 、 onDestroy 、 onRestart

 

外觀模式(Facade)

為子系統中的一組介面提供一個一致的頁面,此模式定義了一個高層介面,這個介面使得這一子系統更加容易使用(完美體現了依賴倒轉原則和迪米特法則)

案例解釋:一個人買很多股票,由於股票眾多難以管理容易造成虧損,可以選擇購買基金,由基金來管理這些股票(基金擅長管理股票)

 

輸出:
方法組A:
子系統方法一
子系統方法二
子系統方法四
方法組B:
子系統方法三
子系統方法四

 

建造者模式(Builder)

將一個複雜的物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。

 

案例解釋:麥當勞的漢堡包永遠不會少加鹽,擺攤的每次炒的東西味道可能有變化

 

使用建造者模式,使用者只需指定需要建造的型別就可以得到它們,而具體建造的過程和細節就不需知道了。

 

什麼時候使用建造者模式:需要建立一些複雜的物件,這些物件內部構建間的建造順序通常是穩定的,但物件內部的構建通常面臨著複雜的變化。

 

建造者模式如何實現高內聚鬆耦合的?將一個複雜物件的建立與它的表示分離,這就可以很容易地改變一個產品的內部表示,並且使得構造程式碼和表示程式碼分開。這樣對於客戶來說,它無需關心產品的建立過程,而只要告訴建造者需要什麼,就能用同樣的構建過程建立不同的產品給客戶

 

 

 

 

 

 

與抽象工廠的區別:在建造者模式裡,有個指導者,由指導者來管理建造者,使用者是與指導者聯絡的,指導者聯絡建造者最後得到產品。即建造模式可以強制實行一種分步驟進行的建造過程。

 

 

觀察者模式(Observer)

 

觀察者模式(釋出-訂閱模式):定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態傳送變化時,會通知所有觀察者物件,讓它們能夠自動更新自己

一個被觀察者管理所有相依於它的觀察者物件,並且在本身的狀態改變時主動發出通知。這通常通過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統。

案例解釋:前臺小姐concreteSubject、裡面的工作的人concreteObserver、老闆來了前臺小姐通知裡面工作的人馬上開始工作

輸出:
觀察者X的新狀態是ABC
觀察者Y的新狀態是ABC

觀察者Z的新狀態是ABC

 

應用:珠寶商運送一批鑽石,有黃金強盜準備搶劫,珠寶商僱傭了私人保鏢,警察局也派人護送,於是當運輸車上路的時候,強盜保鏢警察都要觀察運輸車一舉一動

 

程式:

1.     抽象的被觀察者watched介面,有addWatcher,deleteWatch,notifyWatch方法

2.     抽象的觀察者watcher介面,update方法

3.     具體的被觀察者Transporter實現watched介面

4.     三個具體的觀察者實現watcher,有具體的update方法

5.     測試類

 

觀察者模式在關於目標角色、觀察者角色通訊的具體實現中,有兩個版本。

一種情況便是目標角色在發生變化後,僅僅告訴觀察者角色“我變化了”,觀察者角色如果想要知道具體的變化細節,則就要自己從目標角色的介面中得到。這種模式被很形象的稱為:拉模式——就是說變化的資訊是觀察者角色主動從目標角色中“拉”出來的。

還有一種方法,那就是我目標角色“服務一條龍”,通知你發生變化的同時,通過一個引數將變化的細節傳遞到觀察者角色中去。這就是“推模式”——管你要不要,先給你啦。

 

抽象工廠模式(AbstractFactory)

提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類

 

好處:

1.     易於交換產品系列,例如Ifactory factory = new AccessFactory()在一個應用中只需要在初始化的時候出現一次,這就使得改變一個應用的具體工廠變得非常容易,它只需要改變具體工廠即可使用不同的產品配置。

2.     它讓具體的建立例項過程與客戶端分離,客戶端是通過它們的抽象介面操縱例項,產品的具體類名也被具體工廠的實現分離,不會出現在客戶程式碼中。

所有在用簡單工廠的地方,都可以考慮用反射技術來去除switch或if,解除分支判斷帶來的耦合

 

缺點:如果要新增一個產品類,就得增加很多程式碼

 

用反射 抽象工廠的資料訪問程式

public class OperationFactory {
    private static Map<String, Class<?>> allOperationMaps = new HashMap<String, Class<?>>();
    public static void fillMap() {
      allOperationMaps.put(” “, OperationAdd.class);
      allOperationMaps.put(“-“, OperationSub.class);
      allOperationMaps.put(“*”, OperationMul.class);
      allOperationMaps.put(“/”, OperationDiv.class);
    }
    public static Operation createOperation(String operator)
       throws InstantiationException, IllegalAccessException {
   Operation operation;
   fillMap();
   Class<?> operationClass = allOperationMaps.get(operator);
   if (operationClass == null) {
       throw new RuntimeException(“unsupported operation”);
   }
      operation = (Operation) operationClass.newInstance();
      return operation;
    }
}

public class Client {
    public static void main(String[] args) throws InstantiationException,
       IllegalAccessException {
      Operation operation = OperationFactory.createOperation("/");
      operation.numberA = 7;
      operation.numberB = 8;
      System.out.println(operation.result());
    }
}
輸出:0.875

 

 

狀態模式(State)

當一個物件的內在狀態改變時允許改變其行為,這個物件看起來像是改變了其類

 

狀態模式主要解決的是當控制一個物件狀態轉換的條件表示式過於複雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類當中,可以把複雜的判斷邏輯簡化。

 

好處:將與特定狀態相關的行為區域性化,並且將不同狀態的行為分割開來。

也就是將特定的狀態相關的行為都放入一個物件中,由於所有與狀態相關的程式碼都存在於某個ConcreteState中,所以通過定義性的子類可以很容易地增加新的狀態和轉換。

目的:消除龐大的條件分支語句;狀態模式通過把各種狀態轉移邏輯分佈到State的子類之間,來減少相互間的依賴。

什麼時候用:

當一個物件的行為取決於它的狀態,並且它必須在執行時刻根據狀態改變它的行為時,就可以考慮使用狀態模式。

//Context類,維護一個ConcreteState子類的例項,這個例項定義當前的狀態

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 request() {
       this.state.handle(this);
    }
}

 

//抽象狀態類
public abstract class State {
    public abstract void handle(Context context);
}

//具體狀態類A
class ConcreteStateA extends State {
    public void handle(Context context) {
       System.out.println(“現在是在狀態A”);
       context.setState(new ConcreteStateB());
    }
}//具體狀態類B
class ConcreteStateB extends State {
    public void handle(Context context) {
       System.out.println(“現在是在狀態B”);
       context.setState(new ConcreteStateC());
    }
}//具體狀態類C
class ConcreteStateC extends State {
    public void handle(Context context) {
       System.out.println(“現在是在狀態C”);
       context.setState(new ConcreteStateA());
    }
}

//客戶端:不斷請求,不斷更改狀態
public class StateClient {
    public static void main(String[] args) {
      Context context = new Context(new ConcreteStateA());
      context.request();
      context.request();
      context.request();
      context.request();
      context.request();
    }
}
輸出:
現在是在狀態A
現在是在狀態B
現在是在狀態C
現在是在狀態A
現在是在狀態B
 

 

 

介面卡模式(Adapter)

 

將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以在一起工作。

 

優點

  通過介面卡,客戶端可以呼叫同一介面,因而對客戶端來說是透明的。這樣做更簡單、更直接、更緊湊。

  複用了現存的類,解決了現存類和複用環境要求不一致的問題。

   將目標類和適配者類解耦,通過引入一個介面卡類重用現有的適配者類,而無需修改原有程式碼。

  一個物件介面卡可以把多個不同的適配者類適配到同一個目標,也就是說,同一個介面卡可以把適配者類和它的子類都適配到目標介面。

缺點

   對於物件介面卡來說,更換介面卡的實現過程比較複雜。

適用場景

  系統需要使用現有的類,而這些類的介面不符合系統的介面。

  想要建立一個可以重用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。

  兩個類所做的事情相同或相似,但是具有不同介面的時候。

  舊的系統開發的類已經實現了一些功能,但是客戶端卻只能以另外介面的形式訪問,但我們不希望手動更改原有類的時候。

使用第三方元件,元件介面定義和自己定義的不同,不希望修改自己的介面,但是要使用第三方元件介面的功能。

 

實現:

public abstract class Target {
    public void request() {
   System.out.println(“普通請求!”);
    }
}

//需要適配的類
public class Adaptee {
    public void specificRequest() {
   System.out.println(“特殊的請求!”);
    }
}

//介面卡類,通過在內部包裝一個Adaptee物件,把原介面轉換成目標介面
public class Adapter extends Target {
    private Adaptee adaptee = new Adaptee();
    @Override
    public void request() {
   adaptee.specificRequest();
    }
}

//介面卡客戶端
public class AdapterClient {
   public static void main(String[] args) {
      Target target;
      target = new Adapter();
      target.request();
   }
}
輸出:
特殊的請求!
 

Android 裡的  ListView 和  RecyclerView 的 setAdapter() 方法就是使用了介面卡模式。

 

備忘錄模式(Memento)

在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態,這樣以後就可將該物件回覆到原先儲存的狀態

 

Memento比較適合用於功能複雜的,但需要維護或記錄屬性歷史的類,或者需要儲存的屬性只是眾多屬性中的一小部分時,Originator可以根據儲存的Memento資訊還原到前一狀態。

如果在某個系統中使用命令模式時,需要實現命令的撤銷功能,那麼命令模式可以使用備忘錄模式來儲存可撤銷操作的狀態。

使用備忘錄可以把複雜的物件內部資訊對其他的物件遮蔽起來,從而可以恰當地保持封裝的邊界

 

//備忘錄(Memento)類
public class Memento {
    private String state;
    public Memento(String state) {this.state = state;}
    public String getState() {return state;}
    public void setState(String state) {this.state = state;}
}
//管理者(CareTaker)類:管理備忘錄
public class CareTaker {
    private Memento memento;
    public Memento getMemento() { return memento; }
    public void setMemento(Memento memento) { this.memento = memento; }
}
//發起人(Originator) 類
public class Originator {
    private String state;
    public Memento createMemento() { return new Memento(this.state);}
    public void recoverMemento(Memento memento) { this.setState(memento.getState()); }
    public void show() {System.out.println("state = "   this.state); }
    public String getState() { return state; }
    public void setState(String state) { this.state = state; }
}
//客戶端
public class MementoClient {
    public static void main(String[] args) {
   // 設定初始狀態
   Originator originator = new Originator();
   originator.setState("On");
   originator.show();
   // 管理者通過備忘錄儲存狀態,由於有了很好地封裝,可以隱藏Originator的實現細節
   CareTaker careTaker = new CareTaker();
   careTaker.setMemento(originator.createMemento());
   // 改變狀態
   originator.setState("Off");
   originator.show();
   // 通過管理者從備忘錄中恢復狀態
   originator.recoverMemento(careTaker.getMemento());
   originator.show();
    }
}
輸出:
state = On
state = Off
state = On

 

 

 組合模式(Componet)

 

將物件組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用於對單個物件和組合物件的使用具有一致性

何時使用?

答:需求中是體現部分與整體層次的結構時,以及希望使用者可以忽略組合物件與單個物件的不同,統一地使用組合結構中的所有物件時,就應該考慮用組合模式

 

//Component為組合中的物件宣告介面,在適當情況下,實現所有類共有介面的預設行為。
public abstract class Component {
    protected String name;
    public Component(String name) { this.name = name; }
    public abstract void add(Component component);
    public abstract void remove(Component component);
    public abstract void display(int depth);
}

 

//Leaf在組合中表示葉節點物件,葉節點沒有子節點,實際上它的add、remove方法都是多餘的,實際上我覺得這裡有點違反了介面隔離原則:每個介面中不存在子類用不到卻必須實現的方法,也就是Component介面存在子類Leaf用不上卻必須實現的方法
public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }
    public void add(Component component) {
        System.out.println(“cannot add to a leaf”);
    }
    public void remove(Component component) {
        System.out.println(“cannot remove from a leaf”);
    }
    public void display(int depth) {
        // 通過“-”的數目顯示級別
        System.out.println(StringUtil.repeatableString(“-“, depth) this.name);
    }
}

 

//定義有枝節點行為,用來儲存子部件
public class Composite extends Component {
    private List<Component> children = new ArrayList<Component>();
    public Composite(String name) { super(name); }
    public void add(Component component) { children.add(component); }
    public void remove(Component component) { children.remove(component); }
    public void display(int depth) {
       // 顯示其枝節點名稱,並對其下級進行遍歷
       System.out.println(StringUtil.repeatableString(“-“, depth) this.name);
        for (Component component : children) {
            component.display(depth 2);
        }
    }
}

//客戶端。通過Component介面操作組合部件的物件
public class CompositeClient {
    public static void main(String[] args) {
      // 生成樹根,根上長出兩葉Leaf A和Leaf B
      Composite root = new Composite(“root”);
      root.add(new Leaf(“Leaf A”));
      root.add(new Leaf(“Leaf B”));
      // 根上長出分支Composite X,分支上也有兩葉Leaf X-A和Leaf X-B
      Composite compositeX = new Composite(“Composite X”);
      compositeX.add(new Leaf(“Leaf X-A”));
      compositeX.add(new Leaf(“Leaf X-B”));
      root.add(compositeX);
      // 在Composite X上再長出分支Composite X-Y,分支上也有兩葉Leaf X-Y-A和Leaf X-Y-B
      Composite compositeXY = new Composite(“Composite X-Y”);
      compositeXY.add(new Leaf(“Leaf X-Y-A”));
      compositeXY.add(new Leaf(“Leaf X-Y-B”));
      compositeX.add(compositeXY);
      // 顯示大樹的樣子
      root.display(1);
    }
}

列印出:
-root
—Leaf A
—Leaf B
—Composite X
—–Leaf X-A
—–Leaf X-B
—–Composite X-Y
——-Leaf X-Y-A
——-Leaf X-Y-B
 

 

程式中提到Leaf實現類實現了add與remove方法,而這兩個方法對其本身並沒有用處,這種方式叫透明方式,也就是說在Component中宣告所有用來管理子物件的方法,其中包括add、remove等,這樣實現Component介面的所有子類都具備了Add和remove,這樣做的好處就是葉節點和枝節點對於外界沒有區別,它們具備完全一致的行為介面。但問題也很明顯,因為Leaf類本身不具備Add、remove方法的功能,所以實現它是沒有意義的。

 

 

迭代器模式(Iterator)

提供一種方法順序訪問一個聚合物件中各個元素,而又不暴露該物件的內部表示。

Java等語言都已經對其進行了封裝

程式介紹:用一個Aggregate裡面儲存一個list,帶有控制list的方法,而Iterator裡面包裝這個Aggregate,將對Iterator的各個介面方法的操作重寫為對Aggregate的操作。Aggregate裡面有一個可以建立其對應的Iterator的方法

//聚集介面,可以理解為Iterable
public interface Aggregate<T> {
    public Iterator<T> createIterator();
}

//具體聚集類
public class ConcreteAggregate<T> implements Aggregate<T> {
    private List<T> items = new ArrayList<T>();
    @Override
    public Iterator<T> createIterator() {
       return new ConcreteIterator<T>(this);
    }
    public int count() { return items.size(); }
    public T getItems(int index) { return items.get(index); }
    public void setItems(T item) { items.add(item); }
}

//迭代器介面
public interface Iterator<T> {
    public T first();
    public T next();
    public boolean isDone();
    public T currentItem();
}

//具體迭代器類,給出一種具體迭代的實現方式。思考:迭代器表示的是一種迭代的行為,而聚集則是真正要被迭代的資料集合。之所以要將迭代器和聚集分開,就是為了將行為與資料分開。 可類比Java中Iterator與Iterable的關係進行理解
public class ConcreteIterator<T> implements Iterator<T> {
    private ConcreteAggregate<T> concreteAggregate;
    private int current = 0;
    public ConcreteIterator(ConcreteAggregate<T> concreteAggregate) {

this.setConcreteAggregate(concreteAggregate);

}
    @Override
    public T first() { return concreteAggregate.getItems(0); }
    @Override
    public T next() {
        current ;
        if (current < concreteAggregate.count()) {
            return concreteAggregate.getItems(current);
        }
        return null;
    }
    @Override
    public boolean isDone() {

return current >= concreteAggregate.count() ? true : false;

}
    @Override
    public T currentItem() { return concreteAggregate.getItems(current); }
    public ConcreteAggregate<T> getConcreteAggregate() { return concreteAggregate; }
    public void setConcreteAggregate(ConcreteAggregate<T> concreteAggregate) {

this.concreteAggregate = concreteAggregate;

}
    public int getCurrent() { return current; }
    public void setCurrent(int current) { this.current = current; }
}

//迭代器客戶端
public class IteratorClient {
    public static void main(String[] args) {
      ConcreteAggregate<String> bus = new ConcreteAggregate<String>();
      bus.setItems(“大鳥”);
      bus.setItems(“小菜”);
      bus.setItems(“行李”);
      bus.setItems(“老外”);
      bus.setItems(“公交內部員工”);
      bus.setItems(“小偷”);
      Iterator<String> iterator = new ConcreteIterator<String>(bus);
      while (!iterator.isDone()) {
         System.out.println(iterator.currentItem() “請買票!”);
         iterator.next();
      }
    }
}
輸出:
大鳥請買票!
小菜請買票!
行李請買票!
老外請買票!
公交內部員工請買票!
小偷請買票!
 

 

 

單例模式(Singleton)

保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。

優點:讓類自身負責儲存它的唯一例項。這個類可以保證沒有其他例項可以被建立,並且還提供了一個訪問該例項的方法,這樣就使得對唯一的例項可以嚴格地控制客戶怎樣以及何時訪問它。

 

懶漢式、餓漢式、synchronized懶或餓漢式、雙檢鎖DCL、volatile方式(工作記憶體優化方式)、靜態內部類方式、列舉類方式

 

 

 

如果單例模式不是用列舉類建立的,那麼其無法防止其序列化過程破壞單例特性,因為在序列化過程會利用反射呼叫單例的私有構造方法生成新的物件,如果要避免這種破壞,就要在單例類裡面新增方法readResolve(),

具體參考:http://mp.weixin.qq.com/s/iXC47w4tMfpZzTNxS_JQOw

1.     有一個雙重檢驗鎖的單例模式(即版本五或版本六)

2.     序列化測試程式碼如下

 

 

 

橋接模式(Bridge)

 

將抽象部分與它的實現部分分離,使它們都可以獨立地變化。

這裡並不是說讓抽象類與其派生類分離,因為這沒有任何意義,實現指的是抽象類和它的派生類用來實現自己的物件

 

由於實現的方式有多種,橋接模式的核心意圖就是把這些實現獨立出來,讓它們各自地變化,就使得每種實現的變化不會影響其他實現,從而達到應對變化的目的

 

 

注意:橋接模式與設計模式在程式碼上的實現時及其類似甚至相同的,不同點在於介面卡模式的介面卡類與被介面卡類是已經存在的,需要開發一個介面使得他們實現這個介面一起工作,而橋接模式是先定下介面策略,再讓不同的類去實現它。

 

 

 

public abstract class Abstraction {
    //橋接模式的關鍵,使得Abstraction聚合Implementor
    protected Implementor implementor;//聚合關係
    private String name;
    public Abstraction(String name) {
       this.setName(name);
    }
    public void setImplementor(Implementor implementor) {
       this.implementor = implementor;
    }
    public void operation() {
       System.out.print(“Abstraction-” this.getName() “: “);
       implementor.operation();
    }
    public String getName() {
       return name;
    }
    public void setName(String name) {
       this.name = name;
    }
}

class AbstractionA extends Abstraction {
    public AbstractionA(String name) {
       super(name);
    }
    @Override
    public void operation() {
       super.operation();
    }
}
class AbstractionB extends Abstraction {
    public AbstractionB(String name) {
       super(name);
    }
    @Override
    public void operation() {
       super.operation();
    }
}

 

public abstract class Implementor {
    public abstract void operation();
}

class ConcreteImplemtorA extends Implementor {
    @Override
    public void operation() {
       System.out.println(“ConcreteImplemtorA的方法執行”);
    }
}

class ConcreteImplemtorB extends Implementor {
    @Override
    public void operation() {
       System.out.println(“ConcreteImplemtorB的方法執行”);
    }
}

 

public class BridgeClient {
    public static void main(String[] args) {
      Abstraction a = new AbstractionA(“A”);
      a.setImplementor(new ConcreteImplemtorA());
      a.operation();
      a.setImplementor(new ConcreteImplemtorB());
      a.operation();
      Abstraction b = new AbstractionB(“B”);
      b.setImplementor(new ConcreteImplemtorA());
      b.operation();
      b.setImplementor(new ConcreteImplemtorB());
      b.operation();
      // 這樣通過使用“組合/聚合複用原則”
      // 如果繼續有AbstractionC … 或者ConcreteImplemtorC …
      // 只需要擴充套件類即可,不需要修改現有類,符合“開放-封閉”原則
    }
}
輸出:
Abstraction-A: ConcreteImplemtorA的方法執行
Abstraction-A: ConcreteImplemtorB的方法執行
Abstraction-B: ConcreteImplemtorA的方法執行
Abstraction-B: ConcreteImplemtorB的方法執行
 

 

將以上程式碼更加具體化到現實情況的程式碼如下:

 

 

命令模式(Command)

 

將一個請求封裝為一個物件,從而使你可以用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作

 

 

優點

1.降低物件之間的耦合度。

2.新的命令可以很容易地加入到系統中。

3.可以比較容易地設計一個組合命令。

4.呼叫同一方法實現不同的功能

 

缺點

使用命令模式可能會導致某些系統有過多的具體命令類。因為針對每一個命令都需要設計一個具體命令類,因此某些系統可能需要大量具體命令類,這將影響命令模式的使用。

 

//知道如何實施與執行一個與請求相關的操作,任何類都可能作為一個接收者。真正執行請求的地方!
interface Reciever {   //比喻為廚師
    public void action();
}
class RecieverA implements Reciever { //烤肉的廚師
    @Override
    public void action() {
   System.out.println("RecieverA執行請求!");
    }
}
class RecieverB implements Reciever { //炒菜的廚師
    @Override
    public void action() {
   System.out.println("RecieverB執行請求!");
    }
}
class RecieverC implements Reciever {  //烤魚的廚師
    @Override
    public void action() {
   System.out.println("RecieverC執行請求!");
    }
}
//用來宣告執行操作的介面
public abstract class Command {
    //命令類,服務員手裡的訂單,訂單裡面包含了客戶想要什麼廚師做的菜
    protected Reciever reciever;
    public Command(Reciever reciever) {
       this.reciever = reciever;
    }
    public abstract void execute();
}
// 將一個接收者物件繫結於一個動作,呼叫接收者相應的操作,以實現execute
class ConcreteCommand extends Command {
    public ConcreteCommand(Reciever reciever) {
       super(reciever);
    }
    @Override
    public void execute() {
        reciever.action();
    }
}
//要求該命令執行這個請求
public class Invoker {//服務員,擁有很多訂單
    private List<Command> list = new ArrayList();
    public void addCommand(Command command) {
        list.add(command);
    }
    public void executeCommand() {
        for (Command command:list) {
            command.execute();
        }
    }
}
//建立一個具體命令物件並設定它的接收者
public class CommandClient {
    public static void main(String[] args) {
      Reciever recieverA = new RecieverA();
      Reciever recieverB = new RecieverB();
      Reciever recieverC = new RecieverC();
      Command commandA = new ConcreteCommand(recieverA);
      Command commandB = new ConcreteCommand(recieverB);
      Command commandC = new ConcreteCommand(recieverC);
      Invoker invoker = new Invoker();
      invoker.addCommand(commandA);
      invoker.addCommand(commandB);
      invoker.addCommand(commandC);
      invoker.executeCommand();
    }
}
輸出:
RecieverA執行請求!
RecieverB執行請求!
RecieverC執行請求!

 

 

職責鏈模式(ResponsibilityChain)

使多個物件都有機會處理請求,從而避免請求的傳送者和接受者之間的耦合關係,

將這個物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理他為止。

 

現實案例:申請聚餐費用的管理,申請聚餐費用的大致流程一般是,由申請人先填寫申請單,然後交給領導審批,如果申請批准下來,領導會通知申請人審批通過,然後申請人去財務領取費用,如果沒有批准下來,領導會通知申請人審批未通過,此事也就此作罷。

不同級別的領導,對於審批的額度是不一樣的,比如,專案經理只能審批500元以內的申請;部門經理能審批1000元以內的申請;而總經理可以稽核任意額度的申請。

 

職責鏈靈活在哪

1. 改變內部的傳遞規則

在內部,專案經理完全可以跳過人事部到那一關直接找到總經理。

每個人都可以去動態地指定他的繼任者。

2. 可以從職責鏈任何一關開始。

如果專案經理不在,可以直接去找部門經理,責任鏈還會繼續,沒有影響。

3.用與不用的區別

不用職責鏈的結構,我們需要和公司中的每一個層級都發生耦合關係。

如果反映在程式碼上即使我們需要在一個類中去寫上很多醜陋的if….else語句。

如果用了職責鏈,相當於我們面對的是一個黑箱,我們只需要認識其中的一個部門,然後讓黑箱內部去負責傳遞就好了

 

純的與不純的責任鏈模式

一個純的責任鏈模式要求一個具體的處理者物件只能在兩個行為中選擇一個:一是承擔責任,而是把責任推給下家。不允許出現某一個具體處理者物件在承擔了一部分責任後又把責任向下傳的情況。

在一個純的責任鏈模式裡面,一個請求必須被某一個處理者物件所接收;在一個不純的責任鏈模式裡面,一個請求可以最終不被任何接收端物件所接收。

純的責任鏈模式的實際例子很難找到,一般看到的例子均是不純的責任鏈模式的實現。

 

 

//處理請求的介面
public abstract class Handler {
    protected Handler successor;
    public void setSuccessor(Handler successor) {
      this.successor = successor;
    }
    public abstract void handleRequest(int request);
}

// 具體處理者類,處理它所負責的請求,可訪問它的後繼者,如果可處理該請求,則處理,否則轉給它的後繼者處理
class ConcreteHandlerA extends Handler {
    @Override
    public void handleRequest(int request) {
      if (request >= 0 && request <= 10) {
         System.out.println(this.getClass().getName() “處理了請求” request);
      } else if (successor != null) {
         successor.handleRequest(request);
      }
    }
}

class ConcreteHandlerB extends Handler {
    @Override
    public void handleRequest(int request) {
      if (request > 10 && request <= 20) {
         System.out.println(this.getClass().getName() “處理了請求” request);
      } else if (successor != null) {
         successor.handleRequest(request);
      }
    }
}
class ConcreteHandlerC extends Handler {
    @Override
    public void handleRequest(int request) {
      if (request > 20 && request <= 30) {
         System.out.println(this.getClass().getName() “處理了請求” request);
      } else if (successor != null) {
         successor.handleRequest(request);
      }
    }
}

//鏈上的具體處理者物件提交請求
public class Client {
    public static void main(String[] args) {
      Handler handlerA = new ConcreteHandlerA();
      Handler handlerB = new ConcreteHandlerB();
      Handler handlerC = new ConcreteHandlerC();
      handlerA.setSuccessor(handlerB);
      handlerB.setSuccessor(handlerC);
      int[] requests = { 2, 14, 5, 6, 8, 23, 12, 21 };
      for (int i : requests) {
         handlerA.handleRequest(i);
      }
    }
}
輸出:
ConcreteHandlerA處理了請求2
ConcreteHandlerB處理了請求14
ConcreteHandlerA處理了請求5
ConcreteHandlerA處理了請求6
ConcreteHandlerA處理了請求8
ConcreteHandlerC處理了請求23
ConcreteHandlerB處理了請求12
ConcreteHandlerC處理了請求21
 

 

 

中介者模式(Mediator)

又見調停者模式:用一箇中介物件來封裝一系列的物件互動。中介者使各物件不需要現實地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。

 

優點:減少了各個Colleague的耦合,使得可以獨立地改變和複用各個Colleague和Mediator,其次,由於把物件如何協作進行了抽象,將中介作為一個獨立的概念並將其封裝在一個物件中,這樣關注的物件就從物件各自本身的行為轉移到它們之間的互動上來,也就是站在一個更巨集觀的角度去看待系統。

缺點:由於ConcreteMediator控制了集中化,也是就把互動複雜性變為了中介者的複雜性,這就使得中介者會變得比任何一個ConcreteColleague都複雜。

//抽象中介者類
public abstract class Mediator {
    public abstract void send(String message, Colleague colleague);
}

class ConcreteMediator extends Mediator {
    // 需要了解所有的具體同事物件
    private ConcreteColleague1 c1;
    private ConcreteColleague2 c2;
    public ConcreteColleague1 getC1() {
       return c1;
    }
    public void setC1(ConcreteColleague1 c1) {
       this.c1 = c1;
    }
    public ConcreteColleague2 getC2() {
       return c2;
    }
    public void setC2(ConcreteColleague2 c2) {
       this.c2 = c2;
    }
    @Override
    public void send(String message, Colleague colleague) {
       // 重寫傳送資訊的方法,根據物件做出選擇判斷,通知物件
        if (colleague == c1) {
            c2.notifyMsg(message);
        } else {
            c1.notifyMsg(message);
        }
    }
}

//抽象同事類
public abstract class Colleague {
    protected Mediator mediator;
    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }
    public abstract void sendMsg(String message);
    public abstract void notifyMsg(String message);
}

class ConcreteColleague1 extends Colleague {
    public ConcreteColleague1(Mediator mediator) {
       super(mediator);
    }
    @Override
    public void sendMsg(String message) {
       mediator.send(message, this);
    }
    @Override
    public void notifyMsg(String message) {
       System.out.println(“同事1得到訊息:” message);
    }
}

class ConcreteColleague2 extends Colleague {
    public ConcreteColleague2(Mediator mediator) {
       super(mediator);
    }
    @Override
    public void sendMsg(String message) {
       mediator.send(message, this);
    }
    @Override
    public void notifyMsg(String message) {
       System.out.println(“同事2得到訊息:” message);
    }
}

//客戶端
public class MediatorClient {
    public static void main(String[] args) {
      ConcreteMediator concreteMediator = new ConcreteMediator();
      // 讓兩個具體同事類認識中介者物件
      ConcreteColleague1 concreteColleague1 = new ConcreteColleague1(
         concreteMediator);
      ConcreteColleague2 concreteColleague2 = new ConcreteColleague2(
         concreteMediator);
      // 讓中介者認識各個具體同事類物件
      concreteMediator.setC1(concreteColleague1);
      concreteMediator.setC2(concreteColleague2);
      // 具體同事類物件的訊息傳送都是通過中介者物件轉發
      concreteColleague1.sendMsg(“吃過飯了沒有?”);
      concreteColleague2.sendMsg(“沒有呢,你打算請客?”);
    }
}
輸出:
同事2得到訊息:吃過飯了沒有?     
同事1得到訊息:沒有呢,你打算請客?

 

 

享元模式(FlyWeight)

運用共享技術有效地支援大量細粒度的物件。

也就是說在一個系統中如果有多個相同的物件,那麼只共享一份就可以了,不必每個都去例項化一個物件。

享元模式可以避免大量非常相似類的開銷。在程式設計中,有時需要生成大量細粒度的類例項來表示資料。如果能發現這些例項除了幾個引數外基本上都是相同的,有時就能夠大幅度地減少需要例項化的類的數量。如果能把那些引數移到類例項的外面,在方法呼叫時將它們傳遞進來,就可以通過共享大幅度地減少單個例項的數目。

 

 

什麼時候用享元模式:如果一個應用程式使用了大量的物件,而大量的這些物件造成了很大的儲存開銷就應該考慮使用;還有就是物件的大多數狀態可以外部狀態,如果刪除物件的外部狀態,那麼可以用相對較少的共享物件取代很多組物件,此時可以考慮使用享元模式。

String型別,資料庫連線池,執行緒池等即是用享元模式的應用

 

 

 

//所有具體享元類的超類,接受並作用於外部狀態
public abstract class FlyWeight {
    public abstract void operation(int extrinsicState);
}
class ConcreteFlyWeight extends FlyWeight {
    @Override
    public void operation(int extrinsicState) {
       System.out.println("具體FlyWeight:"   extrinsicState);
    }
}
class UnsharedConcreteFlyWeight extends FlyWeight {
    @Override
    public void operation(int extrinsicState) {
       System.out.println("不共享的具體FlyWeight:"   extrinsicState);
    }
}
//享元工廠
public class FlyWeightFactory {
    private HashMap<String, FlyWeight> flyWeights = new HashMap<String, FlyWeight>();
    public FlyWeight getFlyWeight(String key) {
      if (!flyWeights.containsKey(key)) {
         flyWeights.put(key, new ConcreteFlyWeight());
      }
      return flyWeights.get(key);
    }
}
public class FlyWeightClient {
    public static void main(String[] args) {
   int extrinsicState = 22;
   FlyWeightFactory f = new FlyWeightFactory();
   FlyWeight fx = f.getFlyWeight("X");
   fx.operation(--extrinsicState);
   FlyWeight fy = f.getFlyWeight("Y");
   fy.operation(--extrinsicState);
   FlyWeight fz = f.getFlyWeight("Z");
   fz.operation(--extrinsicState);
   FlyWeight uf = new UnsharedConcreteFlyWeight();
   uf.operation(--extrinsicState);
    }
}
輸出:
具體FlyWeight:21   
具體FlyWeight:20   
具體FlyWeight:19   
不共享的具體FlyWeight:18

 

 

直譯器模式(Mediator)

給定一個語言,定義它的文法的一種表示,並定義一個直譯器,這個直譯器使用該表示來解釋語言中的句子。

 

如果一種特定型別的問題發生的頻率足夠高,那麼就可以考慮將該問題的各個例項表述為一個簡單語言中的句子。也就是說,通過構建一個直譯器,該直譯器解釋這些句子來解決該問題

 

 

//宣告一個抽象的解釋操作,這個介面為抽象語法樹中所有的節點所共享
public abstract class AbstractExpression {
    public abstract void interpret(Context context);
}
//實現與文法中的終結符相關聯的解釋操作,文法中每一個終結符都有一個具體終結表示式與之相對應
class TerminalExpression extends AbstractExpression{
    @Override
    public void interpret(Context context) {
        //這裡根據傳入的輸入物件進行翻譯,
        context.setOutput("“這是英語”");
        System.out.println("終端直譯器:" "原始資訊:"
                 context.getInput() "將會被翻譯為:" context.getOutput());
    }
}
class NonTerminalExpression extends AbstractExpression{
    @Override
    public void interpret(Context context) {
        //這裡根據傳入的輸入物件進行翻譯,
        context.setOutput("“123456”");
        System.out.println("非終端直譯器:" "原始資訊:"
                 context.getInput() "將會被翻譯為:" context.getOutput());
    }
}
//包含直譯器之外的一些全域性資訊
public class Context {
    private String input;
    private String output;
    public String getInput() { return input; }
    public void setInput(String input) { this.input = input; }
    public String getOutput() { return output; }
    public void setOutput(String output) { this.output = output; }
}
//構建表示該文法定義的語言中一個特定的句子的抽象語法樹,呼叫解釋操作
public class ExpressionClient {
    public static void main(String[] args) {
        Context context = new Context();
        context.setInput("this is English");
        List<AbstractExpression> list = new ArrayList<AbstractExpression>();
        list.add(new TerminalExpression());
        list.add(new NonTerminalExpression());
        list.add(new TerminalExpression());
        list.add(new TerminalExpression());
        for (AbstractExpression expression : list) {
            expression.interpret(context);
        }
    }
}
輸出:
終端直譯器:原始資訊:this is English將會被翻譯為:“這是英語”
非終端直譯器:原始資訊:this is English將會被翻譯為:“123456” 
終端直譯器:原始資訊:this is English將會被翻譯為:“這是英語”
終端直譯器:原始資訊:this is English將會被翻譯為:“這是英語”

 

 訪問者模式(Visitor)

表示一個作用於某物件結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作

 

訪問者模式適用於資料結構相對穩定的系統

目的:把處理從資料結構分離出來。如果系統有比較穩定的資料結構,又有易於變化的演算法的話,使用訪問者模式就是比較合適的,因為訪問者模式使得演算法操作的增加變得容易

優點:增加新的操作很容易,因為增加新的操作就意味著增加一個新的訪問者。訪問者模式將有關的行為集中到一個訪問者物件中。

缺點:增加新的資料結構變得困難了

 

事實上,我們很難找到資料結構不變化的情況,所以使用訪問者模式的機會也就不太多了。必須在真正需要它的時候才考慮使用它,不能濫用

 

 

//為該物件結構中ConcreteElement的每一個類宣告一個Visit操作
public abstract class Visitor {   //抽象狀態
    public abstract void visitConcreteElementA(ConcreteElementA concreteElementA);
    public abstract void visitConcreteElementB(ConcreteElementB concreteElementB);
}

class ConcreteVisitor1 extends Visitor {  //狀態:成功
    @Override
    public void visitConcreteElementA(ConcreteElementA concreteElementA) {  //男人的反應
       System.out.println(concreteElementA.getClass().getSimpleName() “被”
          this.getClass().getSimpleName() “訪問”);
    }
    @Override
    public void visitConcreteElementB(ConcreteElementB concreteElementB) {  //女人的反應
        System.out.println(concreteElementB.getClass().getSimpleName() “被”
            this.getClass().getSimpleName() “訪問”);
    }
}

class ConcreteVisitor2 extends Visitor {   //狀態:失敗
    @Override
    public void visitConcreteElementA(ConcreteElementA concreteElementA) {  //男人的反應
        System.out.println(concreteElementA.getClass().getSimpleName() “被”
            this.getClass().getSimpleName() “訪問”);
    }
    @Override
    public void visitConcreteElementB(ConcreteElementB concreteElementB) {   //女人的反應
        System.out.println(concreteElementB.getClass().getSimpleName() “被”
            this.getClass().getSimpleName() “訪問”);
    }
}

//定義一個accept操作,它以一個訪問者為引數
public abstract class Element { //抽象人
    public abstract void accept(Visitor visitor);
}
class ConcreteElementA extends Element {   //具體男人
    @Override
    public void accept(Visitor visitor) {  //男人接受狀態後的反應
       visitor.visitConcreteElementA(this);
    }
}
class ConcreteElementB extends Element {  //具體女人
    @Override
    public void accept(Visitor visitor) {  //女人接受狀態後的反應
       visitor.visitConcreteElementB(this);
    }
}

//提供一個高層的介面以允許訪問者訪問它的元素
public class ObjectStructure {   //定義一個容器用來儲存男人、女人這種不變的資料結構
    private List<Element> elements = new ArrayList<Element>();
    public void attach(Element element) {
       elements.add(element);
    }
    public void detach(Element element) {
       elements.remove(element);
    }
    public void accept(Visitor visitor) {  //遍歷所有資料結構執行他們的方法
        for (Element element : elements) {
            element.accept(visitor);
        }
    }
}

public class VisitorClient {
    public static void main(String[] args) {
      ObjectStructure o = new ObjectStructure();
      o.attach(new ConcreteElementA());
      o.attach(new ConcreteElementB());
      ConcreteVisitor1 visitor1 = new ConcreteVisitor1();
      ConcreteVisitor2 visitor2 = new ConcreteVisitor2();
      o.accept(visitor1);
      o.accept(visitor2);
    }
}
輸出:
ConcreteElementA被ConcreteVisitor1訪問
ConcreteElementB被ConcreteVisitor1訪問
ConcreteElementA被ConcreteVisitor2訪問
ConcreteElementB被ConcreteVisitor2訪問
 

 

 

設計模式總結

建立型模式

抽象工廠:提供一個建立一系列或相關依賴物件的介面,而無需指定它們具體的類

工廠方法:定義一個用於建立物件的介面,讓子類決定例項化哪一個類,工廠模式使一個類的例項化延遲到其子類

通常設計應該是從工廠方法開始,當設計者發現需要更大的靈活性時,設計便會向其他建立型模式演化。當設計者在設計標準之間進行權衡的時候,瞭解多個建立型模式可以給設計者更多的選擇餘地

 

建造者模式:將一個複雜物件的建立與它的表示分離,使得同樣的建立過程可以建立不同的表示

將一個複雜物件的建立與它的表示分離,這就可以很容易地改變一個產品的內部表示,並且使得構造程式碼和表示程式碼分開。這樣對於客戶來說,它無需關心產品的建立過程,而只要告訴建造者需要什麼,就能用同樣的構建過程建立不同的產品給客戶

 

原型模式:用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件

建立相應數目的原型並克隆它們通常比每次用合適的狀態手工例項化該類更方便一些

 

單例模式:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點

讓類自身負責儲存它的唯一例項。這個類可以保證沒有其他例項可以被建立,並且還提供了一個訪問該例項的方法,這樣就使得對唯一的例項可以嚴格地控制客戶怎樣以及何時訪問它。

 

為什麼需要建立型模式建立型模式隱藏了這些類的例項是如何被建立和放在一起,整個系統關於這些物件所知道的是由抽象類所定義的介面,這樣,建立型模式在建立了什麼、誰建立它、它是怎麼被建立的,以及何時建立這些方面提供了很大的靈活性

 

怎麼理解內聚與耦合內聚性描述的是一個例程內部組成部分之間相互聯絡的緊密程度。而耦合性描述的是一個例程與其他例程之間聯絡的緊密程度。軟體開發的目標應該是建立這樣的例程:內部完整,也就是高內聚,而與其他例程之間的聯絡則是小巧、直接、可見、靈活的,這就是鬆耦合,即高內聚,鬆耦合

 

結構型模式

介面卡模式:將一個類的介面轉換成客戶希望的另外一個介面。介面卡使得原本由於介面不相容而不能一起工作的那些類可以一起工作。

想使用一個已經存在的類,而它的介面不符合要求,或者希望建立一個可以複用的類,該類可以與其他不相關的類或不可預見的類協同工作。

 

橋接模式:將抽象部分與它的實現部分分離,使它們都可以獨立地變化。

繼承體系中,有兩個甚至多個方向的變化,那麼就解耦這些不同方向的變化,通過物件組合的方式,把兩個角色之間的繼承關係改為組合關係,從而使這兩者可以應對各自獨立的變化,正符合合成複用原則,找出變化並封裝之。

 

組合模式:將物件組合成樹形結構以表示“部分-整體”的層次結構,組合模式使得用於對單個物件和組合物件的使用具有一致性。

擅長於表示物件的部分與整體的層次結構,客戶可以一致地使用組合結構和單個物件。任何用到基本物件的地方都可以使用組合物件

 

裝飾者模式:動態地給一個物件新增一些額外的職責,就增加的功能來說,裝飾者模式相比生成子類更加靈活

子類多半隻是為某個物件增加一些職責,此時通過裝飾的方式,可以更加靈活、以動態、透明的方式給單個物件新增職責,並在不需要時,撤銷相應的職責。

 

外觀模式:為子系統中的一組介面提供一個一致的介面,外觀模式定義了一個高層介面,這個介面使得這一子系統更加容易使用

讓一個軟體中的子系統間的通訊和相互依賴關係達到最小,而具體方法就是引入一個外觀物件,它為子系統間提供了一個單一而簡單的屏障

 

享元模式:運用共享技術有效地支援大量細粒度的物件

 

代理模式:為其他物件提供一種代理以控制對這個物件的訪問

代理與外觀的主要區別:代理物件代表一個單一物件而外觀物件代表一個子系統;代理的客戶物件無法直接訪問目標物件,由代理提供對單獨的目標物件的訪問控制,而外觀的客戶物件可以直接訪問子系統中的各個物件,但通常由外觀物件提供對子系統各元件功能的簡化的共同層次的呼叫介面

代理與介面卡的主要區別:代理是一種原來物件的代表,其他需要與這個物件打交道的操作都是和這個代表交涉。而介面卡則不需要虛構出一個代表者,只需要應付特定使用目的,將原來的類進行一些組合

 

行為型模式

觀察者模式:定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新

 

模板方法:定義一個操作的演算法骨架,而將一些步驟延遲到子類中,模板方法使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟

由一個抽象類組成,這個抽象類定義了需要覆蓋的可能有不同實現的模板方法,每個從這個抽象類派生的具體類將為此模板實現新方法

 

命令模式:將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化,可以對請求排隊或記錄請求日誌,以及支援可撤銷的操作。

用於分離請求者與實現者,將呼叫操作的物件與知道如何實現該操作的物件解耦,傳送者可以在傳送完就完事了。可以在不同時刻指定、排列和執行請求,可在實施操作前將狀態儲存起來,以便支援取消/重做的操作。還可以記錄整個操作的日誌,也可以支援事務

 

狀態模式:允許一個物件在其內部狀態改變時改變它的行為,讓物件看起來似乎修改了它的類

對於條件分支的擴充套件性變化,狀態模式提供了一個更好的辦法來組織與特定狀態相關的程式碼,決定狀態轉移的邏輯不在單塊的if或switch中,而是分佈在各個狀態子類之間,由於所有與狀態相關的程式碼都存在於某個狀態子類中,所以通過定義新的子類可以很容易地增加新的狀態和轉換

 

職責鏈模式:使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它為之

有多個物件可以處理一個請求,哪個物件處理該請求實現並不知道,要在執行時刻自動確定,此時,最好的辦法就是讓請求傳送者與具體處理者分離,讓客戶在不明確指定接受者的情況下,提交一個請求,然後由所有能處理這請求的物件連成一條鏈,並沿著這條鍵傳遞該請求,直到有一個物件處理它為止

 

 

 

直譯器模式:給定一個語言,定義它的文法的一種表示,並定義一個直譯器,這個直譯器使用該表示來解釋語言中的句子

如果一種特定型別的問題發生的頻率足夠高,那麼就可以考慮將該問題的各個例項表述為一個簡單語言中的句子。也就是說,通過構建一個直譯器,該直譯器解釋這些句子來解決該問題

 

中介者模式:用一箇中介物件來封裝一系列的物件互動。中介者使各物件不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動

將集體行為封裝一個單獨的中介者物件來避免這個問題,中介者負責控制和協調一組物件間的互動。中介者充當一箇中介以使組中的物件不再相互顯式引用。這些物件僅知道中介者,從而減少了相互連線的數目,符合最小知道原則

 

訪問者模式:作用於某物件結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作

訪問者增加具體的Element是困難的,但增加依賴於複雜物件結構的構件的操作就變得容易。僅需增加一個新的訪問者即可在一個物件結構上定義一個新的操作

 

策略模式:定義一系列的演算法,把它們一個個封裝起來,並且使它們可相互替換。本模式使得演算法可獨立於使用它的客戶而變化

 

備忘錄模式:在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態,這樣以後就可將該物件恢復到原先儲存的狀態

可以避免暴露一些只應由物件A管理卻又必須儲存在物件A之外的資訊。備忘錄模式把可能很複雜的物件A的內部資訊對其他物件遮蔽起來,從而保持了封裝邊界

 

迭代器模式:提供一種方法順序訪問一個聚合物件中各個元素,而又不需暴露該物件的內部表示

關鍵思想是將對列表的訪問和遍歷從列表物件中分離出來並放入一個迭代器物件中,迭代器類定義了一個訪問該列表元素的介面。迭代器物件負責跟蹤當前的元素,並且知道哪些元素已經遍歷過了

 

 

較常使用的模式:工廠方法模式、觀察者模式、外觀模式、策略模式、介面卡模式

 

參考連結:

http://blog.csdn.net/jason0539/article/details/44956775#comments

https://www.jianshu.com/p/93bc5aa1f887