【軟件構造】第五章第三節 可複用的設計模式

 第五章第三節 可複用的設計模式

除了Framework,5-2節所討論的其餘技術都過於「基礎」和「細小」,有沒有辦法作更大規模的複用設計? 
本節將介紹幾種典型的「面向複用」的設計模式,設計模式更強調多個類/對象之間的關係和交互過程—比接口/類複用的粒度更大。html

Outline

  • 結構型模式:Structural patterns
    • 適配器模式(Adapter)
    • 裝飾器模式(Decorator )
    • 外觀模式(Facade)
  • 行爲類模式:Behavioral patterns 
    • 策略模式(Strategy)
    • 模板方法模式(Template method)
    • 迭代器模式( Iterator)

Notes

## 結構型模式:Structural patterns

【適配器模式(Adapter)】java

  • 目的:將某個類/接口轉換爲用戶指望的其餘形式。
  • 含義:適配器模式是做爲兩個互不相容的接口的橋樑,將某個類/接口轉換爲client指望的其餘形式。適配器模式使得本來因爲接口不兼容而不能一塊兒工做的的那些類/接口能夠一塊兒工做。
  • 用途:主要解決在軟件系統中,須要將現存的類放到新的環境中,而環境要求的接口是現對象不能知足的。
  • 實現方法:經過增長一個新的接口,將已存在的子類封裝起來,client直接面向接口編程,歷來隱藏了具體子類。適配器繼承或依賴已有的對象,實現想要的目標接口。
  • 對象:將舊組件重用到新系統(也稱爲「包裝器」)。
  • 模型:

  •  實例:

   問題描述:其中LegacyRectangle是已有的類(須要傳入矩形的一個頂點、長和寬),可是與client要求的接口不一致(須要給出對角線兩個頂點座標),咱們此時創建一個新的接口Shape以供客戶端調用,用戶經過Shape接口傳入兩個點的座標。Rectangle做爲Adapter,實現該抽象接口,經過具體的方法實現適配。算法

  在不適用適配器時:會發生委派不相容。數據庫

  使用了適配器後就可以解決上述問題:編程

 

【裝飾器模式(Decorator)】設計模式

  • 含義:
    • 裝飾器模式容許向一個現有的對象添加新的功能,同時又不改變其結構,它是做爲一個現有的類的一個包裝。
    • 這種模式建立了一個裝飾類,用來包裝原有的類,並在保證類方法簽名完整性的前提下,提供額外的功能。
    • 裝飾模式是繼承的一個代替模式,裝飾模式能夠動態地給一個對象添加一些額外的職能。就增長功能來講,裝飾器模式比生成子類更爲靈活。
  • 主要解決:通常的,咱們爲了擴展一個類常用繼承方式實現,因爲繼承爲類引入靜態特徵,而且隨着擴展功能的增多,子類會很膨脹。
  • 實現方法:將具體功能職責劃分,對每個特性構造子類,經過委派機制增長到對象上。
  • 用途:爲對象增長不一樣側面的特性。
  • 優勢:裝飾類和被裝飾類能夠獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式能夠動態擴展一個實現類的功能。
  • 缺點:多層裝飾比較複雜,客戶端須要一個具備多特性的object,須要一層一層的裝飾來實現
  • 模型:

  • 實例1:對於Shape接口和實現    參考:裝飾器模式 菜鳥教程
  • 實例2:
    • 咱們須要對Stack數據結構的各類擴展:
      • UndoStack:一個容許你撤銷先前的push或pop操做的棧
      • SecureStack:一個須要密碼的棧
      • SynchronizedStack:一個串行化併發訪問的棧
    • 咱們能夠採用繼承的方式來解決。以後咱們又須要任意可組合的擴展:
      • SecureUndoStack:須要密碼的堆棧,而且還能夠撤消之前的操做
      • SynchronizedUndoStack:一個堆棧,用於序列化併發訪問,還可讓您撤銷先前的操做
      • SecureSynchronizedStack:須要密碼的堆棧,並用於序列化併發訪問的操做
    • 咱們又怎麼處理呢?繼承層次結構?多繼承?可是會顯得很麻煩,這時候裝飾器模式就能夠很好地解決這一問題。

 

咱們須要一層層具備多種特徵的object,經過一層層的裝飾來實現:安全

Stack s = new ArrayStack(); //構建一個普通的堆棧
UndoStack s = new UndoStack(new ArrayStack()); //構建撤消堆棧
SecureStack s = new SecureStack( new SynchronizedStack( new UndoStack(s)))//構建安全的同步撤消堆棧
  • 裝飾器 vs. 繼承
    • 裝飾器在運行時組成特徵;繼承在編譯時組成特徵。
    • 裝飾器由多個協做對象組成;繼承產生一個明確類型的對象。
    • 能夠混合和匹配多個裝飾;多重繼承在概念上是困難的。
  • java.util.Collections中也有一些裝飾器模式: 
  • 將mutable list 變爲 immutable list:
    static List<T>  unmodifiableList(List<T> lst); 
    static Set<T>   unmodifiableSet( Set<T> set); 
    static Map<K,V> unmodifiableMap( Map<K,V> map);  
  • Similar for synchronization:
    static List<T>  synchronizedList(List<T> lst); 
    static Set<T>   synchronizedSet( Set<T> set);
    static Map<K,V> synchronizedMap( Map<K,V> map);

     

【外觀模式(Facade Pattern)】數據結構

  • 含義:外觀模式隱藏系統的複雜性,並向客戶端提供了一個客戶端能夠訪問系統的接口。這種類型的設計模式屬於結構型模式,它向現有的系統添加一個接口,來隱藏系統的複雜性。這種模式涉及到一個單一的類,該類提供了客戶端請求的簡化方法和對現有系統類方法的委託調用。
  • 意圖:爲子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
  • 主要解決:下降訪問複雜系統的內部子系統時的複雜度,簡化客戶端與之的接口。
  • 實現方法:提供一個統一的接口來取代一系列小接口調用,至關於對複雜系統作了一個封裝,簡化客戶端使用。便於客戶端學習,解耦 。
  • 用途:爲了解決客戶端須要經過一個簡化的接口來訪問複雜系統內的功能這一問題提出的。
  • 優勢: 一、減小系統相互依賴。 二、提升靈活性。 三、提升了安全性。
  • 缺點:不符合開閉原則,若是要改東西很麻煩,繼承重寫都不合適。
  • 模式:

 

  假設咱們有一個具備一組接口的應用程序來使用MySql / Oracle數據庫,並生成不一樣類型的報告,如HTML報告,PDF報告等。 
  所以,咱們將有不一樣的接口集合來處理不一樣類型的數據庫。如今客戶端應用程序可使用這些接口來獲取所需的數據庫鏈接並生成報告。可是,當複雜性增長或界面行爲名稱混淆時,客戶端應用程序將難以管理它。 
  所以,咱們能夠在這裏應用Facade模式,並在現有界面的頂部提供包裝界面以幫助客戶端應用程序。ide

Two Helper Classes for MySQL and Oracle: 分別封裝了客戶端所需的功能

A façade class:

客戶端代碼:

  咱們能夠看到採用了Facade設計模式的客戶端代碼簡潔了許多,更方便客戶使用。

 

##  行爲類模式:Behavioral patterns 

 【策略模式( Strategy)】

  • 含義:在策略模式中,一個類的行爲或算法能夠在運行時更改。在策略模式中,咱們建立表示各類模式的對象和一個行爲隨着策略對象改變而改變的 context對象,策略對象改變 context對象的執行算法。
  • 用途:針對特定任務存在不一樣的算法,但客戶端能夠根據動態上下文在運行時切換算法。  
  • 實現方法:爲算法建立一個接口,併爲算法的每一個變體建立一個實現類。
  • 解決方法:將這些算法封裝成一個一個的類,任意地替換
  • 優勢:算法能夠自由切換、避免使用多重條件判斷、擴展性良好。
  • 缺點:策略類會增多、全部策略類都須要對外暴露。
  • 模式

  • 實例一:Strategy接口與Contex實現  參考:策略模式 菜鳥驛站
  • 實例二:對於Lab3中的內容進行抽象
  • 問題描述:對於一個圖,咱們能夠計算圖中頂點的不一樣類型的中心度,如degreeCentrality(點度中心度)、closenessCentrality(接近中心度)和betweenCentrality(中介中心度)。
    • 用Strategy原則:
    • CentralityStrategy.java
      public interface CentralityStrategy {    //對應上圖的 Strategy<<interface>>    
          public abstract centrality();    
      }    
    • 而後經過三個具體的實現類來實現這個接口:
    • degreeCentralityStrategy.java
       1 public class degreeCentralityStrategy<L extends Vertex, E extends Edge> implements CentralityStrategy {    
       2         
       3     private final Graph<L, E> g;    
       4     private final L v;    
       5         
       6     public degreeCentralityStrategy(Graph<L, E> g, L v) {    
       7         this.g = g;    
       8         this.v = v;    
       9     }    
      10         
      11     @Override    
      12     public double centrality() {    
      13         return GraphMetrics.degreeCentrality(g, v);    
      14     }    
      15 }  
    • closenessCentralityStrategy.java
       1 public class closenessCentralityStrategy<L extends Vertex, E extends Edge> implements CentralityStrategy {    
       2     
       3     private final Graph<L, E> g;    
       4     private final L v;    
       5         
       6     public closenessCentralityStrategy(Graph<L, E> g, L v) {    
       7         this.g = g;    
       8         this.v = v;    
       9     }    
      10         
      11     @Override    
      12     public double centrality() {    
      13         return GraphMetrics.closenessCentrality(g, v);    
      14     }    
      15 }    
    • betweennessCentralityStrategy.java
       1 public class betweennessCentralityStrategy<L extends Vertex, E extends Edge> implements CentralityStrategy{    
       2         
       3     private final Graph<L, E> g;    
       4     private final L v;    
       5         
       6     public betweennessCentralityStrategy(Graph<L, E> g, L v) {    
       7         this.g = g;    
       8         this.v = v;    
       9     }    
      10         
      11     @Override    
      12     public double centrality() {    
      13         return GraphMetrics.betweennessCentrality(g, v);    
      14     }    
      15 }  
    • 而後實現一個Context類 centralityContext.java
      public class CentralityContext {    
              
          public double centrality(CentralityStrategy centralityType) {    
              return centralityType.centrality();    
          }    
      }   
    • 可使用Context來查看當它改變策咯Strategy時的行爲變化,以下Main.java
       1 public class Main {    
       2     
       3     public static void main(String[] args) {    
       4     
       5         Graph<Vertex, Edge> graph = GraphFactory.createGraph("test/graph/GraphPoetTest.txt");    
       6         String[] strings = {"F", "24"};    
       7         Vertex vertex1 = VertexFactory.createVertex("to", "Word", strings);    
       8         CentralityContext context = new CentralityContext();    
       9         double degree = context.centrality(new DegreeCentralityStrategy<>(graph, vertex1));    
      10         double closeness = context.centrality(new ClosenessCentralityStrategy<>(graph, vertex1));    
      11         double betweenness = context.centrality(new BetweennessCentralityStrategy<>(graph, vertex1));    
      12         System.out.println(degree);    
      13         System.out.println(closeness);    
      14         System.out.println(betweenness);    
      15     }    
      16 } 

       

 【模板模式(Template method)】

  • 含義:在模板模式中,一個抽象類公開定義了執行它的方法的方式/模式,它的子類能夠按照須要重寫實現方法,但調用將以抽象類中定義的方法進行。
  • 意圖:定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。
  • 實現方法:共性的步驟在抽象類內公共實現,差別化的步驟在各個子類中實現。通常使用繼承和重寫實現模板模式。
  • 優勢:封裝不變部分,擴展可變部分;提取公共代碼,便於維護;行爲由父類控制嗎,子類實現
  • 缺點:每個不一樣的實現都須要一個子類來實現,致使類的個數增長,使得系統更加龐大。
  • 模式:

  • 實例一:Game抽象類和重寫  參考 模板模式 菜鳥教程
  • 實例二:對於Lab3中的內容進行抽象
  • 問題描述:邊有不少類型:簡單的分爲:有向邊、無向邊
    • Edge.java //建立擴展了上述類的實現類
       1 public abstract class Edge {    
       2         
       3     private final String label;    
       4     private final double weight;    
       5         
       6     //the constructor    
       7     public Edge(String label, double weight) {    
       8         this.label = label;    
       9         this.weight = weight;    
      10     }    
      11     
      12 public abstract boolean addVertices(List<Vertex> vertices);    
      13     
      14 public abstract boolean containVertex(Vertex v);    
      15     
      16 public abstract Set<Vertex> vertices();    
    • DirectedEdge.java
       1 public class DirectedEdge extends Edge{    
       2         
       3     private Vertex source;    
       4     private Vertex target;    
       5         
       6     //the constructor    
       7     public DirectedEdge(String label, double weight) {    
       8         super(label, weight);    
       9     }    
      10     
      11         @Override    
      12     public boolean addVertices(List<Vertex> vertices) {    
      13         source = vertices.get(0);    
      14         target = vertices.get(1);    
      15         return true;    
      16     }    
      17         
      18     @Override    
      19     public boolean containVertex(Vertex v) {    
      20         return source.equals(v) || target.equals(v);    
      21     }    
      22         
      23     @Override    
      24     public Set<Vertex> vertices() {    
      25         Set<Vertex> set = new HashSet<Vertex>();    
      26         set.add(source);    
      27         set.add(target);    
      28         return set;    
      29     }    
    • UndirectedEdge.java
       1 public class UndirectedEdge extends Edge{    
       2     
       3     private Vertex vertex1;    
       4     private Vertex vertex2;    
       5         
       6     public UndirectedEdge(String label, double weight) {    
       7         super(label, weight);    
       8     }    
       9         
      10     @Override    
      11     public boolean addVertices(List<Vertex> vertices) {    
      12         vertex1 = vertices.get(0);    
      13         vertex2 = vertices.get(1);    
      14         return true;    
      15     }    
      16         
      17     @Override    
      18     public boolean containVertex(Vertex v) {    
      19         return vertex1.equals(v) || vertex2.equals(v);    
      20     }    
      21          
      22     @Override    
      23     public Set<Vertex> vertices() {    
      24         Set<Vertex> set = new HashSet<Vertex>();    
      25         set.add(vertex1);    
      26         set.add(vertex2);    
      27         return set;    
      28     }    

       

【迭代器模式( Iterator)】

  • 含義:這種模式用於順序訪問集合對象的元素,而又無需暴露該對象的內部表示。
  • 用途:解決 客戶須要統一的策略來訪問容器中的全部元素,與容器類型無關 
  • 實現方法:這種模式讓本身的集合類實現Iterable接口,並實現本身的獨特Iterator迭代器(hasNext, next, remove),容許客戶端利用這 個迭代器進行顯式或隱式的迭代遍歷。
  • 優勢: 一、它支持以不一樣的方式遍歷一個聚合對象。 二、迭代器簡化了聚合類。 三、在同一個聚合上能夠有多個遍歷。 四、在迭代器模式中,增長新的聚合類和迭代器類都很方便,無須修改原有代碼。
  • 缺點:因爲迭代器模式將存儲數據和遍歷數據的職責分離,增長新的聚合類須要對應增長新的迭代器類,類的個數成對增長,這在必定程度上增長了系統的複雜性。
  • 模式:

  • 實例:
    • Iterable接口:實現該接口的集合對象是可迭代遍歷的
      public interface Iterable<T> { 
          ... 
          Iterator<T> iterator(); 
      } 
    • Iterator接口:迭代器
      public interface Iterator<E> { 
          boolean hasNext(); 
          E next(); 
          void remove(); 
      }
    • 具體例子見下圖:

相關文章
相關標籤/搜索