[Design Patterns - Study Notes] 23 Design Patterns - State Mode (Principle Explanation + Application Scenario Introduction + Case Introduction + Java Code Implementation)

Case introduction

Please write a program to complete the APP lottery. The specific requirements are as follows:

  • If 50 points are deducted from the user every time he participates in this event, the probability of winning is 10%
  • The number of prizes is fixed, and you will not be able to draw the prizes once they are drawn.
  • The event has four states: lottery can be drawn, lottery cannot be drawn, prizes are distributed, and prizes have been collected. The four status transition diagram of the event is as follows:

Insert image description here

The initial status is "cannot draw". After 50 points are successfully deducted, the status changes to "can draw".

introduce

basic introduction

  • State mode: It is mainly used to solve the problem of objects needing to output different behaviors when they transition into multiple states. There is a one-to-one correspondence between states and behaviors (if you are in state A, you have the behaviors and operations owned by state A), and states can be converted to each other.
  • Allows the behavior of an object to change when its internal state changes. The object appears to become an object of another class.
  • In the state mode, classes are used to represent states. You can change the state of an object by switching classes. When you need to add a new class, you only need to add a new class.

Characters

Insert image description here

  • Context(上下文): Used to maintain State instances. Depending on the state, the ConcreteState class corresponding to the instance is also different, so the methods of the State object are also different.
  • State(状态): Abstract state role, defining multiple interfaces
  • ConcreteState(具体状态): It is a specific state role, which implements the method of State interface according to its own state.

Application scenarios

  • When an event or object has many states, and the states are converted to each other, and different behaviors are required for different states, you can consider using the state pattern.

Case implementation

Case number one

Class Diagram

Insert image description here

The Activity class contains all state objects, and each state subclass also contains Activity objects.

accomplish

[Abstract state class: State]

package com.atguigu.state;

/**
 * 状态抽象类
 *
 * @author Administrator
 */
public abstract class State {
    
    

    /**
     * 扣除积分 - 50
     */
    public abstract void deductMoney();

    /**
     * 是否抽中奖品
     *
     * @return
     */
    public abstract boolean raffle();

    /**
     * 发放奖品
     */
    public abstract void dispensePrize();

}

[ Cannot draw lottery status ]

package com.atguigu.state;

/**
 * 不能抽奖状态
 * @author Administrator
 *
 */
public class NoRaffleState extends State {
    
    

    /**
     * 初始化时传入活动引用,扣除积分后改变其状态
     */
    RaffleActivity activity;

    public NoRaffleState(RaffleActivity activity) {
    
    
        this.activity = activity;
    }

    /**
     * 当前状态可以扣积分,扣除后,将状态设置成可以抽奖状态
     */
    @Override
    public void deductMoney() {
    
    
        System.out.println("扣除50积分成功,您可以抽奖了");
        activity.setState(activity.getCanRaffleState());
    }

    /**
     * 当前状态不能抽奖
     * @return
     */
    @Override
    public boolean raffle() {
    
    
        System.out.println("扣了积分才能抽奖喔!");
        return false;
    }

    /**
     * 当前状态不能发奖品
     */
    @Override
    public void dispensePrize() {
    
    
        System.out.println("不能发放奖品");
    }
} 

[Status available for lottery]

package com.atguigu.state;

import java.util.Random;

/**
 * 可以抽奖的状态
 *
 * @author Administrator
 */
public class CanRaffleState extends State {
    
    

    RaffleActivity activity;

    public CanRaffleState(RaffleActivity activity) {
    
    
        this.activity = activity;
    }

    /**
     * 已经扣除了积分,不能再扣
     */
    @Override
    public void deductMoney() {
    
    
        System.out.println("已经扣取过了积分");
    }

    /**
     * 可以抽奖, 抽完奖后,根据实际情况,改成新的状态
     *
     * @return
     */
    @Override
    public boolean raffle() {
    
    
        System.out.println("正在抽奖,请稍等!");
        Random r = new Random();
        int num = r.nextInt(10);
        // 10%中奖机会
        if (num == 0) {
    
    
            // 改变活动状态为发放奖品 context
            activity.setState(activity.getDispenseState());
            return true;
        } else {
    
    
            System.out.println("很遗憾没有抽中奖品!");
            // 改变状态为不能抽奖
            activity.setState(activity.getNoRafflleState());
            return false;
        }
    }

    /**
     * 不能发放奖品
     */
    @Override
    public void dispensePrize() {
    
    
        System.out.println("没中奖,不能发放奖品");
    }
}

[Status of prize distribution]

package com.atguigu.state;

/**
 * 发放奖品的状态
 *
 * @author Administrator
 */
public class DispenseState extends State {
    
    

    /**
     * 初始化时传入活动引用,发放奖品后改变其状态
     */
    RaffleActivity activity;

    public DispenseState(RaffleActivity activity) {
    
    
        this.activity = activity;
    }


    @Override
    public void deductMoney() {
    
    
        System.out.println("不能扣除积分");
    }

    @Override
    public boolean raffle() {
    
    
        System.out.println("不能抽奖");
        return false;
    }

    //发放奖品
    @Override
    public void dispensePrize() {
    
    
        if (activity.getCount() > 0) {
    
    
            System.out.println("恭喜中奖了");
            // 改变状态为不能抽奖
            activity.setState(activity.getNoRafflleState());
        } else {
    
    
            System.out.println("很遗憾,奖品发送完了");
            // 改变状态为奖品发送完毕, 后面我们就不可以抽奖
            activity.setState(activity.getDispensOutState());
            //System.out.println("抽奖活动结束");
            //System.exit(0);
        }

    }
}

[ Prize distribution completed status ]

package com.atguigu.state;

/**
 * 奖品发放完毕状态
 * 说明,当我们activity 改变成 DispenseOutState, 抽奖活动结束
 *
 * @author Administrator
 */
public class DispenseOutState extends State {
    
    

    /**
     * 初始化时传入活动引用
     */
    RaffleActivity activity;

    public DispenseOutState(RaffleActivity activity) {
    
    
        this.activity = activity;
    }

    @Override
    public void deductMoney() {
    
    
        System.out.println("奖品发送完了,请下次再参加");
    }

    @Override
    public boolean raffle() {
    
    
        System.out.println("奖品发送完了,请下次再参加");
        return false;
    }

    @Override
    public void dispensePrize() {
    
    
        System.out.println("奖品发送完了,请下次再参加");
    }
}

【run】

--------第1次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第2次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第3次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第4次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第5次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第6次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第7次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
恭喜中奖了
--------第8次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第9次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾,奖品发送完了
--------第10次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第11次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第12次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第13次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第14次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第15次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第16次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第17次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第18次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第19次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第20次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第21次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第22次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第23次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第24次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第25次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第26次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第27次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第28次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第29次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第30次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加

Process finished with exit code 0

Case 2: Analysis of source code of lending platform

Implementing analysis the traditional way

Determine the status of the order through if/else to implement different logic

【analyze】

This type of code is difficult to cope with changes. When adding a state, we need to manually add if/else. When adding a function, we need to judge all states. Therefore, the code will become more and more bloated, and once a certain state is not processed, extremely serious bugs will occur, making it difficult to maintain.

【Improve】

The order on the lending platform has 审核-发布-抢单several steps. With different operations, the status of the order will change. The implementation of this module in the project will use the status mode.

Status modification process

Insert image description here

Class Diagram

Insert image description here

accomplish

[State enumeration class: StateEnum]

package com.atguigu.state.money;

/**
 * 状态枚举类
 * @author Administrator
 *
 */
public enum StateEnum {
    
    

    //订单生成
    GENERATE(1, "GENERATE"),

    //已审核
    REVIEWED(2, "REVIEWED"),

    //已发布
    PUBLISHED(3, "PUBLISHED"),

    //待付款
    NOT_PAY(4, "NOT_PAY"),

    //已付款
    PAID(5, "PAID"),

    //已完结
    FEED_BACKED(6, "FEED_BACKED");

    private int key;
    private String value;

    StateEnum(int key, String value) {
    
    
        this.key = key;
        this.value = value;
    }
    public int getKey() {
    
    return key;}
    public String getValue() {
    
    return value;}
}

[State interface: State]

package com.atguigu.state.money;

/**
 * 状态接口
 * @author Administrator
 *
 */
public interface State {
    
    

    /**
     * 电审
     */
    void checkEvent(Context context);

    /**
     * 电审失败
     */
    void checkFailEvent(Context context);

    /**
     * 定价发布
     */
    void makePriceEvent(Context context);

    /**
     * 接单
     */
    void acceptOrderEvent(Context context);

    /**
     * 无人接单失效
     */
    void notPeopleAcceptEvent(Context context);

    /**
     * 付款
     */
    void payOrderEvent(Context context);

    /**
     * 接单有人支付失效
     */
    void orderFailureEvent(Context context);

    /**
     * 反馈
     */
    void feedBackEvent(Context context);


    String getCurrentState();
}

[Abstract state class]

After using abstract state classes to implement methods by default, concrete state classes (its subclasses) can only select the required methods to override.

package com.atguigu.state.money;

/**
 * 抽象类,默认实现了 State 接口的所有方法
 * 该类的所有方法,其子类(具体的状态类),可以有选择的进行重写
 */
public abstract class AbstractState implements State {
    
    

    protected static final RuntimeException EXCEPTION = new RuntimeException("操作流程不允许");

    @Override
    public void checkEvent(Context context) {
    
    
        throw EXCEPTION;
    }

    @Override
    public void checkFailEvent(Context context) {
    
    
        throw EXCEPTION;
    }

    @Override
    public void makePriceEvent(Context context) {
    
    
        throw EXCEPTION;
    }

    @Override
    public void acceptOrderEvent(Context context) {
    
    
        throw EXCEPTION;
    }

    @Override
    public void notPeopleAcceptEvent(Context context) {
    
    
        throw EXCEPTION;
    }

    @Override
    public void payOrderEvent(Context context) {
    
    
        throw EXCEPTION;
    }

    @Override
    public void orderFailureEvent(Context context) {
    
    
        throw EXCEPTION;
    }

    @Override
    public void feedBackEvent(Context context) {
    
    
        throw EXCEPTION;
    }
}

[All specific status classes are in this file]

package com.atguigu.state.money;

/**
 * 反馈状态
 */
class FeedBackState extends AbstractState {
    
    

    @Override
    public String getCurrentState() {
    
    
        return StateEnum.FEED_BACKED.getValue();
    }
}

/**
 * 通用状态
 */
class GenerateState extends AbstractState {
    
    

    @Override
    public void checkEvent(Context context) {
    
    
        context.setState(new ReviewState());
    }

    @Override
    public void checkFailEvent(Context context) {
    
    
        context.setState(new FeedBackState());
    }

    @Override
    public String getCurrentState() {
    
    
        return StateEnum.GENERATE.getValue();
    }
}

/**
 * 未支付状态
 */
class NotPayState extends AbstractState {
    
    

    @Override
    public void payOrderEvent(Context context) {
    
    
        context.setState(new PaidState());
    }

    @Override
    public void feedBackEvent(Context context) {
    
    
        context.setState(new FeedBackState());
    }

    @Override
    public String getCurrentState() {
    
    
        return StateEnum.NOT_PAY.getValue();
    }
}

/**
 * 已支付状态
 */
class PaidState extends AbstractState {
    
    

    @Override
    public void feedBackEvent(Context context) {
    
    
        context.setState(new FeedBackState());
    }

    @Override
    public String getCurrentState() {
    
    
        return StateEnum.PAID.getValue();
    }
}

/**
 * 发布状态
 */
class PublishState extends AbstractState {
    
    

    @Override
    public void acceptOrderEvent(Context context) {
    
    
        //接受订单成功,把当前状态设置为NotPayState
        //至于实际上应该变成哪个状态,由流程图来决定
        context.setState(new NotPayState());
    }

    @Override
    public void notPeopleAcceptEvent(Context context) {
    
    
        context.setState(new FeedBackState());
    }

    @Override
    public String getCurrentState() {
    
    
        return StateEnum.PUBLISHED.getValue();
    }
}

/**
 * 回顾状态
 */
class ReviewState extends AbstractState {
    
    

    @Override
    public void makePriceEvent(Context context) {
    
    
        context.setState(new PublishState());
    }

    @Override
    public String getCurrentState() {
    
    
        return StateEnum.REVIEWED.getValue();
    }

}

[Environmental context]

package com.atguigu.state.money;

/**
 * 环境上下文
 */
public class Context extends AbstractState{
    
    
    /**
     * 当前的状态 state, 根据我们的业务流程处理,不停的变化
     */
    private State state;

    @Override
    public void checkEvent(Context context) {
    
    
        state.checkEvent(this);
        getCurrentState();
    }

    @Override
    public void checkFailEvent(Context context) {
    
    
        state.checkFailEvent(this);
        getCurrentState();
    }

    @Override
    public void makePriceEvent(Context context) {
    
    
        state.makePriceEvent(this);
        getCurrentState();
    }

    @Override
    public void acceptOrderEvent(Context context) {
    
    
        state.acceptOrderEvent(this);
        getCurrentState();
    }

    @Override
    public void notPeopleAcceptEvent(Context context) {
    
    
        state.notPeopleAcceptEvent(this);
        getCurrentState();
    }

    @Override
    public void payOrderEvent(Context context) {
    
    
        state.payOrderEvent(this);
        getCurrentState();
    }

    @Override
    public void orderFailureEvent(Context context) {
    
    
        state.orderFailureEvent(this);
        getCurrentState();
    }

    @Override
    public void feedBackEvent(Context context) {
    
    
        state.feedBackEvent(this);
        getCurrentState();
    }

    public State getState() {
    
    
        return state;
    }

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

    @Override
    public String getCurrentState() {
    
    
        System.out.println("当前状态 : " + state.getCurrentState());
        return state.getCurrentState();
    }
}

【Main category】

package com.atguigu.state.money;

/**
 * 测试类
 */
public class ClientTest {
    
    

    public static void main(String[] args) {
    
    
        //创建context 对象
        Context context = new Context();
        //将当前状态设置为 PublishState
        context.setState(new PublishState());
        System.out.println(context.getCurrentState());

//        //publish --> not pay
        context.acceptOrderEvent(context);
//        //not pay --> paid
        context.payOrderEvent(context);
//        // 失败, 检测失败时,会抛出异常
//        try {
    
    
//         context.checkFailEvent(context);
//         System.out.println("流程正常..");
//    } catch (Exception e) {
    
    
//       System.out.println(e.getMessage());
//    }

    }

}

【run】

当前状态 : PUBLISHED
PUBLISHED
当前状态 : NOT_PAY
当前状态 : PAID

Process finished with exit code 0

Case Three: Vault Alarm System

System operating logic

Insert image description here

pseudocode

Traditional implementation

Insert image description here

Use state mode

Insert image description here

Class Diagram

Insert image description here

accomplish

【Status interface】

package com.atguigu.state.Sample;

public interface State {
    
    
    /**
     * 设置时间
     *
     * @param context
     * @param hour
     */
    public abstract void doClock(Context context, int hour);

    /**
     * 使用金库
     *
     * @param context
     */
    public abstract void doUse(Context context);

    /**
     * 按下警铃
     *
     * @param context
     */
    public abstract void doAlarm(Context context);

    /**
     * 正常通话
     *
     * @param context
     */
    public abstract void doPhone(Context context);
}

[Daytime status]

package com.atguigu.state.Sample;
/**
 * 表示白天的状态
 */
public class DayState implements State {
    
    
    /**
     * 每个状态都是一个类,如果每次改变状态都需要生成一个新的实例的话,比较浪费内存和时间,所以在这里使用单例模式
     */
    private static DayState singleton = new DayState();

    /**
     * 构造函数的可见性是private
     */
    private DayState() {
    
    
    }

    /**
     * 获取唯一实例
     * @return
     */
    public static State getInstance() {
    
    
        return singleton;
    }

    /**
     * 设置时间
     * @param context
     * @param hour
     */
    public void doClock(Context context, int hour) {
    
    
        if (hour < 9 || 17 <= hour) {
    
    
            // 如果时间是晚上,切换到夜间状态
            context.changeState(NightState.getInstance());
        }
    }

    /**
     * 使用金库
     * @param context
     */
    public void doUse(Context context) {
    
    
        context.recordLog("使用金库(白天)");
    }

    /**
     * 按下警铃
     * @param context
     */
    public void doAlarm(Context context) {
    
    
        context.callSecurityCenter("按下警铃(白天)");
    }

    /**
     * 正常通话
     * @param context
     */
    public void doPhone(Context context) {
    
    
        context.callSecurityCenter("正常通话(白天)");
    }

    /**
     * 显示表示类的文字
     * @return
     */
    public String toString() {
    
    
        return "[白天]";
    }
}

【Night state】

package com.atguigu.state.Sample;

public class NightState implements State {
    
    
    private static NightState singleton = new NightState();

    /**
     * 构造函数的可见性是private
     */
    private NightState() {
    
    
    }

    /**
     * 获取唯一实例
     * @return
     */
    public static State getInstance() {
    
                     
        return singleton;
    }

    /**
     * 设置时间
     * @param context
     * @param hour
     */
    public void doClock(Context context, int hour) {
    
        
        if (9 <= hour && hour < 17) {
    
    
            context.changeState(DayState.getInstance());
        }
    }

    /**
     * 使用金库
     * @param context
     */
    public void doUse(Context context) {
    
                    
        context.callSecurityCenter("紧急:晚上使用金库!");
    }

    /**
     * 按下警铃
     * @param context
     */
    public void doAlarm(Context context) {
    
                  
        context.callSecurityCenter("按下警铃(晚上)");
    }

    /**
     * 正常通话
     * @param context
     */
    public void doPhone(Context context) {
    
                  
        context.recordLog("晚上的通话录音");
    }

    /**
     * 显示表示类的文字
     * @return
     */
    public String toString() {
    
                              
        return "[晚上]";
    }
}

【Context interface】

  • Responsible for managing status and contacting alert center
package com.atguigu.state.Sample;

public interface Context {
    
    

    /**
     * 设置时间
     *
     * @param hour
     */
    public abstract void setClock(int hour);

    /**
     * 改变状态
     *
     * @param state
     */
    public abstract void changeState(State state);

    /**
     * 联系警报中心
     *
     * @param msg
     */
    public abstract void callSecurityCenter(String msg);

    /**
     * 在警报中心留下记录
     *
     * @param msg
     */
    public abstract void recordLog(String msg);
}

[Context role: SafeFrame]

In this example program, the role of Context role is shared by the Context interface and SafeFrame class. The Context interface defines an interface for external callers to use the state mode, and the SafeFrame class holds the ConcreteState role that represents the current state.

package com.atguigu.state.Sample;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class SafeFrame extends Frame implements ActionListener, Context {
    
    
    /**
     * 显示当前时间
     */
    private TextField textClock = new TextField(60);
    /**
     * 显示警报中心的记录
     */
    private TextArea textScreen = new TextArea(10, 60);
    /**
     * 金库使用按钮
     */
    private Button buttonUse = new Button("使用金库");
    /**
     * 按下警铃按钮
     */
    private Button buttonAlarm = new Button("按下警铃");
    /**
     * 正常通话按钮
     */
    private Button buttonPhone = new Button("正常通话");
    /**
     * 结束按钮
     */
    private Button buttonExit = new Button("结束");

    /**
     * 金库当前的状态
     */
    private State state = DayState.getInstance();

    /**
     * 构造函数
     *
     * @param title
     */
    public SafeFrame(String title) {
    
    
        super(title);
        setBackground(Color.lightGray);
        setLayout(new BorderLayout());
        //  配置textClock
        add(textClock, BorderLayout.NORTH);
        textClock.setEditable(false);
        // 配置textScreen
        add(textScreen, BorderLayout.CENTER);
        textScreen.setEditable(false);
        // 为界面添加按钮
        Panel panel = new Panel();
        panel.add(buttonUse);
        panel.add(buttonAlarm);
        panel.add(buttonPhone);
        panel.add(buttonExit);
        // 配置界面
        add(panel, BorderLayout.SOUTH);
        // 显示
        pack();
        show();
        // 设置监听器
        buttonUse.addActionListener(this);
        buttonAlarm.addActionListener(this);
        buttonPhone.addActionListener(this);
        buttonExit.addActionListener(this);
    }

    /**
     * 按钮被按下后,该方法会被调用
     *
     * @param e
     */
    public void actionPerformed(ActionEvent e) {
    
    
        System.out.println(e.toString());
        if (e.getSource() == buttonUse) {
    
    
            // 金库使用按钮,并不需要去判断状态,直接调用即可
            state.doUse(this);
        } else if (e.getSource() == buttonAlarm) {
    
    
            // 按下警铃按钮
            state.doAlarm(this);
        } else if (e.getSource() == buttonPhone) {
    
    
            // 正常通话按钮
            state.doPhone(this);
        } else if (e.getSource() == buttonExit) {
    
    
            // 结束按钮
            System.exit(0);
        } else {
    
    
            System.out.println("?");
        }
    }

    /**
     * 设置时间
     *
     * @param hour
     */
    public void setClock(int hour) {
    
    
        String clockstring = "现在时间是";
        if (hour < 10) {
    
    
            clockstring += "0" + hour + ":00";
        } else {
    
    
            clockstring += hour + ":00";
        }
        System.out.println(clockstring);
        // 将当前时间显示在界面的上方
        textClock.setText(clockstring);
        // 进行当前状态下的处理
        state.doClock(this, hour);
    }

    /**
     * 改变状态
     *
     * @param state
     */
    public void changeState(State state) {
    
    
        System.out.println("从" + this.state + "状態变为了" + state + "状态。");
        this.state = state;
    }

    /**
     * 联系警报中心
     *
     * @param msg
     */
    public void callSecurityCenter(String msg) {
    
    
        textScreen.append("call! " + msg + "\n");
    }

    /**
     * 在警报中心留下记录
     *
     * @param msg
     */
    public void recordLog(String msg) {
    
    
        textScreen.append("record ... " + msg + "\n");
    }
}

【run】

package com.atguigu.state.Sample;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        SafeFrame frame = new SafeFrame("State Sample");
        while (true) {
    
    
            for (int hour = 0; hour < 24; hour++) {
    
    
                // 设置时间
                frame.setClock(hour);
                try {
    
    
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
    
    
                }
            }
        }
    }
}

【run】

Insert image description here

现在时间是00:00
从[白天]状態变为了[晚上]状态。
现在时间是01:00
现在时间是02:00
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=使用金库,when=1691920919394,modifiers=] on button0
现在时间是03:00
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=按下警铃,when=1691920920040,modifiers=] on button1
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=正常通话,when=1691920920824,modifiers=] on button2
现在时间是04:00
现在时间是05:00
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=按下警铃,when=1691920922071,modifiers=] on button1
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=使用金库,when=1691920922626,modifiers=] on button0
现在时间是06:00
现在时间是07:00
现在时间是08:00
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=使用金库,when=1691920925446,modifiers=] on button0
现在时间是09:00
从[晚上]状態变为了[白天]状态。
现在时间是10:00
现在时间是11:00
现在时间是12:00
现在时间是13:00
现在时间是14:00
现在时间是15:00
Disconnected from the target VM, address: '127.0.0.1:1966', transport: 'socket'

Process finished with exit code 130

analyze

In the above implementation, the specific state class actually calls the method to switch to another state, such as the doClock method of the DayState class. This method has both advantages and disadvantages:

  • Advantages: When we want to know "when the DayState class changes to another state", we only need to look at the DayState class.
  • Disadvantages: Each ConcreteState role needs to know the methods of other ConcreteState roles. The dependencies between various classes are strong. If one ConcreteState class is deleted, other ConcreteState classes need to be modified.

In addition to this implementation method, all state migration can also be handed over to the class that plays the Context role. This can improve the independence of the ConcreteState role and the overall structure of the program will be clearer. Of course, doing so requires the Context role Know all ConcreteState roles and can be improved using the mediator pattern

question

Question one

Question: Define Context as an abstract class instead of an interface, and then let the Context class hold the state field, which is more in line with the design idea of ​​the state pattern. But in the sample program, we did not do this. Instead, we defined the Context role as the Context interface and let the SafeFrame class hold the state field. Why is this?

Answer: There is only single inheritance in Java, so if the Context role is defined as a class, then since the SafeFrame class is already a subclass of the Frame class, it will no longer inherit the Context class. However, if you write another subclass of the Context class and save its instance in the field of the SafeFrame class, you can achieve the above problems by delegating the processing to this instance.

Question 2

Please add a new "Emergency" state to the sample program. No matter what time it is, as long as there is an "emergency", notify the alarm center of the emergency

  • After pressing the alarm bell, the system status changes to "Emergency" status
  • If the vault is used in an "emergency" situation, the alarm center will be notified of the emergency (regardless of the time at that time)
  • If the alarm bell is pressed during an "emergency" situation, the alarm center will be notified of the emergency (regardless of the time at that time)
  • If the phone is used in an "emergency" situation, a call will be made to the alarm center's message line (regardless of the time of day).

[Add an emergency state class]

package com.atguigu.state.A4;

public class UrgentState implements State {
    
    
    private static UrgentState singleton = new UrgentState();

    private UrgentState() {
    
    
    }

    public static State getInstance() {
    
    
        return singleton;
    }

    public void doClock(Context context, int hour) {
    
    
        // 设置时间
        // 在设置时间处理中什么都不做                                 
    }

    public void doUse(Context context) {
    
    
        // 使用金库
        context.callSecurityCenter("紧急:紧急时使用金库!");
    }

    public void doAlarm(Context context) {
    
    
        // 按下警铃
        context.callSecurityCenter("按下警铃(紧急时)");
    }

    public void doPhone(Context context) {
    
    
        // 正常通话
        context.callSecurityCenter("正常通话(紧急时)");
    }

    public String toString() {
    
    
        // 显示字符串
        return "[紧急时]";
    }
}

[Modify the state migration method of other states]

package com.atguigu.state.A4;

public class DayState implements State {
    
    
    private static DayState singleton = new DayState();
    private DayState() {
    
                              
    }
    public static State getInstance() {
    
                    
        return singleton;
    }
    public void doClock(Context context, int hour) {
    
        
        if (hour < 9 || 17 <= hour) {
    
    
            context.changeState(NightState.getInstance());
        }
    }
    public void doUse(Context context) {
    
                 
        context.recordLog("使用金库(白天)");
    }
    public void doAlarm(Context context) {
    
                  
        context.callSecurityCenter("按下警铃(白天)");
        // 只需要看这里就行,一旦按下紧铃,就会进入到紧急状态
        context.changeState(UrgentState.getInstance()); 
    }
    public void doPhone(Context context) {
    
                 
        context.callSecurityCenter("正常通话(白天)");
    }
    public String toString() {
    
                           
        return "[白天]";
    }
}

The night state also needs to modify the corresponding state migration method, which is similar to the day state and will not be shown here.

Summarize

【advantage】

  • The code is highly readable. The state pattern encapsulates the behavior of each state into a corresponding class for easy maintenance.
  • The if-else statements that are prone to problems are deleted. If the behavior of each state is put into a class, and each time a method is called, it is necessary to determine the current state. Not only will a lot of if-else statements be produced, but also Error-prone
  • Complying with the "opening and closing principle", it is easy to add and delete states. You only need to add or delete a ConcreteState class, and then modify the class responsible for state migration. If you use the traditional method and add a new state, you need to add a lot of judgment statements.
  • Use the idea of ​​"divide and conquer" to separate multiple states. Each class only needs to write code based on the current state. There is no need to write complex conditional branch statements before executing events.
  • If you need to add a state-dependent processing method, you only need to add a new method to the State interface and let all ConcreteState classes implement this method. Although the modification is large, developers will definitely not forget to implement this method because If not implemented, compilation will report an error.

【shortcoming】

  • Many categories will be generated. Each state requires a corresponding class. When there are too many states, many classes will be generated, making maintenance more difficult.

Article description

  • This article is my study notes for studying Shang Silicon Valley. Most of the content in the article comes from Shang Silicon Valley videos ( click to learn Shang Silicon Valley related courses ), and some of the content comes from my own thinking. I publish this article to help other people who study more conveniently. Organize your own notes or learn relevant knowledge directly through articles. If there is any infringement, please contact us to delete it. Finally, I would like to express my gratitude to Shang Silicon Valley for its high-quality courses.
  • I also simultaneously read the book "Graphic Design Pattern" (Graphic Design Pattern/(Japanese) Hiroshi Yuki; translated by Yang Wenxuan - Beijing: People's Posts and Telecommunications Press, 2017.1), and then integrated the contents of the two to make the knowledge points more comprehensive.

Guess you like

Origin blog.csdn.net/laodanqiu/article/details/132270910