設計模式-行爲型設計模式

續上一篇文章《設計模式-結構型設計模式》
https://blog.csdn.net/weixin_37624828/article/details/105955923

4 行爲型設計模式

4.1 策略模式

概述:一個類的行爲或其算法可以在運行時更改,這種類型的設計模式屬於行爲型模式。
如何創建:將這些算法封裝成一個一個的類,實現同一個接口。
何時使用:一個系統有許許多多類,而區分它們的只是它們的直接行爲。
優點

  1. 算法可以自由切換;
  2. 避免使用多重條件判斷;
  3. 擴展性良好

缺點

  1. 策略類會增多;
  2. 所有的策略類都需要對外暴露;

使用場景

  1. 一個對象需要在許多行爲中選擇一種行爲的時候;
  2. 減少if…else…;

示例
代碼清單:

  1. Strategy.java -----interface
  2. OperationAdd.java
  3. OperationMultiply.java
  4. OperationSubtract.java
  5. Context.java
  6. StrategyPatternDemo.java
    1.Strategy.java
public interface Strategy {
    public int doOperation(int num1, int num2);
}

2.OperationAdd.java

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

3.OperationMultiply.java

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

4.OperationSubtract.java

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

5.Context.java

public class Context {
    private Strategy strategy;

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

    public int executeOperation(int num1, int num2){
        return strategy.doOperation(num1, num2);
    }
}

6.StrategyPatternDemo.java

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

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

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

4.2 模板模式

概述:一個抽象類公開定義了執行它的方法的方式、模板。它的子類可以按需要重寫方法實現,但調用將以抽象類中定義的方式進行。
如何創建:定義一個操作中的算法骨架,再定義一些相關抽象方法,子類實現這些抽象方法。
何時使用:有一些通用的方法。
優點

  1. 封裝不變部分,擴展可變部分;
  2. 提取公共代碼,便於維護;
  3. 行爲由父類控制,子類實現;

缺點:每一個不同的實現都需要一個子類來實現,導致類的個數增加,使得系統更加龐大。

使用場景

  1. 有多個子類共有的方法,且邏輯相同;
  2. 重要的、複雜的方法,可以考慮作爲模板方法;

示例
代碼清單:

  1. Game.java
  2. Football.java
  3. Basketball.java
  4. TemplatePatternDemo.java
    1.Game.java
public abstract class Game {
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();

    final void play(){
        initialize();
        startPlay();
        endPlay();
    }
}

2.Football.java

public class Football extends Game {
    @Override
    void initialize() {
        System.out.println("Football Game initialize.");
    }

    @Override
    void startPlay() {
        System.out.println("Football Game start play.");
    }

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

3.Basketball.java

public class Basketball extends Game {
    @Override
    void initialize() {
        System.out.println("Basketball Game initialize.");
    }

    @Override
    void startPlay() {
        System.out.println("Basketball Game start play.");
    }

    @Override
    void endPlay() {
        System.out.println("Basketball Game end play.");
    }
}

4.TemplatePatternDemo.java

public class TemplatePatternDemo {
    public static void main(String[] args) {
        Game football = new Football();
        Game basketball = new Basketball();

        football.play();
        basketball.play();

    }
}

4.3 觀察者模式

概述:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。
如何創建:在抽象類裏有一個ArrayList存放觀察者們。
何時使用:一個對象的狀態發生改變,所有的依賴對象都將得到通知。
優點

  1. 觀察者和被觀察者是抽象耦合的;
  2. 建立一套觸發機制;

缺點

  1. 如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間;
  2. 如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰;
  3. 觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化;

使用場景

  1. 一個抽象模型有兩個方面,其中一個方面依賴於另一個方面。將這些方面封裝在獨立地對象中使它們可以各自獨立地改變和複用;
  2. 一個對象的改變將導致其他一個或多個對象也發生改變,而不知道具體有多少對象將發生改變,可以降低對象之間的耦合度;
  3. 一個對象必須通知其他對象,而並不知道這些對象是誰;
  4. 需要在系統中創建一個觸發鏈,A對象的行爲將影響B對象,B對象的行爲將影響C對象……,可以使用觀察者模式創建一種鏈式觸發機制;

示例
代碼清單:

  1. Observer.java
  2. BinaryObserver.java
  3. HexObserver.java
  4. OctalObserver.java
  5. Subject.java
  6. ObserverPatternDemo
    1.Observer.java
public abstract class Observer {
    protected Subject subject;
    public abstract void update();
}

2.BinaryObserver.java

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

3.HexObserver.java

public class HexObserver extends Observer {
    public HexObserver(Subject subject){
        this.subject = subject;
        this.subject.attach(this);
    }
    @Override
    public void update() {
        System.out.println("Hex String: " + Integer.toHexString(subject.getState()));

    }
}

4.OctalObserver.java

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

5.Subject.java

public class Subject {
    private List<Observer> observers = new ArrayList<>();
    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 notifyAllObservers(){
        for(Observer observer : observers){
            observer.update();
        }
    }
}

6.ObserverPatternDemo.java

public class ObserverPatternDemo {
    public static void main(String[] args) {
        Subject subject = new Subject();
        new BinaryObserver(subject);
        new OctalObserver(subject);
        new HexObserver(subject);
        System.out.println("First State change: 15");
        subject.setState(15);
        System.out.println("Second State change: 10");
        subject.setState(10);
    }
}

4.4 訪問者模式

概述:主要將數據結構與數據操作分離。
如何創建:在數據基礎類裏面有一個方法接受訪問者,將自身引用傳入訪問者。
何時使用:需要對一個對象結構中的對象進行很多不同的並且不相關的操作,而需要避免讓這些操作“污染”這些對象的類,使用訪問者模式將這些封裝到類中。
優點

  1. 符合單一職責原則;
  2. 優秀的擴展性;
  3. 靈活性;

缺點

  1. 具體元素對訪問者公佈細節,違反了迪米特原則;
  2. 具體元素變更比較困難難;
  3. 違反了依賴倒置原則,依賴了具體類,沒有依賴抽象

使用場景

  1. 對象結構中對象對應的類很少改變,但經常需要在此對象結構上定義新的操作;
  2. 需要對一個對象結構中的對象進行很多不同的並且不相關的操作,而需要避免讓這些操作“污染”這些對象的類,也不希望在增加新操作時修改這些類。

示例
代碼清單:

  1. ComputerPart.java -----interface
  2. ComputerPartVisitor.java -----interface
  3. Computer.java
  4. Keyboard.java
  5. Monitor.java
  6. Mouse.java
  7. ComputerPartDisplayVisitor.java
  8. VisitorPatternDemo.java
    1.ComputerPart.java
public interface ComputerPart {
    public void accept(ComputerPartVisitor computerPartVisitor);
}

2.ComputerPartVisitor.java

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

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

4.Keyboard.java

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

5.Monitor.java

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

6.Mouse.java

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

7.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 Keyborad.");
    }

    @Override
    public void visit(Monitor monitor) {
        System.out.println("Displaying Monitor.");
    }
}

8.VisitorPatternDemo.java

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

4.5 中介者模式

概述:該模式是用來降低多個對象和類之間的通信複雜性。這種模式提供了一箇中介類,該類通常處理不同類之間的通信,並支持鬆耦合,使代碼易於維護。中介者模式屬於行爲型模式。
如何創建:對象 Colleague 之間的通信封裝到一個類中單獨處理
何時使用:多個類相互耦合,形成了網狀結構
優點

  1. 降低了類的複雜度,將一對多轉化成了一對一;
  2. 各個類之間的解耦;
  3. 符合迪米特原則;

缺點:中介者會龐大,變得複雜難以維護
使用場景

  1. 系統中對象之間存在比較複雜的引用關係,導致它們之間的依賴關係結構混亂而且難以複用該對象;
  2. 想通過一箇中間類來封裝多個類中的行爲,而又不想生成太多的子類;

示例
代碼清單:

  1. User.java
  2. ChatRoom.java
  3. MediatorPatternDemo.java
    1.User.java
public class User {
    private String name;

    User(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void sendMessage(String message){
        ChatRoom.sendMessage(this, message);
    }
}

2.ChatRoom.java

public class ChatRoom {
    public static void sendMessage(User user, String message){
        System.out.println("["+user.getName()+"]: " + message);
    }
}

3.MediatorPatternDemo.java

public class MediatorPatternDemo {
    public static void main(String[] args) {
        User john = new User("John");
        User steven = new User("Steven");

        john.sendMessage("Hi Steven!");
        steven.sendMessage("Hello John!");
    }
}

4.6 迭代器模式

概述:該模式用於順序訪問集合對象的元素,不需要知道集合對象的底層表示。
如何創建:定義迭代器接口,其主要方法有hasNext、next
何時使用:遍歷一個集合
優點

  1. 它支持以不同的方式遍歷一個聚合對象;
  2. 迭代器簡化了聚合類;
  3. 在同一個聚合上可以有多個遍歷;
  4. 在迭代器模式中,增加新的聚合類和迭代器類都很方便,無需修改原有代碼;

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

  1. 訪問一個聚合對象的內容無需暴露它的內部表示;
  2. 需要爲聚合對象提供多種遍歷方式;
  3. 爲遍歷不同的聚合結構提供一個統一的接口;

示例
代碼清單:

  1. Iterator.java ----- interface
  2. Container.java ----- interface
  3. NameRepository.java
  4. IteratorPatternDemo.java

1.Iterator.java

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

2.Container.java

public interface Container {
    Iterator getIterator();
}

3.NameRepository.java

public class NameRepository implements Container {
    String[] names = {"zhangsanfeng","songyuanqiao","yulianzhou","yudaiyan","zhangsongxi","zhangcuishan","yinliting","moshenggu"};
    @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() {
            return names[index++];
        }
    }
}

4.IteratorPatternDemo.java

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

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

4.7 解釋器模式

概述:該模式提供了評估語言的語法或表達式的方式,這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在SQL解析、符號處理引擎等。
如何創建:構建環境類,包含解釋器之外的一些全局信息,一般是HashMap
何時使用:如果一種特定類型的問題發生的頻率足夠高,那麼可能就值得將該問題的各個實例表述爲一個簡單語言中的句子。這樣就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題。
優點

  1. 可擴展性比較好,靈活
  2. 增加了新的解釋表達式的方式
  3. 易於實現簡單文法

缺點

  1. 可利用場景比較少
  2. 對於複雜的文法比較難維護
  3. 解釋器模式會引起類膨脹
  4. 解釋器模式採用遞歸調用方法

使用場景

  1. 可以將一個需要解釋執行的語言中的句子表示爲一個抽象語法樹。
  2. 一些重複出現的問題可以用一種簡單的語言來進行表達。
  3. 一個簡單語法需要解釋的場景。

示例
代碼清單:

  1. Expression.java -----interface
  2. TerminalExpression.java
  3. OrExpression.java
  4. AndExpression.java
  5. InterpreterPatternDemo.java

1.Expression.java

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

2.TerminalExpression.java

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

3.OrExpression.java

public class OrExpression implements Expression{
    private Expression expr1;
    private Expression expr2;

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

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

4.AndExpression.java

public class AndExpression implements Expression {
    private Expression expr1;
    private Expression expr2;

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

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

5.InterpreterPatternDemo.java

public class InterpreterPatternDemo {

    public static Expression getMaleExpression(){
        Expression zhangSanfeng = new TerminalExpression("ZhangSanfeng");
        Expression songYuanqiao = new TerminalExpression("SongYuanqiao");
        return new OrExpression(zhangSanfeng, songYuanqiao);
    }

    public static Expression getMarriedWomen(){
        Expression yinSusu = new TerminalExpression("YinSusu");
        Expression married = new TerminalExpression("Married");
        return new AndExpression(yinSusu, married);
    }

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

        System.out.println("ZhangSanfeng is male?" + isMale.interpret("ZhangSanfeng"));
        System.out.println("YinSusu is married women?" + isMarriedWomen.interpret("Married YinSusu"));

    }
}

4.8 責任鏈模式

概述:責任鏈模式(Chain of Responsibility Pattern)爲請求創建了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬於行爲型模式。在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。
如何創建:Handler 裏面聚合它自己,在 HandlerRequest 裏判斷是否合適,如果沒達到條件則向下傳遞,向誰傳遞之前 set 進去。
何時使用:在處理消息的時候以過濾很多道。
優點

  1. 降低耦合度。它將請求的發送者和接收者解耦。
  2. 簡化了對象。使得對象不需要知道鏈的結構。
  3. 增強給對象指派職責的靈活性。通過改變鏈內的成員或者調動它們的次序,允許動態地新增或者刪除責任。
  4. 增加新的請求處理類很方便。

缺點

  1. 不能保證請求一定被接收。
  2. 系統性能將受到一定影響,而且在進行代碼調試時不太方便,可能會造成循環調用。
  3. 可能不容易觀察運行時的特徵,有礙於除錯。

使用場景

  1. 有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定。
  2. 在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。
  3. 可動態指定一組對象處理請求。

示例
代碼清單:

  1. AbstractLogger.java
  2. DebugLogger.java
  3. ErrorLogger.java
  4. InfoLogger.java
  5. ChainPatternDemo.java
    1.AbstractLogger.java
public abstract class AbstractLogger {
    static int INFO = 1;
    static int DEBUG = 2;
    static int ERROR = 3;

    protected int level;

    AbstractLogger nextLogger;

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

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

        if(nextLogger != null){
            nextLogger.logMessage(level, message);
        }
    }

    public abstract void write(String message);
}

2.DebugLogger.java

public class DebugLogger extends AbstractLogger {
    DebugLogger(int level){
        this.level = level;
    }
    @Override
    public void write(String message) {
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())
                + " DEBUG ---"+Thread.currentThread().getName()
                + DebugLogger.class
                + " : "
                + message);
    }
}

3.ErrorLogger.java

public class ErrorLogger extends AbstractLogger {
    ErrorLogger(int level){
        this.level = level;
    }
    @Override
    public void write(String message) {
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())
                + " ERROR ---"+Thread.currentThread().getContextClassLoader()
                + ErrorLogger.class
                + " : "
                + message);
    }
}

4.InfoLogger.java

public class InfoLogger extends AbstractLogger {

    InfoLogger(int level){
        this.level = level;
    }

    @Override
    public void write(String message) {
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())
                + " INFO ---"+Thread.currentThread().getContextClassLoader()
                + InfoLogger.class
                + " : "
                + message);
    }
}

5.ChainPatternDemo.java

public class ChainPatternDemo {

    public static AbstractLogger getLogger(){
        AbstractLogger infoLogger = new InfoLogger(AbstractLogger.INFO);
        AbstractLogger debugLogger = new DebugLogger(AbstractLogger.DEBUG);
        AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);

        errorLogger.setNextLogger(debugLogger);
        debugLogger.setNextLogger(infoLogger);
        return errorLogger;
    }

    public static void main(String[] args) {

        AbstractLogger logger = getLogger();

        logger.logMessage(AbstractLogger.INFO, "This is an information.");
        logger.logMessage(AbstractLogger.DEBUG, "This is a debug level information.");
        logger.logMessage(AbstractLogger.ERROR, "This is an error information.");

    }
}

4.9 命令模式

概述:命令模式是一種數據驅動的設計模式,它屬於行爲型模式。請求以命令的形式包裹在對象中,並傳給調用對象。調用對象尋找可以處理該命令的合適的對象,並把該命令傳給相應的對象,該對象執行命令。
如何創建:定義三個角色:1、received 真正的命令執行對象 2、Command 3、invoker 使用命令對象的入口
何時使用:在某些場合,比如要對行爲進行"記錄、撤銷/重做、事務"等處理,這種無法抵禦變化的緊耦合是不合適的。在這種情況下,如何將"行爲請求者"與"行爲實現者"解耦?將一組行爲抽象爲對象,可以實現二者之間的鬆耦合。
優點

  1. 降低了系統耦合度。
  2. 新的命令可以很容易添加到系統中去。

缺點:使用命令模式可能會導致某些系統有過多的具體命令類。
使用場景:認爲是命令的地方都可以使用命令模式,比如: 1、GUI 中每一個按鈕都是一條命令。 2、模擬 CMD。
示例
代碼清單:

  1. Stock.java
  2. Order.java -----interface
  3. BuyOrder.java
  4. SellOrder.java
  5. Broker.java
  6. CommandPatternDemo.java

1.Stock.java

public class Stock {
    private String name;
    private int quantity;

    public void setName(String name) {
        this.name = name;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

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

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

2.Order.java

public interface Order {
    void execute();
}

3.BuyOrder.java

public class BuyOrder implements Order {
    private Stock abcStock;
    BuyOrder(Stock stock){
        this.abcStock = stock;
    }
    @Override
    public void execute() {
        abcStock.buyStock();
    }
}

4.SellOrder.java

public class SellOrder implements Order {
    private Stock abcStock;

    SellOrder(Stock stock){
        this.abcStock = stock;

    }
    @Override
    public void execute() {
        abcStock.sellStock();
    }
}

5.Broker.java

public class Broker {
    private List<Order> orders;

    public Broker(){
        orders = new ArrayList<>();
    }

    void setOrders(Order order){
        orders.add(order);
    }

    void takePlace(){
        for(Order order : orders){
            order.execute();
        }
        orders.clear();
    }
}

6.CommandPatternDemo.java

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

        abcStock.setName("ABC");
        abcStock.setQuantity(10);

        BuyOrder buyOrder = new BuyOrder(abcStock);
        SellOrder sellOrder = new SellOrder(abcStock);

        Broker broker = new Broker();
        broker.setOrders(buyOrder);
        broker.setOrders(sellOrder);

        broker.takePlace();
    }
}

4.10 備忘錄模式

概述:備忘錄模式保存一個對象的某個狀態,以便在適當的時候恢復對象。備忘錄模式屬於行爲型模式。
如何創建:客戶不與備忘錄類耦合,與備忘錄管理類耦合。
何時使用:很多時候我們總是需要記錄一個對象的內部狀態,這樣做的目的就是爲了允許用戶取消不確定或者錯誤的操作,能夠恢復到他原先的狀態,使得他有"後悔藥"可吃。
優點

  1. 給用戶提供了一種可以恢復狀態的機制,可以使用戶能夠比較方便地回到某個歷史的狀態。
  2. 實現了信息的封裝,使得用戶不需要關心狀態的保存細節。
    缺點:消耗資源。如果類的成員變量過多,勢必會佔用比較大的資源,而且每一次保存都會消耗一定的內存。
    使用場景
  3. 需要保存/恢復數據的相關狀態場景。
  4. 提供一個可回滾的操作。

示例
代碼清單:

  1. Memento.java
  2. Originator.java
  3. TakeCare.java
  4. MementoPatternDemo.java
    1.Memento.java
public class Memento {
    private String state;

    public Memento(String state){
        this.state = state;
    }

    public String getState(){
        return state;
    }
}

2.Originator.java

public class Originator {
    private String state;

    public Memento saveMemento(){
        return new Memento(state);
    }

    public void getStateFromMemento(Memento memento){
        state = memento.getState();
    }

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

    public String getState(){
        return state;
    }
}

3.TakeCare.java

public class TakeCare {
    private List<Memento> mementoList = new ArrayList<>();

    public void add(Memento memento){
        mementoList.add(memento);
    }

    public Memento get(int index){
        return mementoList.get(index);
    }
}

4.MementoPatternDemo.java

public class MementoPatternDemo {
    public static void main(String[] args) {
        Originator originator = new Originator();
        TakeCare takeCare = new TakeCare();
        originator.setState("State #1");
        takeCare.add(originator.saveMemento());
        originator.setState("State #2");
        originator.setState("State #3");
        takeCare.add(originator.saveMemento());
        originator.setState("State #4");


        System.out.println("當前狀態");
        System.out.println(originator.getState());
        System.out.println("第一次保存狀態");
        originator.getStateFromMemento(takeCare.get(0));
        System.out.println(originator.getState());
        System.out.println("第二次保存狀態");
        originator.getStateFromMemento(takeCare.get(1));
        System.out.println(originator.getState());

    }
}

4.11 狀態模式

概述:在狀態模式(State Pattern)中,類的行爲是基於它的狀態改變的。這種類型的設計模式屬於行爲型模式。在狀態模式中,我們創建表示各種狀態的對象和一個行爲隨着狀態對象改變而改變的 context 對象。
如何創建:通常命令模式的接口中只有一個方法。而狀態模式的接口中有一個或者多個方法。而且,狀態模式的實現類的方法,一般返回值,或者是改變實例變量的值。也就是說,狀態模式一般和對象的狀態有關。實現類的方法有不同的功能,覆蓋接口中的方法。狀態模式和命令模式一樣,也可以用於消除 if…else 等條件選擇語句。
何時使用:代碼中包含大量與對象狀態有關的條件語句。
優點

  1. 封裝了轉換規則。

  2. 枚舉可能的狀態,在枚舉狀態之前需要確定狀態種類。

  3. 將所有與某個狀態有關的行爲放到一個類中,並且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行爲。

  4. 允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。

  5. 可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數。
    缺點

  6. 狀態模式的使用必然會增加系統類和對象的個數。

  7. 狀態模式的結構與實現都較爲複雜,如果使用不當將導致程序結構和代碼的混亂。

  8. 狀態模式對"開閉原則"的支持並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態,而且修改某個狀態類的行爲也需修改對應類的源代碼。

使用場景

  1. 行爲隨狀態改變而改變的場景。

  2. 條件、分支語句的代替者。
    示例
    代碼清單:

  3. State.java -----interface

  4. Context.java

  5. StartState.java

  6. StopState.java

  7. StatePatternDemo.java
    1.State.java

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

2.Context.java

public class Context {
    private State state;

    public State getState() {
        return state;
    }

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

3.StartState.java

public class StartState implements State {

    @Override
    public void doAction(Context context) {
        System.out.println("Player is in start state");
        context.setState(this);
    }

    @Override
    public String toString(){
        return "Start State";
    }
}

4.StopState.java

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

    @Override
    public String toString() {
        return "Stop State";
    }
}

5.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());

        StopState stopState = new StopState();
        stopState.doAction(context);
        System.out.println(context.getState());
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章