設(shè)計(jì)模式之行為型模式

下面總結(jié)設(shè)計(jì)模式中的行為型模式:

1.責(zé)任鏈模式

顧名思義,責(zé)任鏈模式(Chain of Responsibility Pattern)為請(qǐng)求創(chuàng)建了一個(gè)接收者對(duì)象的鏈。這種模式給予請(qǐng)求的類(lèi)型,對(duì)請(qǐng)求的發(fā)送者和接收者進(jìn)行解耦。這種類(lèi)型的設(shè)計(jì)模式屬于行為型模式。
在這種模式中,通常每個(gè)接收者都包含對(duì)另一個(gè)接收者的引用。如果一個(gè)對(duì)象不能處理該請(qǐng)求,那么它會(huì)把相同的請(qǐng)求傳給下一個(gè)接收者,依此類(lèi)推。

介紹

意圖:避免請(qǐng)求發(fā)送者與接收者耦合在一起,讓多個(gè)對(duì)象都有可能接收請(qǐng)求,將這些對(duì)象連接成一條鏈,并且沿著這條鏈傳遞請(qǐng)求,直到有對(duì)象處理它為止。

主要解決:職責(zé)鏈上的處理者負(fù)責(zé)處理請(qǐng)求,客戶(hù)只需要將請(qǐng)求發(fā)送到職責(zé)鏈上即可,無(wú)須關(guān)心請(qǐng)求的處理細(xì)節(jié)和請(qǐng)求的傳遞,所以職責(zé)鏈將請(qǐng)求的發(fā)送者和請(qǐng)求的處理者解耦了。

何時(shí)使用:在處理消息的時(shí)候以過(guò)濾很多道。

如何解決:攔截的類(lèi)都實(shí)現(xiàn)統(tǒng)一接口。

關(guān)鍵代碼:Handler里面聚合它自己,在 HandlerRequest 里判斷是否合適,如果沒(méi)達(dá)到條件則向下傳遞,向誰(shuí)傳遞之前 set 進(jìn)去。

應(yīng)用實(shí)例: 1、紅樓夢(mèng)中的"擊鼓傳花"。 2、JS 中的事件冒泡。 3、JAVA WEB 中 Apache Tomcat 對(duì) Encoding 的處理,Struts2 的攔截器,jsp servlet 的 Filter。 4.JAVA 的異常鏈機(jī)制

優(yōu)點(diǎn): 1、降低耦合度。它將請(qǐng)求的發(fā)送者和接收者解耦。 2、簡(jiǎn)化了對(duì)象。使得對(duì)象不需要知道鏈的結(jié)構(gòu)。 3、增強(qiáng)給對(duì)象指派職責(zé)的靈活性。通過(guò)改變鏈內(nèi)的成員或者調(diào)動(dòng)它們的次序,允許動(dòng)態(tài)地新增或者刪除責(zé)任。 4、增加新的請(qǐng)求處理類(lèi)很方便。

缺點(diǎn): 1、不能保證請(qǐng)求一定被接收。 2、系統(tǒng)性能將受到一定影響,而且在進(jìn)行代碼調(diào)試時(shí)不太方便,可能會(huì)造成循環(huán)調(diào)用。 3、可能不容易觀(guān)察運(yùn)行時(shí)的特征,有礙于除錯(cuò)。

使用場(chǎng)景: 1、有多個(gè)對(duì)象可以處理同一個(gè)請(qǐng)求,具體哪個(gè)對(duì)象處理該請(qǐng)求由運(yùn)行時(shí)刻自動(dòng)確定。 2、在不明確指定接收者的情況下,向多個(gè)對(duì)象中的一個(gè)提交一個(gè)請(qǐng)求。 3、可動(dòng)態(tài)指定一組對(duì)象處理請(qǐng)求。

注意事項(xiàng):在 JAVA WEB 中遇到很多應(yīng)用。

責(zé)任鏈模式.png

實(shí)現(xiàn)

我們以銷(xiāo)售樓盤(pán)為例,客戶(hù)是Customer類(lèi),發(fā)送一個(gè)折扣請(qǐng)求給責(zé)任鏈。責(zé)任鏈由樓盤(pán)各級(jí)人員組成,都繼承自PriceHandler抽象類(lèi),自底向上為Sales(銷(xiāo)售)、Lead(銷(xiāo)售小組長(zhǎng))、Manager(銷(xiāo)售經(jīng)理)、Director(銷(xiāo)售總監(jiān))、VicePresident(銷(xiāo)售副總裁)、CEO(首席執(zhí)行官)。沿著責(zé)任鏈,能批準(zhǔn)折扣的力度依次上升。

步驟 1 創(chuàng)建PriceHandler抽象類(lèi)
PriceHandler.java

package com.imooc.pattern.cor.handler;

/*
 * 價(jià)格處理人,負(fù)責(zé)處理客戶(hù)折扣申請(qǐng)
 */
public abstract class PriceHandler {
    
    /*
     * 直接后繼,用于傳遞請(qǐng)求
     */
    protected PriceHandler successor;

    public void setSuccessor(PriceHandler successor) {
        this.successor = successor;
    }
    
    /*
     * 處理折扣申請(qǐng)
     */
    public abstract  void processDiscount(float discount);

}

步驟 2 創(chuàng)建PriceHandler的具體類(lèi)
Sales.java

package com.imooc.pattern.cor.handler;

/*
 * 銷(xiāo)售, 可以批準(zhǔn)5%以?xún)?nèi)的折扣
 */
public class Sales extends PriceHandler {

    @Override
    public void processDiscount(float discount) {
        if(discount <= 0.05){
            System.out.format("%s批準(zhǔn)了折扣:%.2f%n", this.getClass().getName(), discount);
        }else{
            successor.processDiscount(discount);
        }

    }

}

Lead.java

package com.imooc.pattern.cor.handler;

/*
 * 銷(xiāo)售小組長(zhǎng), 可以批準(zhǔn)15%以?xún)?nèi)的折扣
 */
public class Lead extends PriceHandler {

    @Override
    public void processDiscount(float discount) {
        if(discount<=0.15){
            System.out.format("%s批準(zhǔn)了折扣:%.2f%n",this.getClass().getName(),discount);
        }else{
            successor.processDiscount(discount);
        }

    }

}

Manager.java

package com.imooc.pattern.cor.handler;

/*
 * 銷(xiāo)售經(jīng)理, 可以批準(zhǔn)30%以?xún)?nèi)的折扣
 */
public class Manager extends PriceHandler {

    @Override
    public void processDiscount(float discount) {
        if(discount<=0.3){
            System.out.format("%s批準(zhǔn)了折扣:%.2f%n",this.getClass().getName(),discount);
        }else{
            successor.processDiscount(discount);
        }

    }

}

Director.java

package com.imooc.pattern.cor.handler;

/*
 * 銷(xiāo)售總監(jiān), 可以批準(zhǔn)40%以?xún)?nèi)的折扣
 */
public class Director extends PriceHandler {

    @Override
    public void processDiscount(float discount) {
        if(discount<=0.4){
            System.out.format("%s批準(zhǔn)了折扣:%.2f%n",this.getClass().getName(),discount);
        }else{
            successor.processDiscount(discount);
        }

    }

}

VicePresident.java

package com.imooc.pattern.cor.handler;


/*
 * 銷(xiāo)售副總裁, 可以批準(zhǔn)50%以?xún)?nèi)的折扣
 */
public class VicePresident extends PriceHandler {

    @Override
    public void processDiscount(float discount) {
        if(discount<=0.5){
            System.out.format("%s批準(zhǔn)了折扣:%.2f%n",this.getClass().getName(),discount);
        }else{
            successor.processDiscount(discount);
        }

    }

}

CEO.java

package com.imooc.pattern.cor.handler;

/*
 * CEO, 可以批準(zhǔn)55%以?xún)?nèi)的折扣
 * 折扣超出55%, 就拒絕申請(qǐng)
 */
public class CEO extends PriceHandler {

    @Override
    public void processDiscount(float discount) {
        if(discount<=0.55){
            System.out.format("%s批準(zhǔn)了折扣:%.2f%n",this.getClass().getName(),discount);
        }else{
            System.out.format("%s拒絕了折扣:%.2f%n", this.getClass().getName(),discount);
        }

    }

}

步驟 3 創(chuàng)建PriceHandlerFactory類(lèi)

package com.imooc.pattern.cor.handler;

public class PriceHandlerFactory {

    /*
     * 創(chuàng)建PriceHandler的工廠(chǎng)方法,類(lèi)似于構(gòu)建鏈表并返回表頭
     */
    public static PriceHandler createPriceHandler() {
        
        PriceHandler sales = new Sales();
        PriceHandler lead = new Lead();
        PriceHandler man = new Manager();
        PriceHandler dir = new Director();
        PriceHandler vp = new VicePresident();
        PriceHandler ceo = new CEO();
        
        sales.setSuccessor(lead);
        lead.setSuccessor(man);
        man.setSuccessor(dir);
        dir.setSuccessor(vp);
        vp.setSuccessor(ceo);
        
        return sales;
    }

}

步驟 4 創(chuàng)建Customer類(lèi)

package com.imooc.pattern.cor;

import java.util.Random;
import com.imooc.pattern.cor.handler.PriceHandler;
import com.imooc.pattern.cor.handler.PriceHandlerFactory;

/*
 * 客戶(hù),請(qǐng)求折扣
 */
public class Customer {
    
    private PriceHandler priceHandler;
    
    public void setPriceHandler(PriceHandler priceHandler) {
        this.priceHandler = priceHandler;
    }

    public void requestDiscount(float discount){
        priceHandler.processDiscount(discount);
    }
    
    
    public static void main(String[] args){
        Customer customer = new Customer();
        customer.setPriceHandler(PriceHandlerFactory.createPriceHandler());
        
        Random rand = new Random();
        
        for(int i=1;i<=100;i++){
            System.out.print(i+":");
            customer.requestDiscount(rand.nextFloat());
        }
        
        
    }
    

}

2.命令模式

命令模式(Command Pattern)是一種數(shù)據(jù)驅(qū)動(dòng)的設(shè)計(jì)模式,它屬于行為型模式。請(qǐng)求以命令的形式包裹在對(duì)象中,并傳給調(diào)用對(duì)象。調(diào)用對(duì)象尋找可以處理該命令的合適的對(duì)象,并把該命令傳給相應(yīng)的對(duì)象,該對(duì)象執(zhí)行命令。

介紹

意圖:將一個(gè)請(qǐng)求封裝成一個(gè)對(duì)象,從而使您可以用不同的請(qǐng)求對(duì)客戶(hù)進(jìn)行參數(shù)化。
主要解決:在軟件系統(tǒng)中,行為請(qǐng)求者與行為實(shí)現(xiàn)者通常是一種緊耦合的關(guān)系,但某些場(chǎng)合,比如需要對(duì)行為進(jìn)行記錄、撤銷(xiāo)或重做、事務(wù)等處理時(shí),這種無(wú)法抵御變化的緊耦合的設(shè)計(jì)就不太合適。
何時(shí)使用:在某些場(chǎng)合,比如要對(duì)行為進(jìn)行"記錄、撤銷(xiāo)/重做、事務(wù)"等處理,這種無(wú)法抵御變化的緊耦合是不合適的。在這種情況下,如何將"行為請(qǐng)求者"與"行為實(shí)現(xiàn)者"解耦?將一組行為抽象為對(duì)象,可以實(shí)現(xiàn)二者之間的松耦合。
如何解決:通過(guò)調(diào)用者調(diào)用接受者執(zhí)行命令,順序:調(diào)用者→接受者→命令。
關(guān)鍵代碼:定義三個(gè)角色:1、received 真正的命令執(zhí)行對(duì)象 2、Command 3、invoker 使用命令對(duì)象的入口
應(yīng)用實(shí)例:struts 1 中的 action 核心控制器 ActionServlet 只有一個(gè),相當(dāng)于 Invoker,而模型層的類(lèi)會(huì)隨著不同的應(yīng)用有不同的模型類(lèi),相當(dāng)于具體的 Command。
優(yōu)點(diǎn):1、降低了系統(tǒng)耦合度。 2、新的命令可以很容易添加到系統(tǒng)中去。
缺點(diǎn):使用命令模式可能會(huì)導(dǎo)致某些系統(tǒng)有過(guò)多的具體命令類(lèi)。
使用場(chǎng)景:認(rèn)為是命令的地方都可以使用命令模式,比如: 1、GUI 中每一個(gè)按鈕都是一條命令。 2、模擬 CMD。
注意事項(xiàng):系統(tǒng)需要支持命令的撤銷(xiāo)(Undo)操作和恢復(fù)(Redo)操作,也可以考慮使用命令模式,見(jiàn)命令模式的擴(kuò)展。

實(shí)現(xiàn)

我們首先創(chuàng)建作為命令的接口 Order,然后創(chuàng)建作為請(qǐng)求的 Stock 類(lèi)。實(shí)體命令類(lèi) BuyStock 和 SellStock,實(shí)現(xiàn)了 Order 接口,將執(zhí)行實(shí)際的命令處理。創(chuàng)建作為調(diào)用對(duì)象的類(lèi) Broker,它接受訂單并能下訂單。
Broker 對(duì)象使用命令模式,基于命令的類(lèi)型確定哪個(gè)對(duì)象執(zhí)行哪個(gè)命令。CommandPatternDemo,我們的演示類(lèi)使用 Broker 類(lèi)來(lái)演示命令模式。


步驟 1
創(chuàng)建一個(gè)命令接口。
Order.java

public interface Order {
   void execute();
}

步驟 2
創(chuàng)建一個(gè)請(qǐng)求類(lèi)。
Stock.java

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
創(chuàng)建實(shí)現(xiàn)了 Order 接口的實(shí)體類(lèi)。
BuyStock.java

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

SellStock.java

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

步驟 4
創(chuàng)建命令調(diào)用類(lèi)。
Broker.java

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
使用 Broker 類(lèi)來(lái)接受并執(zhí)行命令。
CommandPatternDemo.java

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
執(zhí)行程序,輸出結(jié)果:

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

3.解釋器模式

解釋器模式(Interpreter Pattern)提供了評(píng)估語(yǔ)言的語(yǔ)法或表達(dá)式的方式,它屬于行為型模式。這種模式實(shí)現(xiàn)了一個(gè)表達(dá)式接口,該接口解釋一個(gè)特定的上下文。這種模式被用在 SQL 解析、符號(hào)處理引擎等。

介紹

意圖:給定一個(gè)語(yǔ)言,定義它的文法表示,并定義一個(gè)解釋器,這個(gè)解釋器使用該標(biāo)識(shí)來(lái)解釋語(yǔ)言中的句子。
主要解決:對(duì)于一些固定文法構(gòu)建一個(gè)解釋句子的解釋器。
何時(shí)使用:如果一種特定類(lèi)型的問(wèn)題發(fā)生的頻率足夠高,那么可能就值得將該問(wèn)題的各個(gè)實(shí)例表述為一個(gè)簡(jiǎn)單語(yǔ)言中的句子。這樣就可以構(gòu)建一個(gè)解釋器,該解釋器通過(guò)解釋這些句子來(lái)解決該問(wèn)題。
如何解決:構(gòu)建語(yǔ)法樹(shù),定義終結(jié)符與非終結(jié)符。
關(guān)鍵代碼:構(gòu)件環(huán)境類(lèi),包含解釋器之外的一些全局信息,一般是 HashMap。
應(yīng)用實(shí)例:編譯器、運(yùn)算表達(dá)式計(jì)算。
優(yōu)點(diǎn):1、可擴(kuò)展性比較好,靈活。 2、增加了新的解釋表達(dá)式的方式。 3、易于實(shí)現(xiàn)簡(jiǎn)單文法。
缺點(diǎn):1、可利用場(chǎng)景比較少。 2、對(duì)于復(fù)雜的文法比較難維護(hù)。 3、解釋器模式會(huì)引起類(lèi)膨脹。 4、解釋器模式采用遞歸調(diào)用方法。
使用場(chǎng)景:1、可以將一個(gè)需要解釋執(zhí)行的語(yǔ)言中的句子表示為一個(gè)抽象語(yǔ)法樹(shù)。 2、一些重復(fù)出現(xiàn)的問(wèn)題可以用一種簡(jiǎn)單的語(yǔ)言來(lái)進(jìn)行表達(dá)。 3、一個(gè)簡(jiǎn)單語(yǔ)法需要解釋的場(chǎng)景。
注意事項(xiàng):可利用場(chǎng)景比較少,JAVA 中如果碰到可以用 expression4J 代替。

實(shí)現(xiàn)

我們將創(chuàng)建一個(gè)接口 Expression 和實(shí)現(xiàn)了 Expression 接口的實(shí)體類(lèi)。定義作為上下文中主要解釋器的 TerminalExpression 類(lèi)。其他的類(lèi) OrExpression、AndExpression 用于創(chuàng)建組合式表達(dá)式。
InterpreterPatternDemo,我們的演示類(lèi)使用 Expression 類(lèi)創(chuàng)建規(guī)則和演示表達(dá)式的解析。


步驟 1
創(chuàng)建一個(gè)表達(dá)式接口。
Expression.java

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

步驟 2
創(chuàng)建實(shí)現(xiàn)了上述接口的實(shí)體類(lèi)。
TerminalExpression.java

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;
   }
}

OrExpression.java

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);
   }
}

AndExpression.java

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 類(lèi)來(lái)創(chuàng)建規(guī)則,并解析它們。
InterpreterPatternDemo.java

public class InterpreterPatternDemo {
 
   //規(guī)則:Robert 和 John 是男性
   public static Expression getMaleExpression(){
      Expression robert = new TerminalExpression("Robert");
      Expression john = new TerminalExpression("John");
      return new OrExpression(robert, john);    
   }
 
   //規(guī)則:Julie 是一個(gè)已婚的女性
   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
執(zhí)行程序,輸出結(jié)果:

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

4.迭代器模式

迭代器模式(Iterator Pattern)是 Java 和 .Net 編程環(huán)境中非常常用的設(shè)計(jì)模式。這種模式用于順序訪(fǎng)問(wèn)集合對(duì)象的元素,不需要知道集合對(duì)象的底層表示。
迭代器模式屬于行為型模式。

介紹

意圖:提供一種方法順序訪(fǎng)問(wèn)一個(gè)聚合對(duì)象中各個(gè)元素, 而又無(wú)須暴露該對(duì)象的內(nèi)部表示。
主要解決:不同的方式來(lái)遍歷整個(gè)整合對(duì)象。
何時(shí)使用:遍歷一個(gè)聚合對(duì)象。
如何解決:把在元素之間游走的責(zé)任交給迭代器,而不是聚合對(duì)象。
關(guān)鍵代碼:定義接口:hasNext, next。
應(yīng)用實(shí)例:JAVA 中的 iterator。
優(yōu)點(diǎn):1、它支持以不同的方式遍歷一個(gè)聚合對(duì)象。 2、迭代器簡(jiǎn)化了聚合類(lèi)。 3、在同一個(gè)聚合上可以有多個(gè)遍歷。 4、在迭代器模式中,增加新的聚合類(lèi)和迭代器類(lèi)都很方便,無(wú)須修改原有代碼。
缺點(diǎn):由于迭代器模式將存儲(chǔ)數(shù)據(jù)和遍歷數(shù)據(jù)的職責(zé)分離,增加新的聚合類(lèi)需要對(duì)應(yīng)增加新的迭代器類(lèi),類(lèi)的個(gè)數(shù)成對(duì)增加,這在一定程度上增加了系統(tǒng)的復(fù)雜性。
使用場(chǎng)景:1、訪(fǎng)問(wèn)一個(gè)聚合對(duì)象的內(nèi)容而無(wú)須暴露它的內(nèi)部表示。 2、需要為聚合對(duì)象提供多種遍歷方式。 3、為遍歷不同的聚合結(jié)構(gòu)提供一個(gè)統(tǒng)一的接口。
注意事項(xiàng):迭代器模式就是分離了集合對(duì)象的遍歷行為,抽象出一個(gè)迭代器類(lèi)來(lái)負(fù)責(zé),這樣既可以做到不暴露集合的內(nèi)部結(jié)構(gòu),又可讓外部代碼透明地訪(fǎng)問(wèn)集合內(nèi)部的數(shù)據(jù)。

實(shí)現(xiàn)

我們將創(chuàng)建一個(gè)敘述導(dǎo)航方法的 Iterator 接口和一個(gè)返回迭代器的 Container 接口。實(shí)現(xiàn)了 Container 接口的實(shí)體類(lèi)將負(fù)責(zé)實(shí)現(xiàn) Iterator 接口。
IteratorPatternDemo,我們的演示類(lèi)使用實(shí)體類(lèi) NamesRepository 來(lái)打印 NamesRepository 中存儲(chǔ)為集合的 Names。


步驟 1
創(chuàng)建接口:
Iterator.java

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

Container.java

public interface Container {
   public Iterator getIterator();
}

步驟 2
創(chuàng)建實(shí)現(xiàn)了 Container 接口的實(shí)體類(lèi)。該類(lèi)有實(shí)現(xiàn)了 Iterator 接口的內(nèi)部類(lèi) NameIterator。
NameRepository.java

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 來(lái)獲取迭代器,并打印名字。
IteratorPatternDemo.java

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
執(zhí)行程序,輸出結(jié)果:

Name : Robert
Name : John
Name : Julie
Name : Lora

5.中介者模式

中介者模式(Mediator Pattern)是用來(lái)降低多個(gè)對(duì)象和類(lèi)之間的通信復(fù)雜性。這種模式提供了一個(gè)中介類(lèi),該類(lèi)通常處理不同類(lèi)之間的通信,并支持松耦合,使代碼易于維護(hù)。中介者模式屬于行為型模式。

介紹

意圖:用一個(gè)中介對(duì)象來(lái)封裝一系列的對(duì)象交互,中介者使各對(duì)象不需要顯式地相互引用,從而使其耦合松散,而且可以獨(dú)立地改變它們之間的交互。
主要解決:對(duì)象與對(duì)象之間存在大量的關(guān)聯(lián)關(guān)系,這樣勢(shì)必會(huì)導(dǎo)致系統(tǒng)的結(jié)構(gòu)變得很復(fù)雜,同時(shí)若一個(gè)對(duì)象發(fā)生改變,我們也需要跟蹤與之相關(guān)聯(lián)的對(duì)象,同時(shí)做出相應(yīng)的處理。
何時(shí)使用:多個(gè)類(lèi)相互耦合,形成了網(wǎng)狀結(jié)構(gòu)。
如何解決:將上述網(wǎng)狀結(jié)構(gòu)分離為星型結(jié)構(gòu)。
關(guān)鍵代碼:對(duì)象 Colleague 之間的通信封裝到一個(gè)類(lèi)中單獨(dú)處理。
應(yīng)用實(shí)例:1、中國(guó)加入 WTO 之前是各個(gè)國(guó)家相互貿(mào)易,結(jié)構(gòu)復(fù)雜,現(xiàn)在是各個(gè)國(guó)家通過(guò) WTO 來(lái)互相貿(mào)易。 2、機(jī)場(chǎng)調(diào)度系統(tǒng)。 3、MVC 框架,其中C(控制器)就是 M(模型)和 V(視圖)的中介者。
優(yōu)點(diǎn):1、降低了類(lèi)的復(fù)雜度,將一對(duì)多轉(zhuǎn)化成了一對(duì)一。 2、各個(gè)類(lèi)之間的解耦。 3、符合迪米特原則。
缺點(diǎn):中介者會(huì)龐大,變得復(fù)雜難以維護(hù)。
使用場(chǎng)景:1、系統(tǒng)中對(duì)象之間存在比較復(fù)雜的引用關(guān)系,導(dǎo)致它們之間的依賴(lài)關(guān)系結(jié)構(gòu)混亂而且難以復(fù)用該對(duì)象。 2、想通過(guò)一個(gè)中間類(lèi)來(lái)封裝多個(gè)類(lèi)中的行為,而又不想生成太多的子類(lèi)。
注意事項(xiàng):不應(yīng)當(dāng)在職責(zé)混亂的時(shí)候使用。

實(shí)現(xiàn)

我們通過(guò)聊天室實(shí)例來(lái)演示中介者模式。實(shí)例中,多個(gè)用戶(hù)可以向聊天室發(fā)送消息,聊天室向所有的用戶(hù)顯示消息。我們將創(chuàng)建兩個(gè)類(lèi) ChatRoom 和 User。User 對(duì)象使用 ChatRoom 方法來(lái)分享他們的消息。
MediatorPatternDemo,我們的演示類(lèi)使用 User 對(duì)象來(lái)顯示他們之間的通信。


步驟 1
創(chuàng)建中介類(lèi)。
ChatRoom.java

import java.util.Date;
 
public class ChatRoom {
   public static void showMessage(User user, String message){
      System.out.println(new Date().toString()
         + " [" + user.getName() +"] : " + message);
   }
}

步驟 2
創(chuàng)建 user 類(lèi)。
User.java

public class User {
   private String name;
 
   public String getName() {
      return name;
   }
 
   public void setName(String name) {
      this.name = name;
   }
 
   public User(String name){
      this.name  = name;
   }
 
   public void sendMessage(String message){
      ChatRoom.showMessage(this,message);
   }
}

步驟 3
使用 User 對(duì)象來(lái)顯示他們之間的通信。
MediatorPatternDemo.java

public class MediatorPatternDemo {
   public static void main(String[] args) {
      User robert = new User("Robert");
      User john = new User("John");
 
      robert.sendMessage("Hi! John!");
      john.sendMessage("Hello! Robert!");
   }
}

步驟 4
執(zhí)行程序,輸出結(jié)果:

Thu Jan 31 16:05:46 IST 2013 [Robert] : Hi! John!
Thu Jan 31 16:05:46 IST 2013 [John] : Hello! Robert!

6.備忘錄模式

備忘錄模式(Memento Pattern)保存一個(gè)對(duì)象的某個(gè)狀態(tài),以便在適當(dāng)?shù)臅r(shí)候恢復(fù)對(duì)象。備忘錄模式屬于行為型模式。

介紹

意圖:在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài)。
主要解決:所謂備忘錄模式就是在不破壞封裝的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài),這樣可以在以后將對(duì)象恢復(fù)到原先保存的狀態(tài)。
何時(shí)使用:很多時(shí)候我們總是需要記錄一個(gè)對(duì)象的內(nèi)部狀態(tài),這樣做的目的就是為了允許用戶(hù)取消不確定或者錯(cuò)誤的操作,能夠恢復(fù)到他原先的狀態(tài),使得他有"后悔藥"可吃。
如何解決:通過(guò)一個(gè)備忘錄類(lèi)專(zhuān)門(mén)存儲(chǔ)對(duì)象狀態(tài)。
關(guān)鍵代碼:客戶(hù)不與備忘錄類(lèi)耦合,與備忘錄管理類(lèi)耦合。
應(yīng)用實(shí)例:1、后悔藥。 2、打游戲時(shí)的存檔。 3、Windows 里的 ctri + z。 4、IE 中的后退。 4、數(shù)據(jù)庫(kù)的事務(wù)管理。
優(yōu)點(diǎn):1、給用戶(hù)提供了一種可以恢復(fù)狀態(tài)的機(jī)制,可以使用戶(hù)能夠比較方便地回到某個(gè)歷史的狀態(tài)。 2、實(shí)現(xiàn)了信息的封裝,使得用戶(hù)不需要關(guān)心狀態(tài)的保存細(xì)節(jié)。
缺點(diǎn):消耗資源。如果類(lèi)的成員變量過(guò)多,勢(shì)必會(huì)占用比較大的資源,而且每一次保存都會(huì)消耗一定的內(nèi)存。
使用場(chǎng)景:1、需要保存/恢復(fù)數(shù)據(jù)的相關(guān)狀態(tài)場(chǎng)景。 2、提供一個(gè)可回滾的操作。
注意事項(xiàng):1、為了符合迪米特原則,還要增加一個(gè)管理備忘錄的類(lèi)。 2、為了節(jié)約內(nèi)存,可使用原型模式+備忘錄模式。

實(shí)現(xiàn)

備忘錄模式使用三個(gè)類(lèi) Memento、Originator 和 CareTaker。Memento 包含了要被恢復(fù)的對(duì)象的狀態(tài)。Originator 創(chuàng)建并在 Memento 對(duì)象中存儲(chǔ)狀態(tài)。Caretaker 對(duì)象負(fù)責(zé)從 Memento 中恢復(fù)對(duì)象的狀態(tài)。
MementoPatternDemo,我們的演示類(lèi)使用 CareTaker 和 Originator 對(duì)象來(lái)顯示對(duì)象的狀態(tài)恢復(fù)。


步驟 1
創(chuàng)建 Memento 類(lèi)。
Memento.java

public class Memento {
   private String state;
 
   public Memento(String state){
      this.state = state;
   }
 
   public String getState(){
      return state;
   }  
}

步驟 2
創(chuàng)建 Originator 類(lèi)。
Originator.java

public class Originator {
   private String state;
 
   public void setState(String state){
      this.state = state;
   }
 
   public String getState(){
      return state;
   }
 
   public Memento saveStateToMemento(){
      return new Memento(state);
   }
 
   public void getStateFromMemento(Memento Memento){
      state = Memento.getState();
   }
}

步驟 3
創(chuàng)建 CareTaker 類(lèi)。
CareTaker.java

import java.util.ArrayList;
import java.util.List;
 
public class CareTaker {
   private List<Memento> mementoList = new ArrayList<Memento>();
 
   public void add(Memento state){
      mementoList.add(state);
   }
 
   public Memento get(int index){
      return mementoList.get(index);
   }
}

步驟 4
使用 CareTaker 和 Originator 對(duì)象。
MementoPatternDemo.java

public class MementoPatternDemo {
   public static void main(String[] args) {
      Originator originator = new Originator();
      CareTaker careTaker = new CareTaker();
      originator.setState("State #1");
      originator.setState("State #2");
      careTaker.add(originator.saveStateToMemento());
      originator.setState("State #3");
      careTaker.add(originator.saveStateToMemento());
      originator.setState("State #4");
      System.out.println("Current State: " + originator.getState());    
      originator.getStateFromMemento(careTaker.get(0));
      System.out.println("First saved State: " + originator.getState());
      originator.getStateFromMemento(careTaker.get(1));
      System.out.println("Second saved State: " + originator.getState());
   }
}

步驟 5
驗(yàn)證輸出。

Current State: State #4
First saved State: State #2
Second saved State: State #3

7.觀(guān)察者模式

當(dāng)對(duì)象間存在一對(duì)多關(guān)系時(shí),則使用觀(guān)察者模式(Observer Pattern)。比如,當(dāng)一個(gè)對(duì)象被修改時(shí),則會(huì)自動(dòng)通知它的依賴(lài)對(duì)象,被通知的對(duì)象會(huì)做出各自的反應(yīng)。觀(guān)察者模式屬于行為型模式。

介紹

意圖:定義對(duì)象間的一種一對(duì)多的依賴(lài)關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴(lài)于它的對(duì)象都得到通知并被自動(dòng)更新。

主要解決:一個(gè)對(duì)象狀態(tài)改變給其他對(duì)象通知的問(wèn)題,而且要考慮到易用和低耦合,保證高度的協(xié)作。

何時(shí)使用:一個(gè)對(duì)象(目標(biāo)對(duì)象)的狀態(tài)發(fā)生改變,進(jìn)行廣播通知,所有的依賴(lài)對(duì)象(觀(guān)察者對(duì)象)都將得到通知并做出各自的反應(yīng)。

如何解決:使用面向?qū)ο蠹夹g(shù),可以將這種依賴(lài)關(guān)系弱化。

關(guān)鍵代碼:在抽象類(lèi)里有一個(gè) ArrayList 存放觀(guān)察者們。

應(yīng)用實(shí)例: 1、拍賣(mài)的時(shí)候,拍賣(mài)師觀(guān)察最高標(biāo)價(jià),然后通知給其他競(jìng)價(jià)者競(jìng)價(jià)。 2、西游記里面悟空請(qǐng)求菩薩降服紅孩兒,菩薩灑了一地水招來(lái)一個(gè)老烏龜,這個(gè)烏龜就是觀(guān)察者,他觀(guān)察菩薩灑水這個(gè)動(dòng)作。

優(yōu)點(diǎn): 1、觀(guān)察者和被觀(guān)察者是抽象耦合的,被觀(guān)察者只知道觀(guān)察者接口,不知道具體的觀(guān)察者類(lèi),實(shí)現(xiàn)了被觀(guān)察者類(lèi)和具體觀(guān)察者類(lèi)的解耦。 2、建立一套觸發(fā)機(jī)制,實(shí)現(xiàn)了動(dòng)態(tài)聯(lián)動(dòng)。
3、支持廣播通信。

缺點(diǎn): 1、如果一個(gè)被觀(guān)察者對(duì)象有很多的直接和間接的觀(guān)察者的話(huà),將所有的觀(guān)察者都通知到會(huì)花費(fèi)很多時(shí)間。 2、如果在觀(guān)察者和觀(guān)察目標(biāo)之間有循環(huán)依賴(lài)的話(huà),觀(guān)察目標(biāo)會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。 3、觀(guān)察者模式?jīng)]有相應(yīng)的機(jī)制讓觀(guān)察者知道所觀(guān)察的目標(biāo)對(duì)象是怎么發(fā)生變化的,而僅僅只是知道觀(guān)察目標(biāo)發(fā)生了變化。

使用場(chǎng)景: 1、一個(gè)抽象模型有兩個(gè)方面,其中一個(gè)方面的操作(觀(guān)察者)依賴(lài)于另一個(gè)方面狀態(tài)的變化(被觀(guān)察者)。 2、如果在更改一個(gè)對(duì)象的時(shí)候,需要同時(shí)連帶改變其他對(duì)象,而且不知道究竟有多少對(duì)象需要被連帶改變。 3、當(dāng)一個(gè)對(duì)象必須通知其他對(duì)象,而又希望這個(gè)對(duì)象和其他被通知的對(duì)象是松散耦合的。

注意事項(xiàng): 1、JAVA 中已經(jīng)有了對(duì)觀(guān)察者模式的支持類(lèi)。 2、避免循環(huán)引用。 3、如果順序執(zhí)行,某一觀(guān)察者錯(cuò)誤會(huì)導(dǎo)致系統(tǒng)卡殼,一般采用異步方式。

實(shí)現(xiàn)

觀(guān)察者模式使用三個(gè)類(lèi) Subject、Observer 和 Client。Subject 對(duì)象帶有綁定觀(guān)察者到 Client 對(duì)象和從 Client 對(duì)象解綁觀(guān)察者的方法。我們創(chuàng)建 Subject 類(lèi)、Observer 抽象類(lèi)和擴(kuò)展了抽象類(lèi) Observer 的實(shí)體類(lèi)。
ObserverPatternDemo,我們的演示類(lèi)使用 Subject 和實(shí)體類(lèi)對(duì)象來(lái)演示觀(guān)察者模式。

觀(guān)察者模式.jpg

觀(guān)察者模式實(shí)現(xiàn)的兩種方式

1) 推模型
目標(biāo)對(duì)象主動(dòng)向觀(guān)察者推送目標(biāo)的詳細(xì)信息,推送的信息通常是目標(biāo)或目標(biāo)對(duì)象的全部數(shù)據(jù)。一般這種模型的實(shí)現(xiàn)中,會(huì)把目標(biāo)對(duì)象想要推送的信息通過(guò)update方法傳遞給觀(guān)察者。
步驟 1
創(chuàng)建 Subject 類(lèi)(目標(biāo)類(lèi)、被觀(guān)察者類(lèi))
Subject.java

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

public class Subject {
   private List<Observer> observers 
      = new ArrayList<Observer>();
   private int state;

   public int getState() {
      return state;
   }

   public void setState(int state) {
      this.state = state;
      notifyAllObservers(state);
   }

   public void attach(Observer observer){
      observers.add(observer);        
   }
   
   public void detach(Observer observer){
      observers.remove(observer);
   }
   
   public void notifyAllObservers(int state){
      for (Observer observer : observers) {
         observer.update(state);
      }
   }     
}

步驟 2
創(chuàng)建 Observer 類(lèi)

Observer.java

public abstract class Observer {
   public abstract void update(int state); //傳遞要推送的信息,觀(guān)察者只能接收到目標(biāo)推送的數(shù)據(jù)
}

步驟 3
創(chuàng)建實(shí)體觀(guān)察者類(lèi)

BinaryObserver.java

public class BinaryObserver extends Observer{
   @Override
   public void update(int state) {
      System.out.println( "Binary String: " 
      + Integer.toBinaryString(state)); 
   }
}

OctalObserver.java

public class OctalObserver extends Observer{
   @Override
   public void update(int state) {
     System.out.println( "Octal String: " 
     + Integer.toOctalString(state)); 
   }
}

HexaObserver.java

public class HexaObserver extends Observer{
   @Override
   public void update(int state) {
      System.out.println( "Hex String: " 
      + Integer.toHexString(state).toUpperCase()); 
   }
}

步驟 4
使用 Subject 和實(shí)體觀(guān)察者對(duì)象

ObserverPatternDemo.java

public class ObserverPatternDemo {
   public static void main(String[] args) {
      Subject subject = new Subject();
      Observer o1 = new HexaObserver();
      Observer o2 = new OctalObserver();
      Observer o3 = new BinaryObserver();
      subject.attach(o1);
      subject.attach(o2);
      subject.attach(o3);
      System.out.println("First state change: 15");    
      subject.setState(15);
      System.out.println("Second state change: 10");    
      subject.setState(10);
   }
}

步驟 5
驗(yàn)證輸出

First state change: 15
Hex String: F
Octal String: 17
Binary String: 1111
Second state change: 10
Hex String: A
Octal String: 12
Binary String: 1010

2) 拉模型
目標(biāo)對(duì)象在通知觀(guān)察者的時(shí)候,只傳遞少量信息。如果觀(guān)察者需要更具體的信息,由觀(guān)察者主動(dòng)到目標(biāo)對(duì)象中獲取,相當(dāng)于觀(guān)察者從目標(biāo)對(duì)象中拉數(shù)據(jù)。一般這種模型的實(shí)現(xiàn)中,會(huì)把目標(biāo)對(duì)象自身的引用通過(guò)update方法傳遞給觀(guān)察者。
步驟 1
創(chuàng)建 Subject 類(lèi)(目標(biāo)類(lèi)、被觀(guān)察者類(lèi))

Subject.java

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

public class Subject {
   private List<Observer> observers 
      = new ArrayList<Observer>();
   private int state;

   public int getState() {
      return state;
   }

   public void setState(int state) {
      this.state = state;
      notifyAllObservers();
   }

   public void attach(Observer observer){
      observers.add(observer);        
   }
   
   public void detach(Observer observer){
      observers.remove(observer);
   }
   
   public void notifyAllObservers(){
      for (Observer observer : observers) {
         observer.update(this);
      }
   }     
}

步驟 2
創(chuàng)建 Observer 類(lèi)

Observer.java

public abstract class Observer {
    /*傳遞目標(biāo)對(duì)象自身的引用,觀(guān)察者可自己選擇
    從目標(biāo)對(duì)象中拉哪些數(shù)據(jù)*/
   public abstract void update(Subject message);
}

步驟 3
創(chuàng)建實(shí)體觀(guān)察者類(lèi)

BinaryObserver.java

public class BinaryObserver extends Observer{
   @Override
   public void update(Subject message) {
      int state = message.getState(); 
      System.out.println( "Binary String: " 
      + Integer.toBinaryString(state)); 
   }
}

OctalObserver.java

public class OctalObserver extends Observer{
   @Override
   public void update(Subject message) {
     int state = message.getState(); 
     System.out.println( "Octal String: " 
     + Integer.toOctalString(state)); 
   }
}

HexaObserver.java

public class HexaObserver extends Observer{
   @Override
   public void update(Subject message) {
      int state = message.getState(); 
      System.out.println( "Hex String: " 
      + Integer.toHexString(state).toUpperCase()); 
   }
}

步驟 4
使用 Subject 和實(shí)體觀(guān)察者對(duì)象

ObserverPatternDemo.java

public class ObserverPatternDemo {
   public static void main(String[] args) {
      Subject subject = new Subject();
      Observer o1 = new HexaObserver();
      Observer o2 = new OctalObserver();
      Observer o3 = new BinaryObserver();
      subject.attach(o1);
      subject.attach(o2);
      subject.attach(o3);
      System.out.println("First state change: 15");    
      subject.setState(15);
      System.out.println("Second state change: 10");    
      subject.setState(10);
   }
}

步驟 5
驗(yàn)證輸出

First state change: 15
Hex String: F
Octal String: 17
Binary String: 1111
Second state change: 10
Hex String: A
Octal String: 12
Binary String: 1010

兩種模型的區(qū)別:

  1. 推模型由目標(biāo)對(duì)象決定推送的信息,觀(guān)察者不能獲取推送信息之外目標(biāo)對(duì)象的其他信息,較為被動(dòng)。拉模型雖然仍是目標(biāo)對(duì)象主動(dòng)推送信息,但推送的是整個(gè)目標(biāo)對(duì)象的引用,觀(guān)察者可以選擇性接收目標(biāo)對(duì)象的信息。
  2. 推模型一般用于目標(biāo)對(duì)象知道觀(guān)察者需要的數(shù)據(jù);而拉模型則用于目標(biāo)對(duì)象不知道觀(guān)察者需要的數(shù)據(jù),因此把自身傳遞給觀(guān)察者,由觀(guān)察者來(lái)取值。
  3. 推模型會(huì)使觀(guān)察者對(duì)象難以復(fù)用。拉模型下,update方法的參數(shù)是目標(biāo)對(duì)象本身,基本上可以適應(yīng)各種情況的需要。

利用Java提供的觀(guān)察者實(shí)現(xiàn)

Java提供了觀(guān)察者模式的實(shí)現(xiàn),有關(guān)類(lèi)和接口是java.util包的Observable類(lèi)和Observer接口。
和自己實(shí)現(xiàn)對(duì)比:
1.不需要自己定義觀(guān)察者和目標(biāo)接口了,JDK幫忙定義了
2.具體的目標(biāo)實(shí)現(xiàn)里面不需要再維護(hù)觀(guān)察者的注冊(cè)信息了,這個(gè)在Java中的Observable類(lèi)里面已經(jīng)幫忙實(shí)現(xiàn)好了。
3.觸發(fā)通知的方式有一點(diǎn)變化,要先調(diào)用setChanged方法,這個(gè)是Java為了幫助實(shí)現(xiàn)更精確的觸發(fā)控制而實(shí)現(xiàn)的功能。
4.具體觀(guān)察者的實(shí)現(xiàn)里面,update方法其實(shí)能同時(shí)支持推模型和拉模型,這個(gè)是Java在定義的時(shí)候,就已經(jīng)考慮進(jìn)去的了。

實(shí)現(xiàn)方法:
1、讓具體Subject實(shí)現(xiàn)類(lèi)繼承Observable目標(biāo)父類(lèi),Observable意為可被觀(guān)察的,所以讓具體目標(biāo)類(lèi)繼承它。
2、讓具體觀(guān)察者實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)Observer接口,Observer意為觀(guān)察者,所以讓具體觀(guān)察者實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)它。

步驟 1
創(chuàng)建具體目標(biāo)對(duì)象實(shí)現(xiàn)類(lèi)繼承Observable類(lèi)
Subject.java

import java.util.Observable;

public class Subject extends Observable {
    private int state;

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
        //通知觀(guān)察者之前必須調(diào)用setChanged()
        this.setChanged();
        
        /*通知所有觀(guān)察者,既傳遞目標(biāo)對(duì)象引用給觀(guān)察者,
         * 也傳遞參數(shù)給觀(guān)察者update的第二個(gè)參數(shù)*/
        this.notifyObservers(Integer.valueOf(state));
        
        /* 重載方法的無(wú)參方法notifyObservers()
         * 通知所有觀(guān)察者,但只是傳遞目標(biāo)對(duì)象引用給觀(guān)察者,
         * 觀(guān)察者update的第二個(gè)參數(shù)為null
         * */
    }
}

步驟 2
創(chuàng)建觀(guān)察者的具體實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)Observer接口
BinaryObserver.java

import java.util.Observable;
import java.util.Observer;

public class BinaryObserver implements Observer {
    /**
     * Observable o是目標(biāo)對(duì)象傳遞的引用,用于拉模型
     * Object arg是目標(biāo)對(duì)象主動(dòng)推送的信息,用于推模型
     * 如果目標(biāo)對(duì)象使用帶參的notifyObservers方法,
     * 則即可推也可拉;如果使用無(wú)參的notifyObservers方法,
     * 則只能拉
     */
    @Override
    public void update(Observable o, Object arg) {
        //1.推的方式
        System.out.println( "推模型:Binary String: " 
        + Integer.toBinaryString(((Integer)arg).intValue())); 
        //2.拉的方式  
        System.out.println( "拉模型:Binary String: " 
        + Integer.toBinaryString(((Subject)o).getState())); 

    }

}

HexaObserver.java

import java.util.Observable;
import java.util.Observer;

public class HexaObserver implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println( "推模型:Hex String: " 
        + Integer.toHexString(((Integer)arg).intValue()).toUpperCase()); 
        System.out.println( "拉模型:Hex String: " 
        + Integer.toHexString(((Subject)o).getState()).toUpperCase()); 
    }

}

OctalObserver.java

import java.util.Observable;
import java.util.Observer;

public class OctalObserver implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println( "推模型:Octal String: " 
                + Integer.toOctalString(((Integer)arg).intValue())); 
                System.out.println( "拉模型:Octal String: " 
                + Integer.toOctalString(((Subject)o).getState())); 
    }

}

步驟 3
創(chuàng)建測(cè)試類(lèi)
ObserverPatternDemo.java

import java.util.Observer;

public class ObserverPatternDemo {
   public static void main(String[] args) {
      Subject subject = new Subject();
      Observer o1 = new HexaObserver();
      Observer o2 = new BinaryObserver();
      Observer o3 = new OctalObserver();
      subject.addObserver(o1); //注冊(cè)觀(guān)察者
      subject.addObserver(o2);
      subject.addObserver(o3);
      System.out.println("First state change: 15");    
      subject.setState(15);
      System.out.println("Second state change: 10");    
      subject.setState(10);
   }
}

步驟 4
驗(yàn)證輸出
First state change: 15
推模型:Octal String: 17
拉模型:Octal String: 17
推模型:Binary String: 1111
拉模型:Binary String: 1111
推模型:Hex String: F
拉模型:Hex String: F
Second state change: 10
推模型:Octal String: 12
拉模型:Octal String: 12
推模型:Binary String: 1010
拉模型:Binary String: 1010
推模型:Hex String: A
拉模型:Hex String: A

區(qū)別對(duì)待觀(guān)察者模式

之前的觀(guān)察者模式是目標(biāo)對(duì)象無(wú)條件通知所有觀(guān)察者對(duì)象,然而有時(shí)需要在特定條件下對(duì)特定的觀(guān)察者進(jìn)行通知。這是就需要觀(guān)察者模式的變形 —— 區(qū)別對(duì)待觀(guān)察者模式。
具體實(shí)現(xiàn)只要修改Subject類(lèi)的notifyAllObservers方法,對(duì)Observer的身份做特定判斷,然后有條件的推送信息即可。

8.狀態(tài)模式

在狀態(tài)模式(State Pattern)中,類(lèi)的行為是基于它的狀態(tài)改變的。這種類(lèi)型的設(shè)計(jì)模式屬于行為型模式。
在狀態(tài)模式中,我們創(chuàng)建表示各種狀態(tài)的對(duì)象和一個(gè)行為隨著狀態(tài)對(duì)象改變而改變的 context 對(duì)象。

介紹

意圖:允許對(duì)象在內(nèi)部狀態(tài)發(fā)生改變時(shí)改變它的行為,對(duì)象看起來(lái)好像修改了它的類(lèi)。
主要解決:對(duì)象的行為依賴(lài)于它的狀態(tài)(屬性),并且可以根據(jù)它的狀態(tài)改變而改變它的相關(guān)行為。
何時(shí)使用:代碼中包含大量與對(duì)象狀態(tài)有關(guān)的條件語(yǔ)句。
如何解決:將各種具體的狀態(tài)類(lèi)抽象出來(lái)。
關(guān)鍵代碼:通常命令模式的接口中只有一個(gè)方法。而狀態(tài)模式的接口中有一個(gè)或者多個(gè)方法。而且,狀態(tài)模式的實(shí)現(xiàn)類(lèi)的方法,一般返回值,或者是改變實(shí)例變量的值。也就是說(shuō),狀態(tài)模式一般和對(duì)象的狀態(tài)有關(guān)。實(shí)現(xiàn)類(lèi)的方法有不同的功能,覆蓋接口中的方法。狀態(tài)模式和命令模式一樣,也可以用于消除 if...else 等條件選擇語(yǔ)句。
應(yīng)用實(shí)例:1、打籃球的時(shí)候運(yùn)動(dòng)員可以有正常狀態(tài)、不正常狀態(tài)和超常狀態(tài)。 2、曾侯乙編鐘中,'鐘是抽象接口','鐘A'等是具體狀態(tài),'曾侯乙編鐘'是具體環(huán)境(Context)。
優(yōu)點(diǎn):1、封裝了轉(zhuǎn)換規(guī)則。 2、枚舉可能的狀態(tài),在枚舉狀態(tài)之前需要確定狀態(tài)種類(lèi)。 3、將所有與某個(gè)狀態(tài)有關(guān)的行為放到一個(gè)類(lèi)中,并且可以方便地增加新的狀態(tài),只需要改變對(duì)象狀態(tài)即可改變對(duì)象的行為。 4、允許狀態(tài)轉(zhuǎn)換邏輯與狀態(tài)對(duì)象合成一體,而不是某一個(gè)巨大的條件語(yǔ)句塊。 5、可以讓多個(gè)環(huán)境對(duì)象共享一個(gè)狀態(tài)對(duì)象,從而減少系統(tǒng)中對(duì)象的個(gè)數(shù)。
缺點(diǎn):1、狀態(tài)模式的使用必然會(huì)增加系統(tǒng)類(lèi)和對(duì)象的個(gè)數(shù)。 2、狀態(tài)模式的結(jié)構(gòu)與實(shí)現(xiàn)都較為復(fù)雜,如果使用不當(dāng)將導(dǎo)致程序結(jié)構(gòu)和代碼的混亂。 3、狀態(tài)模式對(duì)"開(kāi)閉原則"的支持并不太好,對(duì)于可以切換狀態(tài)的狀態(tài)模式,增加新的狀態(tài)類(lèi)需要修改那些負(fù)責(zé)狀態(tài)轉(zhuǎn)換的源代碼,否則無(wú)法切換到新增狀態(tài),而且修改某個(gè)狀態(tài)類(lèi)的行為也需修改對(duì)應(yīng)類(lèi)的源代碼。
使用場(chǎng)景:1、行為隨狀態(tài)改變而改變的場(chǎng)景。 2、條件、分支語(yǔ)句的代替者。
注意事項(xiàng):在行為受狀態(tài)約束的時(shí)候使用狀態(tài)模式,而且狀態(tài)不超過(guò) 5 個(gè)。

實(shí)現(xiàn)

我們將創(chuàng)建一個(gè) State 接口和實(shí)現(xiàn)了 State 接口的實(shí)體狀態(tài)類(lèi)。Context 是一個(gè)帶有某個(gè)狀態(tài)的類(lèi)。
StatePatternDemo,我們的演示類(lèi)使用 Context 和狀態(tài)對(duì)象來(lái)演示 Context 在狀態(tài)改變時(shí)的行為變化。


步驟 1
創(chuàng)建一個(gè)接口。
State.java

public interface State {
   public void doAction(Context context);
}

步驟 2
創(chuàng)建實(shí)現(xiàn)接口的實(shí)體類(lèi)。
StartState.java

public class StartState implements State {
 
   public void doAction(Context context) {
      System.out.println("Player is in start state");
      context.setState(this); 
   }
 
   public String toString(){
      return "Start State";
   }
}

StopState.java

public class StopState implements State {
 
   public void doAction(Context context) {
      System.out.println("Player is in stop state");
      context.setState(this); 
   }
 
   public String toString(){
      return "Stop State";
   }
}

步驟 3
創(chuàng)建 Context 類(lèi)。
Context.java

public class Context {
   private State state;
 
   public Context(){
      state = null;
   }
 
   public void setState(State state){
      this.state = state;     
   }
 
   public State getState(){
      return state;
   }
}

步驟 4
使用 Context 來(lái)查看當(dāng)狀態(tài) State 改變時(shí)的行為變化。
StatePatternDemo.java

public class StatePatternDemo {
   public static void main(String[] args) {
      Context context = new Context();
 
      StartState startState = new StartState();
      startState.doAction(context);
 
      System.out.println(context.getState().toString());
 
      StopState stopState = new StopState();
      stopState.doAction(context);
 
      System.out.println(context.getState().toString());
   }
}

步驟 5
執(zhí)行程序,輸出結(jié)果:

Player is in start state
Start State
Player is in stop state
Stop State

9.空對(duì)象模式

在空對(duì)象模式(Null Object Pattern)中,一個(gè)空對(duì)象取代 NULL 對(duì)象實(shí)例的檢查。Null 對(duì)象不是檢查空值,而是反應(yīng)一個(gè)不做任何動(dòng)作的關(guān)系。這樣的 Null 對(duì)象也可以在數(shù)據(jù)不可用的時(shí)候提供默認(rèn)的行為。
在空對(duì)象模式中,我們創(chuàng)建一個(gè)指定各種要執(zhí)行的操作的抽象類(lèi)和擴(kuò)展該類(lèi)的實(shí)體類(lèi),還創(chuàng)建一個(gè)未對(duì)該類(lèi)做任何實(shí)現(xiàn)的空對(duì)象類(lèi),該空對(duì)象類(lèi)將無(wú)縫地使用在需要檢查空值的地方。

實(shí)現(xiàn)

我們將創(chuàng)建一個(gè)定義操作(在這里,是客戶(hù)的名稱(chēng))的 AbstractCustomer 抽象類(lèi),和擴(kuò)展了 AbstractCustomer 類(lèi)的實(shí)體類(lèi)。工廠(chǎng)類(lèi) CustomerFactory 基于客戶(hù)傳遞的名字來(lái)返回 RealCustomer 或 NullCustomer 對(duì)象。
NullPatternDemo,我們的演示類(lèi)使用 CustomerFactory 來(lái)演示空對(duì)象模式的用法。


步驟 1
創(chuàng)建一個(gè)抽象類(lèi)。
AbstractCustomer.java

public abstract class AbstractCustomer {
   protected String name;
   public abstract boolean isNil();
   public abstract String getName();
}

步驟 2
創(chuàng)建擴(kuò)展了上述類(lèi)的實(shí)體類(lèi)。
RealCustomer.java

public class RealCustomer extends AbstractCustomer {
 
   public RealCustomer(String name) {
      this.name = name;    
   }
   
   @Override
   public String getName() {
      return name;
   }
   
   @Override
   public boolean isNil() {
      return false;
   }
}

NullCustomer.java

public class NullCustomer extends AbstractCustomer {
 
   @Override
   public String getName() {
      return "Not Available in Customer Database";
   }
 
   @Override
   public boolean isNil() {
      return true;
   }
}

步驟 3
創(chuàng)建 CustomerFactory 類(lèi)。
CustomerFactory.java

public class CustomerFactory {
   
   public static final String[] names = {"Rob", "Joe", "Julie"};
 
   public static AbstractCustomer getCustomer(String name){
      for (int i = 0; i < names.length; i++) {
         if (names[i].equalsIgnoreCase(name)){
            return new RealCustomer(name);
         }
      }
      return new NullCustomer();
   }
}

步驟 4
使用 CustomerFactory,基于客戶(hù)傳遞的名字,來(lái)獲取 RealCustomer 或 NullCustomer 對(duì)象。
NullPatternDemo.java

public class NullPatternDemo {
   public static void main(String[] args) {
 
      AbstractCustomer customer1 = CustomerFactory.getCustomer("Rob");
      AbstractCustomer customer2 = CustomerFactory.getCustomer("Bob");
      AbstractCustomer customer3 = CustomerFactory.getCustomer("Julie");
      AbstractCustomer customer4 = CustomerFactory.getCustomer("Laura");
 
      System.out.println("Customers");
      System.out.println(customer1.getName());
      System.out.println(customer2.getName());
      System.out.println(customer3.getName());
      System.out.println(customer4.getName());
   }
}

步驟 5
執(zhí)行程序,輸出結(jié)果:

Customers
Rob
Not Available in Customer Database
Julie
Not Available in Customer Database

10.策略模式

在策略模式(Strategy Pattern)中,一個(gè)類(lèi)的行為或其算法可以在運(yùn)行時(shí)更改。這種類(lèi)型的設(shè)計(jì)模式屬于行為型模式。
在策略模式中,我們創(chuàng)建表示各種策略的對(duì)象和一個(gè)行為隨著策略對(duì)象改變而改變的 context 對(duì)象。策略對(duì)象改變 context 對(duì)象的執(zhí)行算法。

介紹

意圖:定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái), 并且使它們可相互替換。

主要解決:在有多種算法相似的情況下,使用 if...else 所帶來(lái)的復(fù)雜和難以維護(hù)。

何時(shí)使用:一個(gè)系統(tǒng)有許多許多類(lèi),而區(qū)分它們的只是他們直接的行為。

如何解決:將這些算法封裝成一個(gè)一個(gè)的類(lèi),任意地替換。

關(guān)鍵代碼:抽象出行為的共性作為一個(gè)策略接口,各種策略類(lèi)實(shí)現(xiàn)這個(gè)接口。在調(diào)用這個(gè)行為的類(lèi)中通過(guò)組合持有這個(gè)接口的對(duì)象,通過(guò)這個(gè)策略接口對(duì)象代理具體的行為。

應(yīng)用實(shí)例: 1、諸葛亮的錦囊妙計(jì),每一個(gè)錦囊就是一個(gè)策略。 2、旅行的出游方式,選擇騎自行車(chē)、坐汽車(chē),每一種旅行方式都是一個(gè)策略。 3、JAVA AWT 中的 LayoutManager。

優(yōu)點(diǎn): 1、算法可以自由切換。 2、避免使用多重條件判斷。 3、擴(kuò)展性良好。

缺點(diǎn): 1、策略類(lèi)會(huì)增多。 2、所有策略類(lèi)都需要對(duì)外暴露。

使用場(chǎng)景: 1、如果在一個(gè)系統(tǒng)里面有許多類(lèi),它們之間的區(qū)別僅在于它們的行為,那么使用策略模式可以動(dòng)態(tài)地讓一個(gè)對(duì)象在許多行為中選擇一種行為。 2、一個(gè)系統(tǒng)需要?jiǎng)討B(tài)地在幾種算法中選擇一種。 3、如果一個(gè)對(duì)象有很多的行為,如果不用恰當(dāng)?shù)哪J剑@些行為就只好使用多重的條件選擇語(yǔ)句來(lái)實(shí)現(xiàn)。

注意事項(xiàng):如果一個(gè)系統(tǒng)的策略多于四個(gè),就需要考慮使用混合模式,解決策略類(lèi)膨脹的問(wèn)題。

實(shí)現(xiàn)

我們將創(chuàng)建一個(gè)定義活動(dòng)的 Strategy 接口和實(shí)現(xiàn)了 Strategy 接口的實(shí)體策略類(lèi)。Context 是一個(gè)使用了某種策略的類(lèi)。
StrategyPatternDemo,我們的演示類(lèi)使用 Context 和策略對(duì)象來(lái)演示 Context 在它所配置或使用的策略改變時(shí)的行為變化。

策略模式.jpg

步驟 1
創(chuàng)建一個(gè)接口。
Strategy.java

public interface Strategy {
   public int doOperation(int num1, int num2);
}

步驟 2
創(chuàng)建實(shí)現(xiàn)接口的實(shí)體類(lèi)。
OperationAdd.java

public class OperationAdd implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 + num2;
   }
}

OperationSubstract.java

public class OperationSubstract implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 - num2;
   }
}

OperationMultiply.java

public class OperationMultiply implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 * num2;
   }
}

步驟 3
創(chuàng)建 Context 類(lèi)。
Context.java

public class Context {
   private Strategy strategy;//組合一個(gè)策略接口對(duì)象

   public Context(Strategy strategy){
      this.strategy = strategy;
   }

   public int executeStrategy(int num1, int num2){
      return strategy.doOperation(num1, num2);
      //用策略接口對(duì)象代理具體實(shí)現(xiàn)
   }
}

步驟 4
使用 Context 來(lái)查看當(dāng)它改變策略 Strategy 時(shí)的行為變化。
StrategyPatternDemo.java

public class StrategyPatternDemo {
   public static void main(String[] args) {
      Context context = new Context(new OperationAdd());        
      System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

      context = new Context(new OperationSubstract());        
      System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

      context = new Context(new OperationMultiply());        
      System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
   }
}

步驟 5
驗(yàn)證輸出。
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50

11.模板模式

在模板模式(Template Pattern)中,一個(gè)抽象類(lèi)公開(kāi)定義了執(zhí)行它的方法的方式/模板。它的子類(lèi)可以按需要重寫(xiě)方法實(shí)現(xiàn),但調(diào)用將以抽象類(lèi)中定義的方式進(jìn)行。這種類(lèi)型的設(shè)計(jì)模式屬于行為型模式。

介紹

意圖:定義一個(gè)操作中的算法的骨架,而將一些步驟延遲到子類(lèi)中。模板方法使得子類(lèi)可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。

主要解決:一些方法通用,卻在每一個(gè)子類(lèi)都重新寫(xiě)了這一方法。

何時(shí)使用:有一些通用的方法。

如何解決:將這些通用算法抽象出來(lái)。

關(guān)鍵代碼:準(zhǔn)備一個(gè)抽象類(lèi),將部分邏輯以具體方法的形式實(shí)現(xiàn),然后聲明一些抽象方法交由子類(lèi)實(shí)現(xiàn)剩余邏輯,用鉤子方法給予子類(lèi)更大的靈活性。最后將方法匯總為一個(gè)final的模板方法。

應(yīng)用實(shí)例: 1、在造房子的時(shí)候,地基、走線(xiàn)、水管都一樣,只有在建筑的后期才有加壁櫥加?xùn)艡诘炔町悺?2、西游記里面菩薩定好的 81 難,這就是一個(gè)頂層的邏輯骨架。 3、spring 中對(duì) Hibernate 的支持,將一些已經(jīng)定好的方法封裝起來(lái),比如開(kāi)啟事務(wù)、獲取 Session、關(guān)閉 Session 等,程序員不重復(fù)寫(xiě)那些已經(jīng)規(guī)范好的代碼,直接丟一個(gè)實(shí)體就可以保存。

優(yōu)點(diǎn): 1、封裝性好,封裝不變部分,擴(kuò)展可變部分。 2、復(fù)用性好,提取公共代碼,便于維護(hù)。 3、屏蔽細(xì)節(jié),行為由父類(lèi)控制,子類(lèi)實(shí)現(xiàn)。

缺點(diǎn):1、每一個(gè)不同的實(shí)現(xiàn)都需要一個(gè)子類(lèi)來(lái)實(shí)現(xiàn),導(dǎo)致類(lèi)的個(gè)數(shù)增加,使得系統(tǒng)更加龐大。2、Java的單繼承使得繼承了其他父類(lèi)子類(lèi)難以實(shí)現(xiàn)對(duì)模板基類(lèi)的繼承。

使用場(chǎng)景: 1、有多個(gè)子類(lèi)共有的方法,且邏輯相同。 2、重要的、復(fù)雜的方法,可以考慮作為模板方法。

注意事項(xiàng):為防止惡意操作,一般模板方法都加上 final 關(guān)鍵詞。在模板方法內(nèi)的步驟中,通用的方法在抽象基類(lèi)里提供實(shí)現(xiàn),特定的方法定義為抽象方法,延遲到子類(lèi)中實(shí)現(xiàn)。

實(shí)現(xiàn)

我們將創(chuàng)建一個(gè)定義操作的 Game 抽象類(lèi),其中,模板方法設(shè)置為 final,這樣它就不會(huì)被重寫(xiě)。Cricket 和 Football 是擴(kuò)展了 Game 的實(shí)體類(lèi),它們重寫(xiě)了抽象類(lèi)的方法。
TemplatePatternDemo,我們的演示類(lèi)使用 Game 來(lái)演示模板模式的用法。

模板模式.jpg

步驟 1
創(chuàng)建一個(gè)抽象類(lèi),它的模板方法被設(shè)置為 final。
Game.java

public abstract class Game {
   abstract void initialize();
   //如果子類(lèi)通用,可以在抽象基類(lèi)實(shí)現(xiàn),不必定義為抽象方法
   abstract void startPlay();
   abstract void endPlay();

   //模板,定義為final,防止被子類(lèi)重寫(xiě)
   public final void play(){

      //初始化游戲
      initialize();

      //開(kāi)始游戲
      startPlay();

      //結(jié)束游戲
      endPlay();
   }
}

步驟 2
創(chuàng)建擴(kuò)展了上述類(lèi)的實(shí)體類(lèi)。
Cricket.java

public class Cricket extends Game {

   @Override
   void endPlay() {
      System.out.println("Cricket Game Finished!");
   }

   @Override
   void initialize() {
      System.out.println("Cricket Game Initialized! Start playing.");
   }

   @Override
   void startPlay() {
      System.out.println("Cricket Game Started. Enjoy the game!");
   }
}

Football.java

public class Football extends Game {

   @Override
   void endPlay() {
      System.out.println("Football Game Finished!");
   }

   @Override
   void initialize() {
      System.out.println("Football Game Initialized! Start playing.");
   }

   @Override
   void startPlay() {
      System.out.println("Football Game Started. Enjoy the game!");
   }
}

步驟 3
使用 Game 的模板方法 play() 來(lái)演示游戲的定義方式。
TemplatePatternDemo.java

public class TemplatePatternDemo {
   public static void main(String[] args) {

      Game game = new Cricket();
      game.play();
      System.out.println();
      game = new Football();
      game.play();        
   }
}

步驟 4
驗(yàn)證輸出。
Cricket Game Initialized! Start playing.
Cricket Game Started. Enjoy the game!
Cricket Game Finished!
Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!

如果想要靈活選擇模板中的某一步驟是否出現(xiàn),可以添加一個(gè)鉤子方法:比如在泡飲品的模板方法中,燒水——倒入飲品沖劑——加水——加調(diào)料。如果有的飲品不想要調(diào)料,可以在模板方法中把加調(diào)料放在if語(yǔ)句中,if的條件是一個(gè)返回值為boolean類(lèi)型的方法,比如isCustomerWantsCondiments(),提供一個(gè)空的或者默認(rèn)返回true的實(shí)現(xiàn),稱(chēng)為鉤子方法。子類(lèi)可以根據(jù)需要重寫(xiě)該鉤子方法選擇要不要加調(diào)料。

12.訪(fǎng)問(wèn)者模式

在訪(fǎng)問(wèn)者模式(Visitor Pattern)中,我們使用了一個(gè)訪(fǎng)問(wèn)者類(lèi),它改變了元素類(lèi)的執(zhí)行算法。通過(guò)這種方式,元素的執(zhí)行算法可以隨著訪(fǎng)問(wèn)者改變而改變。這種類(lèi)型的設(shè)計(jì)模式屬于行為型模式。根據(jù)模式,元素對(duì)象已接受訪(fǎng)問(wèn)者對(duì)象,這樣訪(fǎng)問(wèn)者對(duì)象就可以處理元素對(duì)象上的操作。

介紹

意圖:主要將數(shù)據(jù)結(jié)構(gòu)與數(shù)據(jù)操作分離。
主要解決:穩(wěn)定的數(shù)據(jù)結(jié)構(gòu)和易變的操作耦合問(wèn)題。
何時(shí)使用:需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的并且不相關(guān)的操作,而需要避免讓這些操作"污染"這些對(duì)象的類(lèi),使用訪(fǎng)問(wèn)者模式將這些封裝到類(lèi)中。
如何解決:在被訪(fǎng)問(wèn)的類(lèi)里面加一個(gè)對(duì)外提供接待訪(fǎng)問(wèn)者的接口。
關(guān)鍵代碼:在數(shù)據(jù)基礎(chǔ)類(lèi)里面有一個(gè)方法接受訪(fǎng)問(wèn)者,將自身引用傳入訪(fǎng)問(wèn)者。
應(yīng)用實(shí)例:您在朋友家做客,您是訪(fǎng)問(wèn)者,朋友接受您的訪(fǎng)問(wèn),您通過(guò)朋友的描述,然后對(duì)朋友的描述做出一個(gè)判斷,這就是訪(fǎng)問(wèn)者模式。
優(yōu)點(diǎn):1、符合單一職責(zé)原則。 2、優(yōu)秀的擴(kuò)展性。 3、靈活性。
缺點(diǎn):1、具體元素對(duì)訪(fǎng)問(wèn)者公布細(xì)節(jié),違反了迪米特原則。 2、具體元素變更比較困難。 3、違反了依賴(lài)倒置原則,依賴(lài)了具體類(lèi),沒(méi)有依賴(lài)抽象。
使用場(chǎng)景:1、對(duì)象結(jié)構(gòu)中對(duì)象對(duì)應(yīng)的類(lèi)很少改變,但經(jīng)常需要在此對(duì)象結(jié)構(gòu)上定義新的操作。 2、需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的并且不相關(guān)的操作,而需要避免讓這些操作"污染"這些對(duì)象的類(lèi),也不希望在增加新操作時(shí)修改這些類(lèi)。
注意事項(xiàng):訪(fǎng)問(wèn)者可以對(duì)功能進(jìn)行統(tǒng)一,可以做報(bào)表、UI、攔截器與過(guò)濾器。

實(shí)現(xiàn)

我們將創(chuàng)建一個(gè)定義接受操作的 ComputerPart 接口。Keyboard、Mouse、Monitor 和 Computer 是實(shí)現(xiàn)了 ComputerPart 接口的實(shí)體類(lèi)。我們將定義另一個(gè)接口 ComputerPartVisitor,它定義了訪(fǎng)問(wèn)者類(lèi)的操作。Computer 使用實(shí)體訪(fǎng)問(wèn)者來(lái)執(zhí)行相應(yīng)的動(dòng)作。
VisitorPatternDemo,我們的演示類(lèi)使用 Computer、ComputerPartVisitor 類(lèi)來(lái)演示訪(fǎng)問(wèn)者模式的用法。


步驟 1
定義一個(gè)表示元素的接口。
ComputerPart.java

public interface ComputerPart {
   public void accept(ComputerPartVisitor computerPartVisitor);
}

步驟 2
創(chuàng)建擴(kuò)展了上述類(lèi)的實(shí)體類(lèi)。
Keyboard.java

public class Keyboard  implements ComputerPart {
 
   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

Monitor.java

public class Monitor  implements ComputerPart {
 
   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

Mouse.java

public class Mouse  implements ComputerPart {
 
   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

Computer.java

public class Computer implements ComputerPart {
   
   ComputerPart[] parts;
 
   public Computer(){
      parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};      
   } 
 
 
   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      for (int i = 0; i < parts.length; i++) {
         parts[i].accept(computerPartVisitor);
      }
      computerPartVisitor.visit(this);
   }
}

步驟 3
定義一個(gè)表示訪(fǎng)問(wèn)者的接口。
ComputerPartVisitor.java

public interface ComputerPartVisitor {
   public void visit(Computer computer);
   public void visit(Mouse mouse);
   public void visit(Keyboard keyboard);
   public void visit(Monitor monitor);
}

步驟 4
創(chuàng)建實(shí)現(xiàn)了上述類(lèi)的實(shí)體訪(fǎng)問(wèn)者。
ComputerPartDisplayVisitor.java

public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
 
   @Override
   public void visit(Computer computer) {
      System.out.println("Displaying Computer.");
   }
 
   @Override
   public void visit(Mouse mouse) {
      System.out.println("Displaying Mouse.");
   }
 
   @Override
   public void visit(Keyboard keyboard) {
      System.out.println("Displaying Keyboard.");
   }
 
   @Override
   public void visit(Monitor monitor) {
      System.out.println("Displaying Monitor.");
   }
}

步驟 5
使用 ComputerPartDisplayVisitor 來(lái)顯示 Computer 的組成部分。
VisitorPatternDemo.java

public class VisitorPatternDemo {
   public static void main(String[] args) {
 
      ComputerPart computer = new Computer();
      computer.accept(new ComputerPartDisplayVisitor());
   }
}

步驟 6
執(zhí)行程序,輸出結(jié)果:

Displaying Mouse.
Displaying Keyboard.
Displaying Monitor.
Displaying Computer.

參考資料:
菜鳥(niǎo)教程之設(shè)計(jì)模式
CyC2018/CS-Notes/設(shè)計(jì)模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 設(shè)計(jì)模式概述 在學(xué)習(xí)面向?qū)ο笃叽笤O(shè)計(jì)原則時(shí)需要注意以下幾點(diǎn):a) 高內(nèi)聚、低耦合和單一職能的“沖突”實(shí)際上,這兩者...
    彥幀閱讀 3,892評(píng)論 0 14
  • 參考資料:菜鳥(niǎo)教程之設(shè)計(jì)模式 設(shè)計(jì)模式概述 設(shè)計(jì)模式(Design pattern)代表了最佳的實(shí)踐,通常被有經(jīng)驗(yàn)...
    Steven1997閱讀 1,275評(píng)論 1 12
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,175評(píng)論 25 708
  • 不曾逝去的青春, 只因?yàn)榻?jīng)歷太多, 失去太多而顯得衰老, 其實(shí)青春還未離去, 只是那些曾經(jīng)因?yàn)槟贻p而墮落。 而沖動(dòng)...
    林楓隨心閱讀 319評(píng)論 0 1
  • #今天的你就是未來(lái)的樣子# 孩子第二個(gè)30天目標(biāo):早睡早起,每周戶(hù)外運(yùn)動(dòng)一次;自己收拾玩具并整理衣服;自己刷牙洗臉...
    1張維閱讀 186評(píng)論 0 1

友情鏈接更多精彩內(nèi)容