天天看點

行為型設計模式

3.行為型模式

3.1觀察者模式

行為型設計模式
  • 意圖:定義對象間的一種一對多的依賴關系,當一個對象的狀态發生改變時,所有依賴于它的對象都得到通知并被自動更新。
  • 主要解決:一個對象狀态改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協作。
  • 何時使用:一個對象(目标對象)的狀态發生改變,所有的依賴對象(觀察者對象)都将得到通知,進行廣播通知。
  • 如何解決:使用面向對象技術,可以将這種依賴關系弱化。
  • 關鍵代碼:在抽象類裡有一個 ArrayList 存放觀察者們。
  • 優點: 1、觀察者和被觀察者是抽象耦合的。 2、建立一套觸發機制。
  • 缺點: 1、如果一個被觀察者對象有很多的直接和間接的觀察者的話,将所有的觀察者都通知到會花費很多時間。 2、如果在觀察者和觀察目标之間有循環依賴的話,觀察目标會觸發它們之間進行循環調用,可能導緻系統崩潰。 3、觀察者模式沒有相應的機制讓觀察者知道所觀察的目标對象是怎麼發生變化的,而僅僅隻是知道觀察目标發生了變化。
  • 注意事項: 1、JAVA 中已經有了對觀察者模式的支援類。 2、避免循環引用。 3、如果順序執行,某一觀察者錯誤會導緻系統卡殼,一般采用異步方式。
package observerPattern;

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

public class ObserverPattern {
    public static void main(String[] args) {
        Subject subject = new ConcreteSubject();
        Observer obs1 = new ConcreteObserver1();
        Observer obs2 = new ConcreteObserver2();
        subject.add(obs1);
        subject.add(obs2);
        subject.notifyObserver();
    }
}

// 抽象目标
abstract class Subject {
    protected List<Observer> observers = new ArrayList<>();
    // 增加觀察者方法
    public void add(Observer observer) {
        observers.add(observer);
    }
    // 删除觀察者方法
    public void remove(Observer observer) {
        observers.remove(observer);
    }
    public abstract void notifyObserver(); //通知觀察者方法
}

// 具體目标
class ConcreteSubject extends Subject {
    public void notifyObserver() {
        System.out.println("具體目标發生改變...");
        System.out.println("--------------");
        for (Observer obs : observers) {
            obs.response();
        }
    }
}

// 抽象觀察者
interface Observer {
    void response(); //反應
}

// 具體觀察者1
class ConcreteObserver1 implements Observer {
    public void response() {
        System.out.println("具體觀察者1作出反應!");
    }
}

// 具體觀察者1
class ConcreteObserver2 implements Observer {
    public void response() {
        System.out.println("具體觀察者2作出反應!");
    }
}
           
import java.util.ArrayList;
import java.util.List;

public class ObserverPattern {
    public static void main(String[] args) {
        Debit zhangSan = new ZhangSan();
        zhangSan.borrowMoney(new LiSi());
        zhangSan.borrowMoney(new WangWu());

        zhangSan.notifyCredits();

    }
}

// 借款方
interface Debit{
    void borrowMoney(Credit credit); // 借錢 添加觀察者
    void notifyCredits(); // 提醒貸款方收錢    通知觀察者
}

// 貸款方
interface Credit{
    void takeMoney();
}

class ZhangSan implements Debit{
    private List<Credit> allCredits = new ArrayList<>();
    private Boolean hasMoney = false; // 是否有錢

    @Override
    public void borrowMoney(Credit credit) {
        // 主題對象添加觀察者
        allCredits.add(credit);
    }

    @Override
    public void notifyCredits() {
        allCredits.forEach(credit -> credit.takeMoney());
    }
}

class LiSi implements Credit{

    @Override
    public void takeMoney() {
        System.out.println("lisi");
    }
}

class WangWu implements Credit{

    @Override
    public void takeMoney() {
        System.out.println("wangwu");
    }
}
           

3.2政策模式

行為型設計模式
  • 意圖:定義一系列的算法,把它們一個個封裝起來, 并且使它們可互相替換。
  • 主要解決:在有多種算法相似的情況下,使用 if...else 所帶來的複雜和難以維護。
  • 何時使用:一個系統有許多許多類,而區分它們的隻是他們直接的行為。
  • 如何解決:将這些算法封裝成一個一個的類,任意地替換。
  • 關鍵代碼:實作同一個接口。
  • 優點: 1、算法可以自由切換。 2、避免使用多重條件判斷。 3、擴充性良好。
  • 缺點: 1、政策類會增多。 2、所有政策類都需要對外暴露。
  • 注意事項:如果一個系統的政策多于四個,就需要考慮使用混合模式,解決政策類膨脹的問題。
package strategypattern;

public class StrategyPattern {
    public static void main(String[] args) {
        Context c = new Context();

        Strategy s = new ConcreteStrategyA();
        c.setStrategy(s);
        c.strategyMethod();

        s = new ConcreteStrategyB();
        c.setStrategy(s);
        c.strategyMethod();
    }
}
// 抽象政策類:定義了一個公共接口,各種不同的算法以不同的方式實作這個接口,環境角色使用這個接口調用不同的算法,一般使用接口或抽象類實作。
interface Strategy {
    void strategyMethod();    //政策方法
}

// 具體政策類A
class ConcreteStrategyA implements Strategy {
    public void strategyMethod() {
        System.out.println("具體政策A的政策方法被通路!");
    }
}

// 具體政策類B
class ConcreteStrategyB implements Strategy {
    public void strategyMethod() {
        System.out.println("具體政策B的政策方法被通路!");
    }
}

// 環境類:持有一個政策類的引用,最終給用戶端調用。
class Context {
    private Strategy strategy;
    public Strategy getStrategy() {
        return strategy;
    }
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    public void strategyMethod() {
        strategy.strategyMethod();
    }
}
           

3.3指令模式

行為型設計模式
  • 意圖:将一個請求封裝成一個對象,進而使您可以用不同的請求對客戶進行參數化。
  • 主要解決:在軟體系統中,行為請求者與行為實作者通常是一種緊耦合的關系,但某些場合,比如需要對行為進行記錄、撤銷或重做、事務等處理時,這種無法抵禦變化的緊耦合的設計就不太合适。
  • 何時使用:在某些場合,比如要對行為進行"記錄、撤銷/重做、事務"等處理,這種無法抵禦變化的緊耦合是不合适的。在這種情況下,如何将"行為請求者"與"行為實作者"解耦?将一組行為抽象為對象,可以實作二者之間的松耦合。
  • 如何解決:通過調用者調用接受者執行指令,順序:調用者→指令→接受者。
  • 關鍵代碼:定義三個角色:1、received 真正的指令執行對象 2、Command 3、invoker 使用指令對象的入口
  • 優點: 1、降低了系統耦合度。 2、新的指令可以很容易添加到系統中去。
  • 缺點:使用指令模式可能會導緻某些系統有過多的具體指令類。
  • 注意事項:系統需要支援指令的撤銷(Undo)操作和恢複(Redo)操作,也可以考慮使用指令模式,見指令模式的擴充。
package commandpattern;

public class CommandPattern {
    public static void main(String[] args) {
        Command cmd = new ConcreteCommand();
        Invoker ir = new Invoker(cmd);
        System.out.println("客戶通路調用者的call()方法...");
        ir.call();
    }
}

// 調用者:是請求的發送者,它通常擁有很多的指令對象,并通過通路指令對象來執行相關請求,它不直接通路接收者。
class Invoker {
    private Command command;
    public Invoker(Command command) {
        this.command = command;
    }
    public void setCommand(Command command) {
        this.command = command;
    }
    public void call() {
        System.out.println("調用者執行指令command...");
        command.execute();
    }
}

// 抽象指令:聲明執行指令的接口,擁有執行指令的抽象方法 execute()。
interface Command {
    void execute();
}

// 具體指令:是抽象指令類的具體實作類,它擁有接收者對象,并通過調用接收者的功能來完成指令要執行的操作。
class ConcreteCommand implements Command {
    private Receiver receiver;
    ConcreteCommand() {
        receiver = new Receiver();
    }
    @Override
    public void execute() {
        receiver.action();
    }
}

// 接收者:執行指令功能的相關操作,是具體指令對象業務的真正實作者。
class Receiver {
    public void action() {
        System.out.println("接收者的action()方法被調用...");
    }
}
           

3.4職責鍊模式

行為型設計模式
行為型設計模式
  • 意圖:避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,将這些對象連接配接成一條鍊,并且沿着這條鍊傳遞請求,直到有對象處理它為止。
  • 主要解決:職責鍊上的處理者負責處理請求,客戶隻需要将請求發送到職責鍊上即可,無須關心請求的處理細節和請求的傳遞,是以職責鍊将請求的發送者和請求的處理者解耦了。
  • 何時使用:
    1. 多個對象可以處理一個請求,但具體由哪個對象處理該請求在運作時自動确定。
    2. 可動态指定一組對象處理請求,或添加新的處理者。
    3. 需要在不明确指定請求處理者的情況下,向多個處理者中的一個送出請求。
  • 如何解決:攔截的類都實作統一接口。
  • 關鍵代碼:Handler 裡面聚合它自己,在 HandlerRequest 裡判斷是否合适,如果沒達到條件則向下傳遞,向誰傳遞之前 set 進去。
  • 優點: 1、降低耦合度。它将請求的發送者和接收者解耦。 2、簡化了對象。使得對象不需要知道鍊的結構。 3、增強給對象指派職責的靈活性。通過改變鍊内的成員或者調動它們的次序,允許動态地新增或者删除責任。 4、增加新的請求處理類很友善。
  • 缺點: 1、不能保證請求一定被接收。 2、系統性能将受到一定影響,而且在進行代碼調試時不太友善,可能會造成循環調用。 3、可能不容易觀察運作時的特征,有礙于除錯。
  • 注意事項:在 JAVA WEB 中遇到很多應用。
package chainOfResponsibilityPattern;

public class ChainOfResponsibilityPattern {
    public static void main(String[] args) {
        // 建立處理鍊,并向鍊頭的具體處理者對象送出請求,它不關心處理細節和請求的傳遞過程。
        // 組裝責任鍊
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        handler1.setNext(handler2);
        // 送出請求
        handler1.handleRequest("two");
    }
}

// 抽象處理者角色:定義一個處理請求的接口,包含抽象處理方法和一個後繼連接配接。
abstract class Handler {
    private Handler next;
    public void setNext(Handler next) {
        this.next = next;
    }
    public Handler getNext() {
        return next;
    }
    //處理請求的方法
    public abstract void handleRequest(String request);
}

// 具體處理者角色1:實作抽象處理者的處理方法,判斷能否處理本次請求,如果可以處理請求則處理,否則将該請求轉給它的後繼者。
class ConcreteHandler1 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("one")) {
            System.out.println("具體處理者1負責處理該請求!");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(request);
            } else {
                System.out.println("沒有人處理該請求!");
            }
        }
    }
}

// 具體處理者角色2
class ConcreteHandler2 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("two")) {
            System.out.println("具體處理者2負責處理該請求!");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(request);
            } else {
                System.out.println("沒有人處理該請求!");
            }
        }
    }
}
           

3.5狀态模式

行為型設計模式
  • 意圖:允許對象在内部狀态發生改變時改變它的行為,對象看起來好像修改了它的類。
  • 主要解決:對象的行為依賴于它的狀态(屬性),并且可以根據它的狀态改變而改變它的相關行為。
  • 何時使用:代碼中包含大量與對象狀态有關的條件語句。
  • 如何解決:将各種具體的狀态類抽象出來。
  • 關鍵代碼:通常指令模式的接口中隻有一個方法。而狀态模式的接口中有一個或者多個方法。而且,狀态模式的實作類的方法,一般傳回值,或者是改變執行個體變量的值。也就是說,狀态模式一般和對象的狀态有關。實作類的方法有不同的功能,覆寫接口中的方法。狀态模式和指令模式一樣,也可以用于消除 if...else 等條件選擇語句。
  • 優點: 1、封裝了轉換規則。 2、枚舉可能的狀态,在枚舉狀态之前需要确定狀态種類。 3、将所有與某個狀态有關的行為放到一個類中,并且可以友善地增加新的狀态,隻需要改變對象狀态即可改變對象的行為。 4、允許狀态轉換邏輯與狀态對象合成一體,而不是某一個巨大的條件語句塊。 5、可以讓多個環境對象共享一個狀态對象,進而減少系統中對象的個數。
  • 缺點: 1、狀态模式的使用必然會增加系統類和對象的個數。 2、狀态模式的結構與實作都較為複雜,如果使用不當将導緻程式結構和代碼的混亂。 3、狀态模式對"開閉原則"的支援并不太好,對于可以切換狀态的狀态模式,增加新的狀态類需要修改那些負責狀态轉換的源代碼,否則無法切換到新增狀态,而且修改某個狀态類的行為也需修改對應類的源代碼。
  • 注意事項:在行為受狀态限制的時候使用狀态模式,而且狀态不超過 5 個。
package statePattern;

public class StatePattern {
    public static void main(String[] args) {
        Context context = new Context();
        context.Handle();
        context.Handle();
        context.Handle();
        context.Handle();
    }
}

// 環境類:它定義了用戶端需要的接口,内部維護一個目前狀态,并負責具體狀态的切換。
class Context {
    private State state;
    // 定義環境類的初始狀态
    public Context() {
        this.state = new ConcreteStateA();
    }
    // 設定新狀态
    public void setState(State state) {
        this.state = state;
    }
    // 讀取狀态
    public State getState() {
        return (state);
    }
    // 對請求做處理
    public void Handle() {
        state.Handle(this);
    }
}

// 抽象狀态類:定義一個接口,用以封裝環境對象中的特定狀态所對應的行為,可以有一個或多個行為。
abstract class State {
    public abstract void Handle(Context context);
}

// 具體狀态A類:實作抽象狀态所對應的行為,并且在需要的情況下進行狀态切換。
class ConcreteStateA extends State {
    public void Handle(Context context) {
        System.out.println("目前狀态是 A.");
        context.setState(new ConcreteStateB());
    }
}

// 具體狀态B類
class ConcreteStateB extends State {
    public void Handle(Context context) {
        System.out.println("目前狀态是 B.");
        context.setState(new ConcreteStateA());
    }
}
           

3.6中介者模式

行為型設計模式
  • 意圖:用一個中介對象來封裝一系列的對象互動,中介者使各對象不需要顯式地互相引用,進而使其耦合松散,而且可以獨立地改變它們之間的互動。
  • 主要解決:對象與對象之間存在大量的關聯關系,這樣勢必會導緻系統的結構變得很複雜,同時若一個對象發生改變,我們也需要跟蹤與之相關聯的對象,同時做出相應的處理。
  • 何時使用:多個類互相耦合,形成了網狀結構。
  • 如何解決:将上述網狀結構分離為星型結構。
  • 關鍵代碼:對象 Colleague 之間的通信封裝到一個類中單獨處理。
  • 優點: 1、降低了類的複雜度,将一對多轉化成了一對一。 2、各個類之間的解耦。 3、符合迪米特原則。
  • 缺點:中介者會龐大,變得複雜難以維護。
  • 注意事項:不應當在職責混亂的時候使用。
package mediatorPattern;

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

public class MediatorPattern {
    public static void main(String[] args) {
        Mediator md = new ConcreteMediator();
        Colleague c1, c2;
        c1 = new ConcreteColleague1();
        c2 = new ConcreteColleague2();
        md.register(c1);
        md.register(c2);
        c1.send();
        System.out.println("-------------");
        c2.send();
    }
}

// 抽象中介者:是中介者的接口,提供了同僚對象注冊與轉發同僚對象資訊的抽象方法。
abstract class Mediator {
    public abstract void register(Colleague colleague);
    public abstract void relay(Colleague cl); // 轉發
}

// 具體中介者:實作中介者接口,定義一個 List 來管理同僚對象,協調各個同僚角色之間的互動關系,是以它依賴于同僚角色。
class ConcreteMediator extends Mediator {
    private List<Colleague> colleagues = new ArrayList<>();
    @Override
    public void register(Colleague colleague) {
        if (!colleagues.contains(colleague)) {
            colleagues.add(colleague);
            colleague.setMedium(this); // 設定中介是自己
        }
    }
    @Override
    public void relay(Colleague cl) {
        for (Colleague ob : colleagues) {
            if (!ob.equals(cl)) {
                ob.receive();
            }
        }
    }
}

// 抽象同僚類:定義同僚類的接口,儲存中介者對象,提供同僚對象互動的抽象方法,實作所有互相影響的同僚類的公共功能。
abstract class Colleague {
    protected Mediator mediator;
    public void setMedium(Mediator mediator) {
        this.mediator = mediator;
    }
    public abstract void receive();
    public abstract void send();
}

// 具體同僚類:是抽象同僚類的實作者,當需要與其他同僚對象互動時,由中介者對象負責後續的互動。
class ConcreteColleague1 extends Colleague {
    public void receive() {
        System.out.println("具體同僚類1收到請求。");
    }
    public void send() {
        System.out.println("具體同僚類1送出請求。");
        mediator.relay(this); // 請中介者轉發
    }
}

// 具體同僚類
class ConcreteColleague2 extends Colleague {
    public void receive() {
        System.out.println("具體同僚類2收到請求。");
    }
    public void send() {
        System.out.println("具體同僚類2送出請求。");
        mediator.relay(this); // 請中介者轉發
    }
}
           

3.7疊代器模式

行為型設計模式
  • 意圖:提供一種方法順序通路一個聚合對象中各個元素, 而又無須暴露該對象的内部表示。
  • 主要解決:不同的方式來周遊整個整合對象。
  • 何時使用:周遊一個聚合對象。
  • 如何解決:把在元素之間遊走的責任交給疊代器,而不是聚合對象。
  • 優點: 1、它支援以不同的方式周遊一個聚合對象。 2、疊代器簡化了聚合類。 3、在同一個聚合上可以有多個周遊。 4、在疊代器模式中,增加新的聚合類和疊代器類都很友善,無須修改原有代碼。
  • 缺點:由于疊代器模式将存儲資料和周遊資料的職責分離,增加新的聚合類需要對應增加新的疊代器類,類的個數成對增加,這在一定程度上增加了系統的複雜性。
  • 注意事項:疊代器模式就是分離了集合對象的周遊行為,抽象出一個疊代器類來負責,這樣既可以做到不暴露集合的内部結構,又可讓外部代碼透明地通路集合内部的資料。
package iteratorPattern;

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

public class IteratorPattern {
    public static void main(String[] args) {
        Aggregate ag = new ConcreteAggregate();
        ag.add("中山大學");
        ag.add("華南理工");
        ag.add("韶關學院");
        System.out.print("聚合的内容有:");
        Iterator it = ag.getIterator();
        while (it.hasNext()) {
            Object ob = it.next();
            System.out.print(ob.toString() + "\t");
        }
        Object ob = it.first();
        System.out.println("\nFirst:" + ob.toString());
    }
}

// 抽象聚合:定義存儲、添加、删除聚合對象以及建立疊代器對象的接口。
interface Aggregate {
    void add(Object obj);
    void remove(Object obj);
    Iterator getIterator();
}

// 具體聚合:實作抽象聚合類,傳回一個具體疊代器的執行個體。
class ConcreteAggregate implements Aggregate {
    private List<Object> list = new ArrayList<>();
    
    @Override
    public void add(Object obj) {
        list.add(obj);
    }
    @Override
    public void remove(Object obj) {
        list.remove(obj);
    }
    @Override
    public Iterator getIterator() {
        return (new ConcreteIterator(list));
    }
}

// 抽象疊代器:定義通路和周遊聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
interface Iterator {
    Object first();
    Object next();
    boolean hasNext();
}

// 具體疊代器:實作抽象疊代器接口中所定義的方法,完成對聚合對象的周遊,記錄周遊的目前位置。
class ConcreteIterator implements Iterator {
    private List<Object> list = null;
    private int index = -1;
    public ConcreteIterator(List<Object> list) {
        this.list = list;
    }
    @Override
    public boolean hasNext() {
        if (index < list.size() - 1) {
            return true;
        } else {
            return false;
        }
    }
    public Object first() {
        index = 0;
        Object obj = list.get(index);
        ;
        return obj;
    }
    public Object next() {
        Object obj = null;
        if (this.hasNext()) {
            obj = list.get(++index);
        }
        return obj;
    }
}
           

3.8通路者模式

行為型設計模式
  • 意圖:主要将資料結構與資料操作分離。
  • 主要解決:穩定的資料結構和易變的操作耦合問題。
  • 何時使用:需要對一個對象結構中的對象進行很多不同的并且不相關的操作,而需要避免讓這些操作"污染"這些對象的類,使用通路者模式将這些封裝到類中。
  • 如何解決:在被通路的類裡面加一個對外提供接待通路者的接口。
  • 關鍵代碼:在資料基礎類裡面有一個方法接受通路者,将自身引用傳入通路者。
  • 優點: 1、符合單一職責原則。 2、優秀的擴充性。 3、靈活性。
  • 缺點: 1、具體元素對通路者公布細節,違反了迪米特原則。 2、具體元素變更比較困難。 3、違反了依賴倒置原則,依賴了具體類,沒有依賴抽象。
  • 注意事項:通路者可以對功能進行統一,可以做報表、UI、攔截器與過濾器。
package visitorPattern;

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

public class VisitorPattern {
    public static void main(String[] args) {
        ObjectStructure os = new ObjectStructure();
        os.add(new ConcreteElementA());
        os.add(new ConcreteElementB());
        Visitor visitor = new ConcreteVisitorA();
        os.accept(visitor);
        System.out.println("------------------------");
        visitor = new ConcreteVisitorB();
        os.accept(visitor);
    }
}

// 抽象通路者:定義一個通路具體元素的接口,為每個具體元素類對應一個通路操作 visit() ,
// 該操作中的參數類型辨別了被通路的具體元素。
interface Visitor {
    void visit(ConcreteElementA element);
    void visit(ConcreteElementB element);
}

// 具體通路者A類:實作抽象通路者角色中聲明的各個通路操作,确定通路者通路一個元素時該做什麼。
class ConcreteVisitorA implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("具體通路者A通路-->" + element.operationA());
    }
    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("具體通路者A通路-->" + element.operationB());
    }
}

// 具體通路者B類
class ConcreteVisitorB implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("具體通路者B通路-->" + element.operationA());
    }
    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("具體通路者B通路-->" + element.operationB());
    }
}

// 抽象元素類:聲明一個包含接受操作 accept() 的接口,被接受的通路者對象作為 accept() 方法的參數。
interface Element {
    void accept(Visitor visitor);
}

// 具體元素A類:實作抽象元素角色提供的 accept() 操作,其方法體通常都是 visitor.visit(this) ,
// 另外具體元素中可能還包含本身業務邏輯的相關操作。
class ConcreteElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public String operationA() {
        return "具體元素A的操作。";
    }
}

// 具體元素B類
class ConcreteElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public String operationB() {
        return "具體元素B的操作。";
    }
}

// 對象結構角色:是一個包含元素角色的容器,提供讓通路者對象周遊容器中的所有元素的方法,
// 通常由 List、Set、Map 等聚合類實作。
class ObjectStructure {
    private List<Element> list = new ArrayList<Element>();
    public void accept(Visitor visitor) {
        Iterator<Element> i = list.iterator();
        while (i.hasNext()) {
            i.next().accept(visitor);
        }
    }
    public void add(Element element) {
        list.add(element);
    }
    public void remove(Element element) {
        list.remove(element);
    }
}
           

3.9備忘錄模式

行為型設計模式
  • 意圖:在不破壞封裝性的前提下,捕獲一個對象的内部狀态,并在該對象之外儲存這個狀态。
  • 主要解決:所謂備忘錄模式就是在不破壞封裝的前提下,捕獲一個對象的内部狀态,并在該對象之外儲存這個狀态,這樣可以在以後将對象恢複到原先儲存的狀态。
  • 何時使用:很多時候我們總是需要記錄一個對象的内部狀态,這樣做的目的就是為了允許使用者取消不确定或者錯誤的操作,能夠恢複到他原先的狀态,使得他有"後悔藥"可吃。
  • 如何解決:通過一個備忘錄類專門存儲對象狀态。
  • 關鍵代碼:客戶不與備忘錄類耦合,與備忘錄管理類耦合。
  • 優點: 1、給使用者提供了一種可以恢複狀态的機制,可以使使用者能夠比較友善地回到某個曆史的狀态。 2、實作了資訊的封裝,使得使用者不需要關心狀态的儲存細節。
  • 缺點:消耗資源。如果類的成員變量過多,勢必會占用比較大的資源,而且每一次儲存都會消耗一定的記憶體。
  • 注意事項: 1、為了符合迪米特原則,還要增加一個管理備忘錄的類。 2、為了節約記憶體,可使用原型模式+備忘錄模式。
package mementoPattern;

public class MementoPattern {
    public static void main(String[] args) {
        Originator or = new Originator();
        Caretaker cr = new Caretaker();
        or.setState("S0");
        System.out.println("初始狀态:" + or.getState());
        cr.setMemento(or.createMemento()); //儲存狀态
        or.setState("S1");
        System.out.println("新的狀态:" + or.getState());
        or.restoreMemento(cr.getMemento()); //恢複狀态
        System.out.println("恢複狀态:" + or.getState());
    }
}

// 備忘錄:記錄目前時刻的内部狀态資訊,提供建立備忘錄和恢複備忘錄資料的功能,實作其他業務功能,它可以通路備忘錄裡的所有資訊。
class Memento {
    private String state;
    public Memento(String state) {
        this.state = state;
    }
    public void setState(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
}

// 發起人:負責存儲發起人的内部狀态,在需要的時候提供這些内部狀态給發起人。
class Originator {
    private String state;
    public void setState(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }

    public Memento createMemento() {
        return new Memento(state);
    }
    public void restoreMemento(Memento m) {
        this.setState(m.getState());
    }
}

// 管理者:對備忘錄進行管理,提供儲存與擷取備忘錄的功能,但其不能對備忘錄的内容進行通路與修改。
class Caretaker {
    private Memento memento;
    public void setMemento(Memento m) {
        memento = m;
    }
    public Memento getMemento() {
        return memento;
    }
}
           

3.10模闆方法模式

行為型設計模式
  • 意圖:定義一個操作中的算法的骨架,而将一些步驟延遲到子類中。模闆方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
  • 主要解決:一些方法通用,卻在每一個子類都重新寫了這一方法。
  • 何時使用:有一些通用的方法。
  • 如何解決:将這些通用算法抽象出來。
  • 缺點:每一個不同的實作都需要一個子類來實作,導緻類的個數增加,使得系統更加龐大。
  • 注意事項:為防止惡意操作,一般模闆方法都加上 final 關鍵詞。
package templatemethodpattern;

public class TemplateMethodPattern {
    public static void main(String[] args) {
        AbstractClass tm = new ConcreteClass();
        tm.TemplateMethod();
    }
}
//抽象類
abstract class AbstractClass {
    //模闆方法:定義了算法的骨架,按某種順序調用其包含的基本方法。
    public void TemplateMethod() {
        SpecificMethod();
        abstractMethod1();
        abstractMethod2();
    }
    //具體方法:在抽象類中已經實作,在具體子類中可以繼承或重寫它。
    public void SpecificMethod() {
        System.out.println("抽象類中的具體方法被調用...");
    }
    //抽象方法1:在抽象類中聲明,由具體子類實作。
    public abstract void abstractMethod1();
    //抽象方法2
    public abstract void abstractMethod2();
}
//具體子類
class ConcreteClass extends AbstractClass {
    public void abstractMethod1() {
        System.out.println("抽象方法1的實作被調用...");
    }
    public void abstractMethod2() {
        System.out.println("抽象方法2的實作被調用...");
    }
}
           

3.11解釋器模式

  • 意圖:給定一個語言,定義它的文法表示,并定義一個解釋器,這個解釋器使用該辨別來解釋語言中的句子。
  • 主要解決:對于一些固定文法建構一個解釋句子的解釋器。
  • 何時使用:如果一種特定類型的問題發生的頻率足夠高,那麼可能就值得将該問題的各個執行個體表述為一個簡單語言中的句子。這樣就可以建構一個解釋器,該解釋器通過解釋這些句子來解決該問題。
  • 如何解決:建構文法樹,定義終結符與非終結符。
  • 關鍵代碼:建構環境類,包含解釋器之外的一些全局資訊,一般是 HashMap。
  • 優點: 1、可擴充性比較好,靈活。 2、增加了新的解釋表達式的方式。 3、易于實作簡單文法。
  • 缺點: 1、可利用場景比較少。 2、對于複雜的文法比較難維護。 3、解釋器模式會引起類膨脹。 4、解釋器模式采用遞歸調用方法。
  • 注意事項:可利用場景比較少,JAVA 中如果碰到可以用 expression4J 代替。
package interpreterPattern;

public class InterpreterPattern {
    // 主要任務是将需要分析的句子或表達式轉換成使用解釋器對象描述的抽象文法樹,
    // 然後調用解釋器的解釋方法,當然也可以通過環境角色間接通路解釋器的解釋方法。
}

// 抽象表達式類:定義解釋器的接口,約定解釋器的解釋操作,主要包含解釋方法 interpret()。
interface AbstractExpression {
    void interpret(String info);    // 解釋方法
}

// 終結符表達式類:是抽象表達式的子類,用來實作文法中與終結符相關的操作,
// 文法中的每一個終結符都有一個具體終結表達式與之相對應。
class TerminalExpression implements AbstractExpression {
    public void interpret(String info) {
        // 對終結符表達式的處理
    }
}

// 非終結符表達式類:也是抽象表達式的子類,用來實作文法中與非終結符相關的操作,
// 文法中的每條規則都對應于一個非終結符表達式。
class NonterminalExpression implements AbstractExpression {
    private AbstractExpression exp1;
    private AbstractExpression exp2;
    public void interpret(String info) {
        // 非對終結符表達式的處理
    }
}

// 環境類:通常包含各個解釋器需要的資料或是公共的功能,一般用來傳遞被所有解釋器共享的資料,
// 後面的解釋器可以從這裡擷取這些值。
class Context {
    private AbstractExpression exp;
    public Context() {
        // 資料初始化
    }
    public void operation(String info) {
        // 調用相關表達式類的解釋方法
    }
}