Java學習--設計模式之行為型模式(一)


一、簡介

  行為型模式:這些設計模式特別關注對象之間的通信。包括:責任鏈模式(Chain of Responsibility Pattern)、命令模式(Command Pattern)、解釋器模式(Interpreter Pattern)、迭代器模式(Iterator Pattern)、中介者模式(Mediator Pattern)、備忘錄模式(Memento Pattern)、觀察者模式(Observer Pattern)、狀態模式(State Pattern)、空對象模式(Null Object Pattern)、策略模式(Strategy Pattern)、模板模式(Template Pattern)、訪問者模式(Visitor Pattern);

 

二、責任鏈模式(Chain of Responsibility Pattern)

  1、概念

   顧名思義,責任鏈模式(Chain of Responsibility Pattern)為請求創建了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬於行為型模式。在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求,那么它會把相同的請求傳給下一個接收者,依此類推。

  2、簡介

   意圖:避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,並且沿着這條鏈傳遞請求,直到有對象處理它為止。

   主要解決:職責鏈上的處理者負責處理請求,客戶只需要將請求發送到職責鏈上即可,無須關心請求的處理細節和請求的傳遞,所以職責鏈將請求的發送者和請求的處理者解耦了。

   何時使用:在處理消息的時候以過濾很多道。

   如何解決:攔截的類都實現統一接口。

   關鍵代碼:Handler 里面聚合它自己,在 HanleRequest 里判斷是否合適,如果沒達到條件則向下傳遞,向誰傳遞之前 set 進去。

   應用實例: 

    (1)、JS 中的事件冒泡。

    (2)、JAVA WEB 中 Apache Tomcat 對 Encoding 的處理,Struts2 的攔截器,jsp servlet 的 Filter。

   優點: 

    (1)、降低耦合度。它將請求的發送者和接收者解耦。

    (2)、簡化了對象。使得對象不需要知道鏈的結構。

    (3)、增強給對象指派職責的靈活性。通過改變鏈內的成員或者調動它們的次序,允許動態地新增或者刪除責任。

    (4)、增加新的請求處理類很方便。

   缺點: 

    (1)、不能保證請求一定被接收。

    (2)、系統性能將受到一定影響,而且在進行代碼調試時不太方便,可能會造成循環調用。

    (3)、可能不容易觀察運行時的特征,有礙於除錯。

   使用場景: 

    (1)、有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定。

    (2)、在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。

    (3)、可動態指定一組對象處理請求。

   注意事項:在 JAVA WEB 中遇到很多應用。

  3、實例

   我們創建抽象類 AbstractLogger,帶有詳細的日志記錄級別。然后我們創建三種類型的記錄器,都擴展了 AbstractLogger。每個記錄器消息的級別是否屬於自己的級別,如果是則相應地打印出來,否則將不打印並把消息傳給下一個記錄器。

    

    (1)、創建抽象的記錄器類

public abstract class AbstractLogger {
   public static int INFO = 1;
   public static int DEBUG = 2;
   public static int ERROR = 3;

   protected int level;

   //責任鏈中的下一個元素
   protected AbstractLogger nextLogger;

   public void setNextLogger(AbstractLogger nextLogger){
      this.nextLogger = nextLogger;
   }

   public void logMessage(int level, String message){
      if(this.level <= level){
         write(message);
      }
      if(nextLogger !=null){
         nextLogger.logMessage(level, message);
      }
   }

   abstract protected void write(String message);
    
}

    (2)、創建擴展了該記錄類的實體類

public class ConsoleLogger extends AbstractLogger {

   public ConsoleLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {        
      System.out.println("Standard Console::Logger: " + message);
   }
}
public class ErrorLogger extends AbstractLogger {

   public ErrorLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {        
      System.out.println("Error Console::Logger: " + message);
   }
}
public class FileLogger extends AbstractLogger {

   public FileLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {        
      System.out.println("File::Logger: " + message);
   }
}

    (3)、創建不同類型的記錄器。賦予它們不同的錯誤級別,並在每個記錄器中設置下一個記錄器。每個記錄器中的下一個記錄器代表的是鏈的一部分。

public class ChainPatternDemo {
    
   private static AbstractLogger getChainOfLoggers(){

      AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
      AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
      AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

      errorLogger.setNextLogger(fileLogger);
      fileLogger.setNextLogger(consoleLogger);

      return errorLogger;    
   }

   public static void main(String[] args) {
      AbstractLogger loggerChain = getChainOfLoggers();

      loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");

      loggerChain.logMessage(AbstractLogger.DEBUG,  "This is an debug level information.");

      loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information.");
   }
}

    (4)、驗證輸出

1 Standard Console::Logger: This is an information.
2 File::Logger: This is an debug level information.
3 Standard Console::Logger: This is an debug level information.
4 Error Console::Logger: This is an error information.
5 File::Logger: This is an error information.
6 Standard Console::Logger: This is an error information.

 

三、命令模式(Command Pattern)

  1、概念

   命令模式(Command Pattern)是一種數據驅動的設計模式,它屬於行為型模式。請求以命令的形式包裹在對象中,並傳給調用對象。調用對象尋找可以處理該命令的合適的對象,並把該命令傳給相應的對象,該對象執行命令。

  2、簡介

   意圖:將一個請求封裝成一個對象,從而使您可以用不同的請求對客戶進行參數化。

   主要解決:在軟件系統中,行為請求者與實現者通常是一種緊耦合的關系,但某些場合,比如需要對行為進行記錄、撤銷或重做、事務等處理時,這種無法抵御變化的緊耦合的設計就不太合適。

   如何解決:通過調用者調用接受者執行命令,順序:調用者→接受者→命令。

   優點: 

    (1)、降低了系統耦合度。

    (2)、新的命令可以很容易添加到系統中去。

   缺點:使用命令模式可能會導致某些系統有過多的具體命令類。

   使用場景:認為是命令的地方都可以使用命令模式,比如: 1、GUI 中每一個按鈕都是一條命令。 2、模擬 CMD。

   注意事項:系統需要支持命令的撤銷(Undo)操作和恢復(Redo)操作,也可以考慮使用命令模式,見命令模式的擴展。

  3、實例

   我們首先創建作為命令的接口 Order,然后創建作為請求的 Stock 類。實體命令類 BuyStock 和 SellStock,實現了 Order 接口,將執行實際的命令處理。創建作為調用對象的類 Broker,它接受訂單並能下訂單。Broker 對象使用命令模式,基於命令的類型確定哪個對象執行哪個命令。演示類 CommandPatternDemo,我們的演示類使用 Broker 類來演示命令模式。

    

    (1)、創建一個命令接口

public interface Order {
   void execute();
}

    (2)、創建一個請求類

public class Stock {
    
   private String name = "ABC";
   private int quantity = 10;

   public void buy(){
      System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] bought");
   }
   public void sell(){
      System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] sold");
   }
}

    (3)、創建實現了 Order 接口的實體類

public class BuyStock implements Order {
   private Stock abcStock;

   public BuyStock(Stock abcStock){
      this.abcStock = abcStock;
   }

   public void execute() {
      abcStock.buy();
   }
}
public class SellStock implements Order {
   private Stock abcStock;

   public SellStock(Stock abcStock){
      this.abcStock = abcStock;
   }

   public void execute() {
      abcStock.sell();
   }
}

    (4)、創建命令調用類

import java.util.ArrayList;
import java.util.List;

   public class Broker {
   private List<Order> orderList = new ArrayList<Order>(); 

   public void takeOrder(Order order){
      orderList.add(order);        
   }

   public void placeOrders(){
      for (Order order : orderList) {
         order.execute();
      }
      orderList.clear();
   }
}

    (5)、使用 Borker 類來接受並執行命令

public class CommandPatternDemo {
   public static void main(String[] args) {
      Stock abcStock = new Stock();

      BuyStock buyStockOrder = new BuyStock(abcStock);
      SellStock sellStockOrder = new SellStock(abcStock);

      Broker broker = new Broker();
      broker.takeOrder(buyStockOrder);
      broker.takeOrder(sellStockOrder);

      broker.placeOrders();
   }
}

    (6)、演示結果

1 Stock [ Name: ABC, Quantity: 10 ] bought
2 Stock [ Name: ABC, Quantity: 10 ] sold

 

四、解釋器模式(Interpreter Pattern)

  1、概念

   解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式,它屬於行為型模式。這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。

  2、簡介

   意圖:給定一個語言,定義它的文法表示,並定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子。

   主要解決:對於一些固定文法構建一個解釋句子的解釋器。

   何時使用:如果一種特定類型的問題發生的頻率足夠高,那么可能就值得將該問題的各個實例表述為一個簡單語言中的句子。就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題。

   如何解決:構建語法樹,定義終結符與非終結符。

   關鍵代碼:構件環境類,包含解釋器之外的一些全局信息,一般是 HashMap。

   應用實例:編譯器、運算表達式計算。

   優點: 

    (1)、可擴展性比較好,靈活。

    (2)、增加了新的解釋表達式的方式。

    (3)、易於實現簡單文法。

   缺點: 

    (1)、可利用場景比較少。

    (2)、對於復雜的文法比較難維護。

    (3)、解釋器模式會引起類膨脹。

    (4)、解釋器模式采用遞歸調用方法。

   使用場景: 

    (1)、可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹。

    (2)、一些重復出現的問題可以用一種簡單的語言來進行表達。

    (3)、一個簡單語法需要解釋的場景。

   注意事項:可利用場景比較少,JAVA 中如果碰到可以用 expression4J 代替。

  3、實例

   我們將創建一個接口 Expression 和實現了 Expression 接口的實體類。定義作為上下文中主要解釋器的 TerminalExpression 類。其他的類 OrExpression、AndExpression 用於創建組合式表達式。演示類 InterpreterPatternDemo,我們的演示類使用 Expression 類創建規則和演示表達式的解析。

    

    (1)、創建一個表達式接口

public interface Expression {
   public boolean interpret(String context);
}

    (2)、創建實現了 Expression 接口的實體類

public class TerminalExpression implements Expression {
    
   private String data;

   public TerminalExpression(String data){
      this.data = data; 
   }

   @Override
   public boolean interpret(String context) {
      if(context.contains(data)){
         return true;
      }
      return false;
   }
}
public class OrExpression implements Expression {
     
   private Expression expr1 = null;
   private Expression expr2 = null;

   public OrExpression(Expression expr1, Expression expr2) { 
      this.expr1 = expr1;
      this.expr2 = expr2;
   }

   @Override
   public boolean interpret(String context) {        
      return expr1.interpret(context) || expr2.interpret(context);
   }
}
public class AndExpression implements Expression {
     
   private Expression expr1 = null;
   private Expression expr2 = null;

   public AndExpression(Expression expr1, Expression expr2) { 
      this.expr1 = expr1;
      this.expr2 = expr2;
   }

   @Override
   public boolean interpret(String context) {        
      return expr1.interpret(context) && expr2.interpret(context);
   }
}

    (3)、InterpreterPatternDemo 使用 Expression 類來創建規則,並解析它們。

public class InterpreterPatternDemo {

   //規則:Robert 和 John 是男性
   public static Expression getMaleExpression(){
      Expression robert = new TerminalExpression("Robert");
      Expression john = new TerminalExpression("John");
      return new OrExpression(robert, john);        
   }

   //規則:Julie 是一個已婚的女性
   public static Expression getMarriedWomanExpression(){
      Expression julie = new TerminalExpression("Julie");
      Expression married = new TerminalExpression("Married");
      return new AndExpression(julie, married);        
   }

   public static void main(String[] args) {
      Expression isMale = getMaleExpression();
      Expression isMarriedWoman = getMarriedWomanExpression();

      System.out.println("John is male? " + isMale.interpret("John"));
      System.out.println("Julie is a married women? " + isMarriedWoman.interpret("Married Julie"));
   }
}

    (4)、演示結果

1 John is male? true
2 Julie is a married women? true

 

五、迭代器模式(Iterator Pattern)

  1、概念

   迭代器模式(Iterator Pattern)是 Java 和 .Net 編程環境中非常常用的設計模式。這種模式用於順序訪問集合對象的元素,不需要知道集合對象的底層表示。迭代器模式屬於行為型模式。

  2、簡介

   意圖:提供一種方法順序訪問一個聚合對象中各個元素, 而又無須暴露該對象的內部表示。

   主要解決:不同的方式來遍歷整個整合對象。

   何時使用:遍歷一個聚合對象。

   如何解決:把在元素之間游走的責任交給迭代器,而不是聚合對象。

   應用實例:JAVA 中的 iterator。

   優點: 

    (1)、它支持以不同的方式遍歷一個聚合對象。

    (2)、迭代器簡化了聚合類。

    (3)、在同一個聚合上可以有多個遍歷。

    (4)、在迭代器模式中,增加新的聚合類和迭代器類都很方便,無須修改原有代碼。

   缺點:由於迭代器模式將存儲數據和遍歷數據的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,這在一定程度上增加了系統的復雜性。

   使用場景: 

    (1)、訪問一個聚合對象的內容而無須暴露它的內部表示。

    (2)、需要為聚合對象提供多種遍歷方式。

    (3)、為遍歷不同的聚合結構提供一個統一的接口。

   注意事項:迭代器模式就是分離了集合對象的遍歷行為,抽象出一個迭代器類來負責,這樣既可以做到不暴露集合的內部結構,又可讓外部代碼透明地訪問集合內部的數據。

  3、實例

   我們將創建一個敘述導航方法的 Iterator 接口和一個返回迭代器的 Container 接口。實現了 Container 接口的實體類將負責實現 Iterator 接口。演示類 IteratorPatternDemo,我們的演示類使用實體類 NamesRepository 來打印 NamesRepository 中存儲為集合的 Names。

    

    (1)、創建接口

public interface Iterator {
   public boolean hasNext();
   public Object next();
}

    (2)、創建實現了 Container 接口的實體類。該類有實現了 Iterator 接口的內部類 NameIterator。

public class NameRepository implements Container {
   public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};

   @Override
   public Iterator getIterator() {
      return new NameIterator();
   }

   private class NameIterator implements Iterator {

      int index;

      @Override
      public boolean hasNext() {
         if(index < names.length){
            return true;
         }
         return false;
      }

      @Override
      public Object next() {
         if(this.hasNext()){
            return names[index++];
         }
         return null;
      }        
   }
}

    (3)、使用 NameRepository 來獲取迭代器,並打印名字。

public class IteratorPatternDemo {
    
   public static void main(String[] args) {
      NameRepository namesRepository = new NameRepository();

      for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
         String name = (String)iter.next();
         System.out.println("Name : " + name);
      }     
   }
}

    (4)、演示結果

1 Name : Robert
2 Name : John
3 Name : Julie
4 Name : Lora

 

PS:因作者能力有限,如有誤還請諒解;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM