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

1.責(zé)任鏈模式(Chain of Responsibility)
2.命令模式(Command)
3.解釋器模式(Interpreter)
4.迭代器模式(Iterator)
5.中介者模式(Mediator)
6.備忘錄模式(Memento)
7.觀察者模式(Observer)
8.狀態(tài)模式(State)
9.策略模式(Strategy)
10.模板方法模式(Template Method)
11.訪問者模式(Visitor)

行為型關(guān)注點(diǎn):關(guān)注對(duì)象之間的交互通信(方法級(jí)別)


模板方法模式(Template Method)

定義:

定義一個(gè)操作中的算法骨架,而將一些步驟延遲到子類中。

通俗來說

在父類中設(shè)計(jì)主流程,將主流程步驟的實(shí)現(xiàn)方法交給子類來實(shí)現(xiàn)。這樣就實(shí)現(xiàn)了方法的延遲。

角色
  • 抽象模板類(AbstractClass):,就是一個(gè)抽象模板,定義算法的骨架。
  • 具體實(shí)現(xiàn)類(ConcreteClass):實(shí)現(xiàn)算法其中某些過程方法,每個(gè)具體實(shí)現(xiàn)類給出算法的不同實(shí)現(xiàn),從而使得抽象模板中骨架方法的實(shí)現(xiàn)各不相同。
模板方法類圖

抽象模板類:

public abstract class AbstractClass {

    public abstract void primitiveOperation1();

    public abstract void primitiveOperation2();

    // 模板方法,給出了邏輯的骨架
    // 而邏輯的組成是一些相應(yīng)的抽象操作,他們都推遲到子類實(shí)現(xiàn)
    public void templateMethod() {

        primitiveOperation1();
        primitiveOperation2();
        System.out.println("");
    }
}

具體實(shí)現(xiàn)類A:

public class ConcreteClassA extends AbstractClass {

    @Override
    public void primitiveOperation1() {

        System.out.println("具體類A方法1實(shí)現(xiàn)");
    }

    @Override
    public void primitiveOperation2() {

        System.out.println("具體類A方法2實(shí)現(xiàn)");
    }
}

具體實(shí)現(xiàn)類B:

public class ConcreteClassA extends AbstractClass {

    @Override
    public void primitiveOperation1() {

        System.out.println("具體類B方法1實(shí)現(xiàn)");
    }

    @Override
    public void primitiveOperation2() {

        System.out.println("具體類B方法2實(shí)現(xiàn)");
    }
}

客戶端調(diào)用類

public class Client {

    public static void main(String[] args) {

        AbstractClass c;

        c = new ConcreteClassA();
        c.templateMethod();

        c = new ConcreteClassB();
        c.templateMethod();
    }
}
優(yōu)點(diǎn):
  • 1.模板方法模式通過把不變的行為搬移到父類,去除了子類中重復(fù)的代碼。
  • 2.通過子類擴(kuò)展增加新的行為,符合開閉原則。
應(yīng)用場(chǎng)景:

多個(gè)子類有共有的方法,并且邏輯基本相同。
重構(gòu)時(shí),模板方法是一個(gè)經(jīng)常使用的方法。把相同代碼抽取到父類中,然后通過構(gòu)造函數(shù)約束其行為。


策略模式(Strategy)

定義

策略模式定義了一系列算法,并將每個(gè)算法封裝起來,讓它們之間可以互相替換,讓算法的變化獨(dú)立于使用算法的客戶。

通俗來說

通過注入不同的類,改變業(yè)務(wù)方法的業(yè)務(wù)行為通過選擇策略類,來執(zhí)行不同算法分支。。此模式就是springIOC的思想。核心是通過注入對(duì)象,改變行為。

通過改變注入的對(duì)象,從而改變它的行為。

通過實(shí)例化不同的class對(duì)象,改變spring業(yè)務(wù)方法的業(yè)務(wù)行為。IOC思想的雛形。

角色:
  • 上下文角色(Context):也叫Context封裝角色,起承上啟下的作用,屏蔽高層模塊對(duì)策略算法的直接訪問,封裝可能存在的變化。
  • 抽象策略角色(AbstractStrategy):是對(duì)策略算法家族的抽象,通常為借口,定義每個(gè)策略算法必須具有的方法和屬性。
  • 具體策略角色(ConcreteStrategy):用于實(shí)現(xiàn)抽象策略中的操作,即實(shí)現(xiàn)的具體算法。


    image.png

context上下文:

public class Context {
    Strategy strategy;
    
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    
    //上下文接口
    public void contextInterface() {
        strategy.algorithmInterface();
    }

}

抽象策略角色:

public interface Strategy {
    //算法方法
    void algorithmInterface();

}

具體策略角色:

public class ConcreteStrategyA implements Strategy {
    @Override
    public void algorithmInterface() {
        System.out.println("算法A實(shí)現(xiàn)");
    }

}

調(diào)用代碼:

public class Client {
    
    public static void main(String[] args) {
        Context context;
        
        context = new Context(new ConcreteStrategyA());
        context.contextInterface();
        
        context = new Context(new ConcreteStrategyB());
        context.contextInterface();
        
        context = new Context(new ConcreteStrategyC());
        context.contextInterface();
    }

}
優(yōu)點(diǎn):
  • 算法可以自由切換。
  • 避免使用多重條件轉(zhuǎn)移語(yǔ)句。
  • 擴(kuò)展性好。
缺點(diǎn):
  • 用戶需要知道所有策略類,并自行決定使用哪一種策略。
  • 每個(gè)算法都會(huì)產(chǎn)生一個(gè)策略類,造成策略類過多。
應(yīng)用場(chǎng)景:

線程池的拒絕策略 默認(rèn)四種就是策略模式
java AWT中的layoutManager 布局管理器。

策略模式和裝飾模式的區(qū)別

策略模式側(cè)重于算法的自由替換,IOC的一種實(shí)現(xiàn)思想,關(guān)注的是方法級(jí)別的。
裝飾模式側(cè)重于對(duì)類的增強(qiáng),需要了解每一個(gè)裝飾器做什么功能,是關(guān)注類級(jí)別組合。


責(zé)任鏈模式(Chain of Responsibility)

定義

請(qǐng)求創(chuàng)建了一個(gè)接收者對(duì)象的鏈。這種模式給予請(qǐng)求的類型,對(duì)請(qǐng)求的發(fā)送者和接收者進(jìn)行解耦。這種類型的設(shè)計(jì)模式屬于行為型模式。

通俗來說

將請(qǐng)求傳給一個(gè)接收者鏈,由鏈將請(qǐng)求流轉(zhuǎn)給目標(biāo)對(duì)象。
當(dāng)某一個(gè)業(yè)務(wù)請(qǐng)求,多個(gè)對(duì)象需要對(duì)這個(gè)請(qǐng)求做處理,就可以使用責(zé)任鏈模式。把所有對(duì)象串成一個(gè)鏈?zhǔn)浇Y(jié)構(gòu),讓請(qǐng)求在鏈中流動(dòng)。

責(zé)任鏈和組合模式有點(diǎn)相似,組合模式是適合樹形結(jié)構(gòu)的業(yè)務(wù)邏輯,而鏈表呢是一種特殊的樹,只有一個(gè)后繼節(jié)點(diǎn)。責(zé)任鏈就是這種特殊的組合模式。

角色
  • 抽象處理者角色(handler):定義出一個(gè)處理請(qǐng)求的接口,如果需要,接口也可以定義出一個(gè)模板方法以設(shè)定固定的流轉(zhuǎn)流程。這個(gè)角色通常是一個(gè)抽象類或者java接口。角色內(nèi)包含下個(gè)節(jié)點(diǎn)的引用。

  • 具體處理者角色(ConcreteHandler):具體處理者,接到請(qǐng)求后,可以選擇將請(qǐng)求處理,或流轉(zhuǎn)到下個(gè)實(shí)例。

責(zé)任鏈模式類圖

抽象處理者:

public abstract class Handler {
    
    /**
     * 持有后繼的責(zé)任對(duì)象
     */
    protected Handler successor;

    public Handler (Handler handler) {
        this.successor = handler;
    }
    /**
     * 示意處理請(qǐng)求的方法,雖然這個(gè)示意方法是沒有傳入?yún)?shù)的
     * 但實(shí)際是可以傳入?yún)?shù)的,根據(jù)具體需要來選擇是否傳遞參數(shù)
     */
    public abstract void handleRequest();
    /**
     * 取值方法
     */
    public Handler getSuccessor() {
        return successor;
    }
    /**
     * 賦值方法,設(shè)置后繼的責(zé)任對(duì)象
     */
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    
}

具體處理者:

public class ConcreteHandler extends Handler {
    /**
     * 處理方法,調(diào)用此方法處理請(qǐng)求
     */
    @Override
    public void handleRequest() {
        /**
         * 判斷是否有后繼的責(zé)任對(duì)象
         * 如果有,就轉(zhuǎn)發(fā)請(qǐng)求給后繼的責(zé)任對(duì)象
         * 如果沒有,則處理請(qǐng)求
         */
        if(getSuccessor() != null)
        {            
            System.out.println("放過請(qǐng)求");
            getSuccessor().handleRequest();            
        }else
        {            
            System.out.println("處理請(qǐng)求");
        }
    }
 
}

調(diào)用方:

public class Client {
 
    public static void main(String[] args) {
        //組裝責(zé)任鏈
        Handler handler = new ConcreteHandler(null);
        handler = new ConcreteHandler(handler);
        
        //提交請(qǐng)求
        handler1.handleRequest();
    }

可以將流轉(zhuǎn)下一個(gè)流程的代碼抽象到父類中,由一個(gè)模板方法模式進(jìn)行封裝,所有子類流轉(zhuǎn)的代碼都是相同的。

  if(null != getSuccessor()){
    getSuccessor().handleRequest();
  }
優(yōu)點(diǎn):

簡(jiǎn)化了調(diào)用,使用者不需要知道鏈的結(jié)構(gòu)
責(zé)任分離處理,讓各個(gè)節(jié)點(diǎn)各司其職。
動(dòng)態(tài)地增加減少責(zé)任鏈

應(yīng)用場(chǎng)景:

spring的攔截器 就是一個(gè)責(zé)任鏈模式
servlet的filter 就是一個(gè)責(zé)任鏈模式
springsecurity 的核心邏輯也是一個(gè)過濾器鏈。
做審批處理的時(shí)候。


命令模式(Command)

定義

將請(qǐng)求、命令封裝成對(duì)象,這樣可以讓項(xiàng)目使用這些對(duì)象來參數(shù)化其他對(duì)象。是的命令的請(qǐng)求者和執(zhí)行者解耦。

通俗來說

可以使發(fā)送者接收者解耦,發(fā)送者和接收者之間并沒有直接的引用關(guān)系。發(fā)送者只知道如何發(fā)送請(qǐng)求,不需要知道如何完成請(qǐng)求。

角色:
  • 調(diào)用者(Invoker) :就是具體的調(diào)用者。
  • 接收者(receiver) :如何實(shí)施和執(zhí)行一個(gè)請(qǐng)求相關(guān)的一個(gè)操作。
  • 抽象命令角色(command):需要執(zhí)行的所有命令都在這里,可以使接口或抽象類
  • 具體命令角色(ConcreteCommand):將一個(gè)接收者對(duì)象與一個(gè)命令綁定,調(diào)用接收者響應(yīng)的操作。
命令模式類圖

抽象命令

/**
 * 命令抽象父類
 */
public abstract class Commond {

    private Barbecue barbecue;

    public Commond(Barbecue barbecue) {
        super();
        this.barbecue = barbecue;
    }

    public abstract void excuteCommond();

    public Barbecue getBarbecue() {
        return barbecue;
    }

    public void setBarbecue(Barbecue barbecue) {
        this.barbecue = barbecue;
    }
}

具體命令1

/**
 * 烤羊腿命令
 */
public class MuttonCommod extends Commond{

    public MuttonCommod(Barbecue barbecue) {
        super(barbecue);
    }
    @Override
    public void excuteCommond() {
        super.getBarbecue().makeMutton();
    }

}

具體命令2

/**
 * 烤雞翅命令
 */
public class ChickenCommond extends Commond{

    public ChickenCommond(Barbecue barbecue) {
        super(barbecue);
    }
    @Override
    public void excuteCommond() {
        super.getBarbecue().makeChicken();
    }

}

接收者(Receiver)

/**
 * 燒烤師傅
 */
public class Barbecue {

    public void makeMutton() {
        System.out.println("烤羊肉串");
    }

    public void makeChicken() {
        System.out.println("考雞肉串");
    }
}

調(diào)用者(Invoker)

/**
 * 服務(wù)員
 */
public class Waiter {

    private List<Commond> commonds = new ArrayList<>();

    public void addCommond(Commond commond) {
        // TODO 可以做很多事情  記日志等等
        commonds.add(commond);
    }

    public void removeCommond(Commond commond) {
        // TODO 可以做很多事情  記日志等等
        commonds.remove(commond);
    }

    public void Notify() {
        for (Commond commond : commonds) {
            commond.excuteCommond();
        }
    }
}
優(yōu)點(diǎn):

1.降低耦合。
2.容易擴(kuò)展新命令或者一組命令。

缺點(diǎn):

命令的無限擴(kuò)展會(huì)增加類的數(shù)量,提高了系統(tǒng)的復(fù)雜度。

命令模式和策略模式的區(qū)別:
  • 命令模式和策略模式很相似,只是命令模式多了一個(gè)接收者的角色。
  • 策略模式關(guān)注的是算法的替換問題,或者提供多種算法由調(diào)用者選擇使用。算法的自由替換是它的要點(diǎn)。
  • 命令模式則是對(duì)調(diào)用方和接收方的解耦,要求把請(qǐng)求的內(nèi)容封裝成一個(gè)一個(gè)的命令,由接收者執(zhí)行。由于封裝成了命令就可以對(duì)命令進(jìn)行多種處理,撤銷 排隊(duì)等等。
  • 策略模式適用于算法要求變換的場(chǎng)景。命令模式適合兩個(gè)緊耦合關(guān)系的對(duì)象解耦 或者多命令多撤銷的場(chǎng)景。

舉兩個(gè)具體的例子:

1.如果要實(shí)現(xiàn)壓縮 我們選擇zip算法 和gzip算法 那么策略模式就會(huì)設(shè)計(jì)成兩個(gè)策略:zip 一個(gè)策略 里面包含了 壓縮和解壓縮的方法,gzip一個(gè)策略 也包含一個(gè)壓縮和一個(gè)解壓縮的算法。
而命令模式就會(huì)設(shè)計(jì)成4個(gè)命令 zip壓縮命令、zip解壓縮命令、gzip壓縮命令、gzip解壓縮命令。
由此看出命令模式關(guān)注的是通過封裝成命令來解耦。

2.另外一種情況說明命令模式可以像適配器模式一樣,作為一種補(bǔ)救辦法。假設(shè)有A、B、C三個(gè)類,三個(gè)類的功能大體相似。但沒有實(shí)現(xiàn)同一接口,這個(gè)時(shí)候我們可以用命令模式解除他們與調(diào)用者之間的緊耦合。封裝三個(gè)命令實(shí)現(xiàn)類,來聚合A、B、C三個(gè)類,通過命令的方式讓調(diào)用者不直接操作A、B、C三個(gè)類。而此時(shí)如果是想用策略模式去實(shí)現(xiàn),需要修改大量的代碼 讓A、B、C去實(shí)現(xiàn)相同的接口,并不適用。

命令模式和外觀模式的區(qū)別:
  • 1.外觀模式是結(jié)構(gòu)型模型,命令模式是行為型模型。
  • 2.外觀模式關(guān)注類如何組合聚合來解耦,命令模式關(guān)注方法間的通信來解耦。
  • 3.外觀模式主要為了簡(jiǎn)化操作。封裝內(nèi)部,不符合開閉原則,新增需要修改外觀類。命令模式主要為了發(fā)送方接收方解耦,符合開閉原則
  • 4.控制粒度不一樣。還是用遙控器來舉例,命令模式像一個(gè)多按鈕的遙控器,每個(gè)按鈕是一個(gè)命令,調(diào)用方使用某些按鈕進(jìn)行開關(guān)操作,并且還支持等待執(zhí)行的行為。命令模式能細(xì)粒度的控制每個(gè)按鈕的執(zhí)行順序,執(zhí)行是否撤回。而外觀模式則是更像遙控器上只有一個(gè)按鈕,而這一個(gè)按鈕執(zhí)行多個(gè)設(shè)定好順序的開關(guān)操作。并且中間不能撤回。外觀模式無法穿透外觀類(或接口)去細(xì)粒度的控制內(nèi)部的執(zhí)行順序或某些特殊的邏輯。
    命令模式控制內(nèi)部細(xì)節(jié)操作,外觀模式控制整體操作。
應(yīng)用場(chǎng)景:
  • 1.請(qǐng)求的調(diào)用者和接收者,需要解耦。使得調(diào)用者和接收者不直接交互。
  • 2.需要抽象出等待執(zhí)行的行為。
  • 3.命令模式適合做可撤銷類的應(yīng)用。
    1. 對(duì)沒有使用面向接口編程的時(shí)候,就可以從中間加入一個(gè)command命令抽象層,用命令模式來改造調(diào)用方式。來解耦。

解釋器模式(Interpreter)

定義

給定一個(gè)語(yǔ)言,定義它的文法的一種表示,并定義一個(gè)解釋器,這個(gè)解釋器使用該表示來解釋語(yǔ)言中的句子。

通俗來說

對(duì)于一些固定文法構(gòu)建一個(gè)解釋句子的解釋器。

角色:
  • 抽象表達(dá)式(Expression):抽象表達(dá)式,定義解釋器的接口,約定解釋器的解釋操作,主要包含一個(gè)interpret()方法。
  • 終結(jié)表達(dá)式(TerminalExpression):是抽象表達(dá)式的子類,用來實(shí)現(xiàn)文法中與終結(jié)符相關(guān)的操作,文法中的每一個(gè)終結(jié)符都有一個(gè)具體終結(jié)表達(dá)式與之相對(duì)應(yīng)。
  • 非終結(jié)表達(dá)式(NonTerminalExpression):也是抽象表達(dá)式的子類,用來實(shí)現(xiàn)文法中非終結(jié)符的相關(guān)操作,文法中每條規(guī)則都對(duì)應(yīng)一個(gè)非終結(jié)符表達(dá)式。
  • 上下文容器(Context):context是環(huán)境角色,包含解釋器之外的一些全局信息


    解釋器模式類圖

通過解釋器實(shí)現(xiàn)四則運(yùn)算,如計(jì)算加減法表達(dá)式,UML如圖:


加減法表達(dá)式類圖

上下文容器context

public class Context{
    
    //定義表達(dá)式
    private Expression expression;
    
    //構(gòu)造函數(shù)傳參,并解析
    public Context(String expStr) {
        //安排運(yùn)算先后順序
        Stack<Expression> stack = new Stack<>();
        //表達(dá)式拆分為字符數(shù)組
        char[] charArray = expStr.toCharArray();
        
        Expression left = null;
        Expression right = null;
        for(int i=0; i<charArray.length; i++) {
            switch (charArray[i]) {
            case '+':    //加法
                left = stack.pop();
                right = new VarExpression(String.valueOf(charArray[++i]));
                stack.push(new AddExpression(left, right));
                break;
            case '-':    //減法
                left = stack.pop();
                right = new VarExpression(String.valueOf(charArray[++i]));
                stack.push(new SubExpression(left, right));
                break;
            default:    //公式中的變量
                stack.push(new VarExpression(String.valueOf(charArray[i])));
                break;
            }
        }
        this.expression = stack.pop();
    }
    
    //計(jì)算
    public int run(HashMap<String, Integer> var) {
        return this.expression.interpreter(var);
    }

}

抽象表達(dá)式

public abstract class Expression {
    //解析公式和數(shù)值,key是公式中的參數(shù),value是具體的數(shù)值
    public abstract int interpreter(HashMap<String,Integer> varMap)
}

非終結(jié)解釋器(變量解釋器)

public class VarExpression extends Expression {
    
    private String key;
    
    public VarExpression(String key) {
        this.key = key;
    }

    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return var.get(this.key);
    }

}

終結(jié)解釋器(抽象運(yùn)算符解釋器)

public class SymbolExpression extends Expression {
    
    protected Expression left;
    protected Expression right;

    public SymbolExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpreter(HashMap<String, Integer> var) {
        // TODO Auto-generated method stub
        return 0;
    }
    
}

終結(jié)解釋器實(shí)現(xiàn)1 (加法解釋器)

public class AddExpression extends SymbolExpression {

    public AddExpression(Expression left, Expression right) {
        super(left, right);
    }
    
    public int interpreter(HashMap<String, Integer> var) {
        return super.left.interpreter(var) + super.right.interpreter(var);
    }

}

終結(jié)解釋器實(shí)現(xiàn)2(減法解釋器)

public class SubExpression extends SymbolExpression {
    
    public SubExpression(Expression left, Expression right) {
        super(left, right);
    }
    
    public int interpreter(HashMap<String, Integer> var) {
        return super.left.interpreter(var) - super.right.interpreter(var);
    }

}

調(diào)用方

public class Client {
    
    public static void main(String[] args) throws IOException {
        String expStr = getExpStr();
        HashMap<String, Integer> var = getValue(expStr);
        Context context = new Context (expStr);
        System.out.println("運(yùn)算結(jié)果:" + expStr + "=" + calculator.run(var));
    }
    
    //獲得表達(dá)式
    public static String getExpStr() throws IOException {
        System.out.print("請(qǐng)輸入表達(dá)式:");
        return (new BufferedReader(new InputStreamReader(System.in))).readLine();
    }
    
    //獲得值映射
    public static HashMap<String, Integer> getValue(String expStr) throws IOException {
        HashMap<String, Integer> map = new HashMap<>();
        
        for(char ch : expStr.toCharArray()) {
            if(ch != '+' && ch != '-' ) {
                if(! map.containsKey(String.valueOf(ch))) {
                    System.out.print("請(qǐng)輸入" + String.valueOf(ch) + "的值:");
                    String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                    map.put(String.valueOf(ch), Integer.valueOf(in));
                }
            }
        }
        
        return map;
    }
    
}
優(yōu)點(diǎn):

解決特定重復(fù)出現(xiàn)的場(chǎng)景??蓴U(kuò)展性好

缺點(diǎn):

1.解釋器模式會(huì)引起類膨脹
2.解釋器模式采用遞歸調(diào)用方法,導(dǎo)致調(diào)試復(fù)雜。
3.使用大量循環(huán)遞歸,效率是一個(gè)不容忽視的問題。
盡量不要在重要的模塊中使用解釋器模式,否則維護(hù)是一個(gè)很大的問題。

應(yīng)用場(chǎng)景:

1.正則表達(dá)式
2.Spring el表達(dá)式
3.corn表達(dá)式


迭代器模式(Iterator)

定義

提供一種遍歷集合元素的統(tǒng)一接口,用一致的方法遍歷集合元素,不需要知道集合對(duì)象的底層表示,即不暴露其內(nèi)部結(jié)構(gòu)。

通俗來說

用戶通過特定的接口訪問容器的數(shù)據(jù),不需要了解容器內(nèi)部的數(shù)據(jù)結(jié)構(gòu)。

角色:
  • 抽象迭代器(Iterator):迭代器接口,提供hasNext、next、remove方法。
  • 具體迭代器(ConcreteItorator):具體的迭代器類,管理迭代
  • 聚合接口(aggregate):一個(gè)統(tǒng)一的聚合接口,將調(diào)用方和具體的聚合解耦。包含一個(gè)createIterator方法。
  • 具體聚合(ConcreteAggregate):持有對(duì)象的集合,并提供createIterator的具體方法。
  • element(元素)


    迭代器類圖

聚合接口

public interface Aggregate{
    public abstract Iterator createIterator();
}

迭代器接口

public interface Iterator<T>{
     boolean hasNext();
     T next();
    void remove();
}

元素

public class Book{
    private String name ;
    public Book(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }
}

具體聚合

public class BookShelf implements Aggregate {

    private List<Book> books;


    public BookShelf() {
        this.books = new ArrayList<Book>();
    }

    public Book getBookAt(int index) {
        return books.get(index);
    }

    public void appendBook(Book book) {
        books.add(book);
    }

    public int getLength() {
        return books.size();
    }

    public Iterator iterator() {
        return new BookShelfIterator(this);
    }
}

具體迭代器

public class BookShelfIterator implements Iterator {

    private BookShelf bookShelf;
    private int index;

    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
        this.index = 0;
    }

    public boolean hasNext() {
        if (index < bookShelf.getLength()) {
            return true;
        } else {
            return false;
        }
    }


    public Object next() {
        Book book = bookShelf.getBookAt(index);
        index++;
        return book;
    }
}
優(yōu)點(diǎn):

1.面向接口編程
2.將元素的遍歷和實(shí)現(xiàn)分離開來

為什么不讓容器直接繼承Iterator接口,Iterable接口會(huì)不會(huì)有一種多此一舉的感覺?

個(gè)人認(rèn)為是為了讓數(shù)據(jù)處理邏輯和容器內(nèi)部數(shù)據(jù)管理進(jìn)行分離,提供容器數(shù)據(jù)的安全性。

應(yīng)用場(chǎng)景:

需要遍歷的場(chǎng)景,提供多種遍歷。而無需暴露其內(nèi)部結(jié)構(gòu)。
JDK中 Collection包下基本都實(shí)現(xiàn)了iterator

  • fail-fast:直接在容器上進(jìn)行遍歷,在遍歷過程中一點(diǎn)發(fā)現(xiàn)容器被修改,會(huì)拋出ConcurrentModificationException異常導(dǎo)致遍歷失敗,常見的hashMap arrayList
  • fail-safe:遍歷時(shí)復(fù)制一份容器,對(duì)容器修改不影響遍歷。ConcurrentHaspMap和CopyOnWrite容器

中介者模式(Mediator)

定義

定義一個(gè)中介者對(duì)象, 封裝一系列對(duì)象的交互關(guān)系, 使得各對(duì)象不必顯示的相互引用, 從而使其耦合松散, 而且可以獨(dú)立的改變它們的交互

通俗來說

多個(gè)類相互耦合,會(huì)形成網(wǎng)狀結(jié)構(gòu),使用中介者模式將網(wǎng)站結(jié)構(gòu)分離為星狀結(jié)構(gòu),由中介者來控制這些類之間消息的相互流轉(zhuǎn),從而使其耦合松散。

角色
  • 1.中介者(Mediator):抽象中介者,用于定義統(tǒng)一接口,用于各同事之間的交互。

  • 2.具體中介者(ConcreteMediator):具體中介者持有各同事對(duì)象的引用,負(fù)責(zé)協(xié)調(diào)各同事對(duì)象的行為以完成協(xié)作,因此具體中介者必須依賴具體同事對(duì)象。(不符合依賴倒置原則)

  • 3.同事角色(Colleague):每個(gè)同事角色都知道中介者對(duì)象,而且與其他同事角色通信的時(shí)候,都通過中介者協(xié)作完成。每個(gè)同事角色都有兩種行為:

    • 1.自發(fā)行為:同事本身的行為,如修改自身的狀態(tài),與其他同事或者中介者沒有任何聯(lián)系。
    • 2.依賴方法:必須依賴中介者才能完成的行為。
  • 4.具體同事角色(ConcreteColleague): 實(shí)現(xiàn)抽象同事角色。

中介者模式類圖

中介者

public interface Mediator {
    /**
     * 注冊(cè)同事方法 可以使用構(gòu)造器 而不用此方法
     */
    void register(Member member);

    /**
     * 發(fā)出命令方法
     */
    void notifyOthers(String info,Member member);

具體中介者

public class ConcreteMediator implements Mediator  {
   private HashMap  memberMap = new HashMap<String, Member >();

  public void register( Member member) {
        memberMap.put(member.getClass().getSimpleName(), member);
    }

    public void notifyOthers(String info, Member member){
        String name = member.getClass().getSimpleName();
        switch (name){
            case "Member01": {
                memberMap.get("Member02").recv(info);
                memberMap.get("Member03").recv(info);
                break;
            }
            case "Member02": {
                memberMap.get("Member01").recv(info);
                memberMap.get("Member03").recv(info);
                break;
            }
            case "Member03": {
                memberMap.get("Member01").recv(info);
                memberMap.get("Member02").recv(info);
                break;
            }
        }
    }
}

同事角色

public abstract class Member {
    protected Mediator mediator;
    public Mediator getMediator() {
        return mediator;
    }

    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }
    //依賴方法 將調(diào)用行為傳遞給中介者,讓中介者具體進(jìn)行下一步操作。  也可以將此方法延遲到子類實(shí)現(xiàn)
    public void send(String info){
        mediator.notifyOthers(info,this);
    }
    //自發(fā)行為---中介者調(diào)用  
    public abstract  void recv(String info);  
}

具體同事角色1

public class Member01 extends Member{
    public void recv(String info){
       System.out.println(this.getClass().getName() + " 收到消息: " + info +"進(jìn)行處理1");
    }
}

具體同事角色2

public class Member02 extends Member{
    public void recv(String info){
       System.out.println(this.getClass().getName() + " 收到消息: " + info +"進(jìn)行處理2");
    }
}

具體同事角色3

public class Member03 extends Member{
    public void recv(String info){
       System.out.println(this.getClass().getName() + " 收到消息: " + info +"進(jìn)行處理3");
    }
}

調(diào)用方

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

        Member member01 = new Member01();
        Member member02 = new Member02();
        Member member03 = new Member03();

        Mediator mediator = new Mediator();
        mediator.register(member01);
        mediator.register(member01);
        mediator.register(member01);

        member01.setMediator(mediator);
        member02.setMediator(mediator);
        member03.setMediator(mediator);

        member01.notifyOthers("消息1");
        member02.notifyOthers("消息2");
        member03.notifyOthers("消息3");
    }
}

優(yōu)點(diǎn):

1.多個(gè)類相互耦合,會(huì)形成網(wǎng)狀結(jié)構(gòu),使用中介者模式將網(wǎng)站結(jié)構(gòu)分離為星狀結(jié)構(gòu),進(jìn)行解耦。
2.減少類間的依賴,降低耦合,符合迪米特法則。

缺點(diǎn):

中介者是將各個(gè)類中的耦合,轉(zhuǎn)移到自己的內(nèi)部,中介者承擔(dān)了更多的責(zé)任,一個(gè)中介者出現(xiàn)問題,整個(gè)系統(tǒng)就會(huì)收到影響。
如果設(shè)計(jì)不當(dāng),中介者對(duì)象本身會(huì)過于復(fù)雜。 本身并不符合開閉原則。
應(yīng)用場(chǎng)景:
將網(wǎng)狀依賴關(guān)系轉(zhuǎn)化為星狀依賴關(guān)系。

中介者模式、代理模式、外觀模式區(qū)別

外觀模式(Facade Pattern):定義一個(gè)外觀類,隱藏系統(tǒng)的復(fù)雜性,為客戶端提供簡(jiǎn)化的方法和對(duì)現(xiàn)有系統(tǒng)類方法的委托調(diào)用。
代理模式(Proxy Pattren):用一個(gè)代理類代表另一個(gè)類的功能,但是不改變被代理類的功能。目的是控制對(duì)被代理類的訪問。
中介者模式(Mediator Pattern):各個(gè)類之間互相交互,形成網(wǎng)狀結(jié)構(gòu),中介者使各對(duì)象之間不直接相互引用。從而耦合松散。從而形成網(wǎng)狀結(jié)構(gòu)。例如MVC中的C 就是view和model的中介者。讓view與model 不網(wǎng)狀交互。


觀察者模式(Observer)

定義

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

通俗來說

又被稱為發(fā)布-訂閱模式 一個(gè)對(duì)象狀態(tài)改變時(shí),通知其他對(duì)象。為的是盡量弱化對(duì)象間的依賴。

角色

抽象主題(Subject):也叫抽象目標(biāo)類,提供了一個(gè)用于保存觀察者對(duì)象的集合,和增加刪除,以及通知觀察者的方法。
具體主題(ConcreteSubject):實(shí)現(xiàn)通知方法的具體類。
抽象觀察者(Observer):是一個(gè)抽象類或接口,包含一個(gè)update方法。當(dāng)接到主題更改通知時(shí)被調(diào)用。
具體觀察者(ConcreteOberser):抽象觀察者的實(shí)現(xiàn)。

觀察者模式類圖

抽象主題(Subject)

public class Subject {

    //觀察者數(shù)組
    private Vector<Observer> oVector = new Vector<>();
    
    //增加一個(gè)觀察者
    public void addObserver(Observer observer) {
        this.oVector.add(observer);
    }
    
    //刪除一個(gè)觀察者
    public void deleteObserver(Observer observer) {
        this.oVector.remove(observer);
    }
    
    //通知所有觀察者
    public void notifyObserver() {
        for(Observer observer : this.oVector) {
            observer.update();
        }
    }
    
}

具體主題(ConcreteSubject)

public class ConcreteSubject extend Subject {
   public void doSomething() {
      //...
      super.notifyObserver();
   }

}

抽象觀察者(Observer)

public interface Observer {
    void update();
}

具體觀察者(ConcreteObserver)

public class ConcreteObserver implements Observer {

    @Override
    public void update() {
        System.out.println("收到消息,進(jìn)行處理");
    }

}

調(diào)用方

public class Client {
    
    public static void main(String[] args) {
        //創(chuàng)建一個(gè)主題
        ConcreteSubject subject = new ConcreteSubject();
        //定義一個(gè)觀察者
        Observer observer = new ConcreteObserver();
        //觀察
        subject.addObserver(observer);
        //開始活動(dòng)
        subject.doSomething();
    }
    
}
優(yōu)點(diǎn):

1.實(shí)現(xiàn)了觸發(fā)機(jī)制。
2.觀察者和被觀察者是弱耦合。

觀察者模式和消息中間件這種發(fā)布訂閱模式區(qū)別

發(fā)布訂閱是 通知到broker的topic中 broker再通知訂閱方。中間是有數(shù)據(jù)傳輸。沒有耦合。而觀察者模式是有耦合的。
發(fā)布訂閱更像是觀察者模式+命令模式,觀察者負(fù)責(zé)事件觸發(fā),有消息了觀察者處理觸發(fā),命令模式負(fù)責(zé)解耦發(fā)送方接收方,傳輸?shù)臄?shù)據(jù)其實(shí)就是命令。發(fā)送方并不知道具體會(huì)由哪個(gè)接收方處理。

應(yīng)用場(chǎng)景:

事件的觸發(fā)場(chǎng)景
發(fā)布訂閱這種,跨系統(tǒng)消息變換場(chǎng)景

JDK對(duì)觀察者模式的支持
Observer接口 觀察者接口 觀察者需要實(shí)現(xiàn)的接口
Observable類 是觀察的目標(biāo)發(fā)布方


備忘錄模式(Memento)

定義

在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài),這樣以后就可以將該對(duì)象恢復(fù)到原先保存的狀態(tài)。

通俗來說

給用戶提供一種可以恢復(fù)狀態(tài)的機(jī)制,可以使用戶能夠比較方便地回到某個(gè)歷史狀態(tài)。

角色
  • 原發(fā)器(originator):是一個(gè)普通的類,創(chuàng)建備忘錄的原始角色,備忘錄存儲(chǔ)它的當(dāng)前內(nèi)部狀態(tài),也可以使用備忘錄來恢復(fù)其內(nèi)部狀態(tài)。
  • 備忘錄(Memento):存儲(chǔ)原發(fā)器的內(nèi)部狀態(tài),根據(jù)原發(fā)器來決定保存哪些內(nèi)部狀態(tài)備忘錄一般可以參考原發(fā)器設(shè)計(jì),根據(jù)實(shí)際需要確定備忘錄中的屬性。需要注意的是,除了原發(fā)器和守護(hù)者類之外,備忘錄對(duì)象不能直接供其他類使用。
  • 守護(hù)者(CareTaker):守護(hù)者又稱(管理者,負(fù)責(zé)人)。他負(fù)責(zé)保存?zhèn)渫?,但是不能?duì)備忘錄的內(nèi)容進(jìn)行操作或檢查。在守護(hù)者勒種,可以存儲(chǔ)多個(gè)備忘錄對(duì)象。但是他只負(fù)責(zé)存儲(chǔ),而不能修改對(duì)象,也無需知道對(duì)象的實(shí)現(xiàn)細(xì)節(jié)。
備忘錄模式類圖

原發(fā)器

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

備忘錄

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

守護(hù)者

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

調(diào)用方

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());
   }
}
優(yōu)點(diǎn):

1.提供了狀態(tài)恢復(fù)機(jī)制。
2.提供了封裝,用戶不需要關(guān)心狀態(tài)的保存細(xì)節(jié)。
缺點(diǎn):
如果成員變量過多,勢(shì)必會(huì)占用比較大的資源,而且每次保存都會(huì)消耗一定的內(nèi)存。

應(yīng)用場(chǎng)景:
  • 1.瀏覽器中的后退
  • 2.springSecurity 登錄后跳轉(zhuǎn)到之前訪問的頁(yè)
  • 3.打游戲時(shí)的存檔
  • 4.windows的ctrl+z
  • 5.數(shù)據(jù)庫(kù)里的事務(wù)管理。

一般備忘錄模式和原型模式配合使用。


狀態(tài)模式(State)

定義

當(dāng)一個(gè)對(duì)象的內(nèi)在狀態(tài)改變時(shí)允許改變其行為,這個(gè)對(duì)象看起來像是改變了其類。

通俗來說

狀態(tài)模式很像策略模式,但是關(guān)注于狀態(tài)的轉(zhuǎn)換而改變內(nèi)在行為,而且狀態(tài)之間相互有依賴。
狀態(tài)模式也能減少代碼中的if else 判斷不同的狀態(tài)

角色
  • 抽象狀態(tài)(State):定義一個(gè)接口,來封裝使用Context的一個(gè)特定狀態(tài)相關(guān)的行為。
  • 具體狀態(tài)(ConcreteState):抽象狀態(tài)的實(shí)現(xiàn)。
  • 上下文環(huán)境(Context):定義了客戶程序需要的接口,并且維護(hù)一個(gè)狀態(tài)角色的實(shí)例,將與狀態(tài)相關(guān)的操作委托給狀態(tài)角色實(shí)例來處理。


    狀態(tài)模式類圖

抽象狀態(tài)

public abstract class AbstractState {

    /** 封裝了上下文 */
    protected Context context;

    public void setContext(Context  context){
        this.context = context;
    }

    /** 執(zhí)行方法 */
    abstract void run();

    /** 切換至下一個(gè)狀態(tài) */
    abstract void next();
}

具體狀態(tài)1

public class AState extends AbstractState {

    /** 執(zhí)行方法 */
    @Override
    public void run() {
        System.out.println("執(zhí)行 AState 的 run() 方法");
    }

    /** 切換至下一個(gè)狀態(tài) */
    @Override
    void next() {
        System.out.println("執(zhí)行 AState 的 next() 方法");
        // 定義了下一個(gè)狀態(tài)是 BState
        context.setState(new BState());
    }
}

具體狀態(tài)2

public class BState extends AbstractState {

    /** 執(zhí)行方法 */
    @Override
    public void run() {
        System.out.println("執(zhí)行 BState 的 run() 方法");
    }

    /** 切換至下一個(gè)狀態(tài) */
    @Override
    void next() {
        System.out.println("執(zhí)行 BState 的 next() 方法");
        // 定義了下一個(gè)狀態(tài)是 CState
        context.setState(new CState());
    }
}

具體狀態(tài)3

public class CState extends AbstractState {

    /** 執(zhí)行方法 */
    @Override
    public void run() {
        System.out.println("執(zhí)行 CState 的 run() 方法");
    }

    /** 切換至下一個(gè)狀態(tài) */
    @Override
    void next() {
        System.out.println("執(zhí)行 CState 的 next() 方法");
        System.out.println("已經(jīng)是終態(tài)了!");
    }
}

上下文環(huán)境

public class Context {

    private AbstractState state;

    public AbstractState getState() {
        return state;
    }
    /** 設(shè)置當(dāng)前狀態(tài) */
    public void setState(AbstractState state) {
        this.state = state;
        // 記得 setContext,不然會(huì)空指針
        this.state.setContext(this);
    }
    /** 執(zhí)行方法 */
    public void run(){
        this.state.run();
    }
    /** 下一個(gè)狀態(tài) */
    public void next(){
        this.state.next();
    }
}

調(diào)用方

public class TestMe {

    public static void main(String[] args){
        Context context = new Context();
        context.setState(new AState());

        context.run();
        System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());
        context.next();
        System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());

        context.run();
        System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());
        context.next();
        System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());

        context.run();
        System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());
        context.next();
        System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());

    }
}
優(yōu)點(diǎn)

1.將狀態(tài)相關(guān)的行為封裝在一個(gè)狀態(tài)中,符合單一職責(zé)。

缺點(diǎn)

狀態(tài)較多的情況下,導(dǎo)致類膨脹。
結(jié)構(gòu)和實(shí)現(xiàn)都比較復(fù)雜,使用不當(dāng)造成代碼結(jié)構(gòu)混亂。
并不符合開閉原則,新增狀態(tài),變化到這個(gè)狀態(tài),需要修改與之關(guān)聯(lián)的狀態(tài)

應(yīng)用場(chǎng)景

1.當(dāng)一個(gè)對(duì)象的行為取決于它的狀態(tài),并且在運(yùn)行時(shí)需要根據(jù)狀態(tài)做出不同行為,可以使用狀態(tài)模式。
2.一個(gè)操作中含有大量的if else 并且判斷條件是狀態(tài)的時(shí)候。

狀態(tài)模式和策略模式的區(qū)別
  • 策略模式的客戶端必須對(duì)所有策略類了解,權(quán)衡場(chǎng)景策略選擇。對(duì)客戶暴露。算法之間沒有依賴關(guān)系。策略模式不依賴上下文。
  • 狀態(tài)模式依賴于狀態(tài)變化時(shí),其內(nèi)部行為變化。將動(dòng)作委托到代表當(dāng)前狀態(tài)的對(duì)象,對(duì)外表現(xiàn)為類發(fā)生了變化。狀態(tài)之間有依賴關(guān)系。狀態(tài)模式需要依賴上下文容器。

訪問者模式(Visitor)

設(shè)計(jì)模式中比較復(fù)雜的。

什么是雙重分派?
http://www.itdecent.cn/p/a42928e3f9d3
1.首先在客戶端程序中,將 具體的Visit訪問者為參數(shù)傳遞到了Element中(第一次分派)
2.然后element實(shí)現(xiàn)類 調(diào)用作為參數(shù)的visit中的具體方法,將自己(this)作為參數(shù)傳入,完成第二次分派。

定義

封裝一些作用于某種數(shù)據(jù)結(jié)構(gòu)中的各元素的操作,它可以在不改變這個(gè)數(shù)據(jù)結(jié)構(gòu)的前提下定義作用于這些元素的新的操作。

通俗來說

利用雙重分派機(jī)制,Element接口需要accept方法來接受一個(gè)訪問者,而accept方法中又調(diào)用visit 中的具體方法,傳入element自己(this) 一共這兩次分派,來實(shí)現(xiàn)將數(shù)據(jù)元素和數(shù)據(jù)操作分離。適合對(duì)象結(jié)構(gòu)不經(jīng)常變化的情況。

角色
  • 抽象訪問者(Visitor):是抽象訪問者,為每一個(gè)ConcreteElement的每一個(gè)類聲明一個(gè)visit操作。
  • 具體訪問者(ConcreteVisitor):是一個(gè)具體的訪問者,實(shí)現(xiàn)每個(gè)有Visitor聲明的操作。是每個(gè)操作的實(shí)現(xiàn)部分。
  • 抽象元素(Element):定義一個(gè)accept方法,接收一個(gè)訪問者對(duì)象。
  • 具體元素(ConcreteElement): 實(shí)現(xiàn)了accept方法。
  • 對(duì)象結(jié)構(gòu)(ObjectStructure):
訪問者模式類圖

抽象訪問者

interface Visitor {
   void visit(Games games);
   void visit(Photos photos);
}


抽象元素

interface Computer {
   void accept(Visitor visitor);
}

具體訪問者

class ZhangSan implements Visitor {
   @Override
   public void visit(Games games) {
       games.play();
   }

   @Override
   public void visit(Photos photos) {
       photos.watch();
   }
}

class LiSi implements Visitor {
   @Override
   public void visit(Games games) {
       games.play();
   }
   @Override
   public void visit(Photos photos) {
       photos.watch();
   }
}

具體元素

class Games implements Computer {
   @Override
   public void accept(Visitor visitor) {
       visitor.visit(this);
   }

   public void play() {
       System.out.println("play lol");
   }
}

class Photos implements Computer {
   @Override
   public void accept(Visitor visitor) {
       visitor.visit(this);
   }
   
   public void watch() {
       System.out.println("watch scenery photo");
   }
}

對(duì)象結(jié)構(gòu)

class ObjectStructure {

    private List<Computer> computers = new ArrayList<Computer>();

    public void action(Visitor visitor) {
        computers.forEach(c -> {
            c.accept(visitor);
        });
    }
    public void add(Computer computer) {
        computers.add(computer);
    }
}

調(diào)用方

public static void main(String[] args) {
       // 創(chuàng)建一個(gè)結(jié)構(gòu)對(duì)象
       ObjectStructure os = new ObjectStructure();
       // 給結(jié)構(gòu)增加一個(gè)節(jié)點(diǎn)
       os.add(new Games());
       // 給結(jié)構(gòu)增加一個(gè)節(jié)點(diǎn)
       os.add(new Photos());
       // 創(chuàng)建一個(gè)訪問者
       Visitor visitor = new ZhangSan();
       os.action(visitor);

}
優(yōu)點(diǎn)

1.數(shù)據(jù)元素和數(shù)據(jù)操作解耦,使得操作集合可以獨(dú)立變化。擴(kuò)展性好。
2.符合單一職責(zé)

缺點(diǎn)

1.不適合對(duì)象結(jié)構(gòu)經(jīng)常變化的情況元素?cái)U(kuò)展比較困難,需要修改visitor以及visitor所有的實(shí)現(xiàn)類。
2.元素對(duì)訪問者公布細(xì)節(jié),違反了迪米特法則。
3.依賴具體類,而沒有依賴接口,違反了依賴倒置原則。

應(yīng)用場(chǎng)景

數(shù)據(jù)結(jié)構(gòu)穩(wěn)定,作用于數(shù)據(jù)結(jié)構(gòu)的操作經(jīng)常變化。
讓靜態(tài)分派轉(zhuǎn)為動(dòng)態(tài)分派的情況。

在遍歷集合的過程中對(duì)元素進(jìn)行固定的處理是常有的需求。Visitor模式正是為了應(yīng)對(duì)這種需求而出現(xiàn)的。在訪問元素集合的過程中對(duì)元素進(jìn)行相同的處理,這種模式就是Vistor模式。

?著作權(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)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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