State 模式

State模式

在State 模式中,我们用类来表示状态。State的意思是“状态”。在现实世界中,我们会考虑各种东西的“状态”,但是几乎不会将状态当作“东西”看待。因此,可能很难理解“用类来表示状态”的意思。
以类来表示状态后,我们就能通过切换类来方便地改变对象的状态。当需要增加新的状态时,如何修改代码这个问题也会很明确。

示例程序

金库报警系统功能简介

pic1

金库报警系统结构图

pic2

类和接口的一览表
名字说明
State表示金库状态的接口
DayState表示“白天”状态的类。它实现了State接口
NightState表示“晚上”状态的类。它实现了State接口
Context表示管理金库状态,并与报警中心联系的接口
SafeFrame实现了Context接口。在它内部持有按钮和画面显示等UI信息
Main测试程序行为的类
示例程序的类图

pic3

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

    void doUse(Context context);                // 使用金库

    void doAlarm(Context context);              // 按下警铃

    void doPhone(Context context);              // 正常通话
}
DayState
public class DayState implements State {
    private static DayState singleton = new DayState();
    private DayState() {                                // 构造函数的可见性是private
    }
    public static State getInstance() {                 // 获取唯一实例
        return singleton;
    }
    @Override
    public void doClock(Context context, int hour) {    // 设置时间
        if (hour < 9 || 17 <= hour) {
            context.changeState(NightState.getInstance());
        }
    }
    @Override
    public void doUse(Context context) {                // 使用金库
        context.recordLog("使用金库(白天)");
    }
    @Override
    public void doAlarm(Context context) {              // 按下警铃
        context.callSecurityCenter("按下警铃(白天)");
    }
    @Override
    public void doPhone(Context context) {              // 正常通话
        context.callSecurityCenter("正常通话(白天)");
    }
    @Override
    public String toString() {                          // 显示表示类的文字
        return "[白天]";
    }
}
NightState
public class NightState implements State {
    private static NightState singleton = new NightState();
    private NightState() {                              // 构造函数的可见性是private
    }
    public static State getInstance() {                 // 获取唯一实例
        return singleton;
    }
    @Override
    public void doClock(Context context, int hour) {    // 设置时间
        if (9 <= hour && hour < 17) {
            context.changeState(DayState.getInstance());
        }
    }
    @Override
    public void doUse(Context context) {                // 使用金库
        context.callSecurityCenter("紧急:晚上使用金库!");
    }
    @Override
    public void doAlarm(Context context) {              // 按下警铃
        context.callSecurityCenter("按下警铃(晚上)");
    }
    @Override
    public void doPhone(Context context) {              // 正常通话
        context.recordLog("晚上的通话录音");
    }
    @Override
    public String toString() {                          // 显示表示类的文字
        return "[晚上]";
    }
}
Context
public interface Context {

    void setClock(int hour);                // 设置时间

    void changeState(State state);          // 改变状态

    void callSecurityCenter(String msg);    // 联系警报中心

    void recordLog(String msg);             // 在警报中心留下记录
}
SafeFrame
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();           // 当前的状态

    // 构造函数
    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);
    }
    // 按钮被按下后该方法会被调用
    @Override
    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("?");
        }
    }
    // 设置时间
    @Override
    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);
    }
    // 改变状态
    @Override
    public void changeState(State state) {
        System.out.println("从" + this.state + "状態变为了" + state + "状态。");
        this.state = state;
    }
    // 联系警报中心
    @Override
    public void callSecurityCenter(String msg) {
        textScreen.append("call! " + msg + "\n");
    }
    // 在警报中心留下记录
    @Override
    public void recordLog(String msg) {
        textScreen.append("record ... " + msg + "\n");
    }
}
Main
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) {
                }
            }
        }
    }
}
示例程序的时序图

pic4

State 模式中的登场角色

1.State(状态)

State角色表示状态,定义了根据不同状态进行不同处理的接口(API)。该接口(API)是那些处理内容依赖于状态的方法的集合。在示例程序中,由State接口扮演此角色。

2.ConcreteState(具体状态)

ConcreteState角色表示各个具体的状态,它实现了State接口。在示例程序中,由DayState和NightState类扮演此角色。

3.Context(状态、前后关系、上下文)

Context角色持有表示当前状态的ConcreteState角色。此外,它还定义了供外部调用者使用State模式的接口(API)。在示例程序中,由Context接口和SafeFrame类扮演此角色。

这里稍微做一下补充说明。在示例程序中,Context角色的作用被Context接口和SafeFrame类分担了。具体而言,Context接口定义了供外部调用者使用State模式的接口(API),而SafeFrame类则持有表示当前状态的ConcreteState角色。

State 模式通用类图

pic5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值