State模式(状态设计模式)

Posted dgwblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了State模式(状态设计模式)相关的知识,希望对你有一定的参考价值。

State???

State模式中,我们用类来表示状态。以类来表示状态后,我们就能通过切换类来方便地改变对象的状态。当需要增加新的状态时,如何修改代码这个问题也会很明确。

  • 直接用状态代替硬编码
    依赖于状态的处理,来执行具体的操作

理清职责

技术分享图片

  • 实现功能:
·有一个金库
·金库与警报中心相连
·金库里有警铃和正常通话用的电话·金库里有时钟,监视着现在的时间
·白天的时间范围是9:00~16:59,晚上的时间范围是17:00~23:59和0:00~8:59
·金库只能在白天使用
·白天使用金库的话,会在警报中心留下记录
·晚上使用金库的话,会向警报中心发送紧急事态通知
·任何时候都可以使用警铃
·使用警铃的话,会向警报中心发送紧急事态通知
·任何时候都可以使用电话(但晚上只有留言电话)
·白天使用电话的话,会呼叫警报中心
·晚上用电话的话,会呼叫警报中心的留言电话

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

  • 使用与不使用状态模式对比
  1. 不使用
使用金库时被调用的方法(){
if(白天){
向警报中心报告使用记录
]elseif(晚上){
向警报中心报告紧急事态
警铃响起时被调用的方法(){
向警报中心报告紧急事态
正常通话时被调用的方法(){
if(白天){
呼叫警报中心
}elseif(晚上){
呼叫警报中心的留言电话
}

  1. 使用
表示百天的状态的类{
使用金库时被调用的方法(){
向警报中心报告使用记录
警铃响起时被调用的方法(){
向警报中心报告紧急事态
正常通话时被调用的方法(){
呼叫警报中心
表示晚上的状态的类{
使用金库时被调用的方法(){
向警报中心报告紧急事态
警铃响起时被调用的方法(){
向警报中心报告紧急事态
正常通话时被调用的方法(){
呼叫警报中心的留言电话

- 相关设计模式

◆Singleton模式(第5章)Singleton 模式常常会出现在ConcreteState角色中。在示例程序中,我们就使用了Singleton模式。这是因为在表示状态的类中并没有定义任何实例字段(即表示实例的状态的字段)。
◆Flyweight模式(第20章)在表示状态的类中并没有定义任何实例字段。因此,有时我们可以使用Flyweight模式在多个Context 角色之间共享ConcreteState角色。

UML

技术分享图片

时序图:

技术分享图片

Code

  • DayState NightState State

public interface State {

    //设置时间
    void doclock(Context context, int hour);

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

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

    // 正常通话
    void dophone(Context context);

}

public class NightState implements State {

    private NightState() {
    }

    private static NightState singleton = new NightState();

    public static State getInstance() {
        return (State) singleton;
    }

    @Override
    public void doclock(Context context, int hour) {
        if (hour >= 9 && hour < 17) {
            context.changeState(DayState.getInstance());
        }
    }

    @Override
    public void doUse(Context context) {
        context.recordLog("使用金库[晚上]");
    }

    @Override
    public void doAlarm(Context context) {
        context.callSecurityCenter("按下警铃[晚上]");
    }

    @Override
    public void dophone(Context context) {
        context.recordLog("正常通话[晚上]");
    }

    @Override
    public String toString() {
        return "DayState{晚上}";
    }

}

public class DayState implements State {

    /**
     * 这里使用单例模式,因为每次改变一次状态都会生成一次实例,非常浪费内存与时间
     */
    private DayState() {
    }

    private static DayState singleton = new DayState();


    public static State getInstance() {
        return singleton;
    }

    @Override
    public void doclock(Context context, int hour) {
        if (hour < 9 || hour >= 17) {
            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.recordLog("正常通话[白天]");
    }

    @Override
    public String toString() {
        return "DayState{白天}";
    }
}


  • Context 、SateFrame 、MainT

···

public class MainT {

public static void main(String[] args) {
    SateFrame frame = new SateFrame("Safe Smaple");

    // 24个小时制
    while (true){
        for (int i = 0; i < 24; i++) {
            frame.setClock(i);
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }

        }
    }
}

}

public interface Context {
//设置时间
void setClock(int hour);

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

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

// 在警报中心留下记录
void recordLog(String msg);

}

public class SateFrame 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 SateFrame(String title) throws HeadlessException {
    super(title);
    setBackground(Color.lightGray);
    setLayout(new BorderLayout());

    add(textClock,BorderLayout.NORTH);
    textClock.setEditable(false);

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

/**
 * 可以看出这里的操作就简化很多了:
 * 基本只有业务逻辑代码:
 * 判断状态相关的代码可以直接由相关的状态代码实现,
 * 即为由类的状态代替了if else代码
 */

@Override
public void actionPerformed(ActionEvent e) {
    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("调用---"+msg+"
");
}

@Override
public void recordLog(String msg) {
    textScreen.append("记录---"+msg+"
");
}

}

···

以上是关于State模式(状态设计模式)的主要内容,如果未能解决你的问题,请参考以下文章

状态模式(State)

设计模式之- 状态模式(State Pattern)

23种设计模式之状态模式(State)

尝试使用片段保存夜间模式状态

状态模式---State

程序设计模式 —— State 状态模式