每日学一个设计模式19——状态模式

状态模式(用类表示状态)

用处

以类表示状态后,可以通过切换类来方便地切换对象的状态,当需要增加新的状态时,如何修改代码也很明确,可以省去许多if,else的状态判断。

角色

  • State (状态)
    该角色表示状态,定义了根据不同状态进行不同处理的接口(API)。该接口(API)是那些处理内容依赖于状态的方法的集合
  • ConcreteState(具体状态)
    该角色表示各个具体的状态,它实现了State接口。
  • Context(上下文)
    该角色持有表示当前状态的ConcreteState角色。此外,它还定义了供外部调用者使用State模式的接口(API)。

类图

注:图中的State少了个MethodD
在这里插入图片描述

由类图可以看出

  • Context类提供了一个对外部调用的接口,通过State接口聚合了ConcreteState类,其中ConcreteState类可以任意切换(多态)。
  • Context类中的方法(requestX,requestY,requestZ)中调用State接口的抽象方法(methodA,methodB,methodC),当聚合的ConcreteState对象切换时,调用的方法也不同,以此来实现在不同状态下实现不同的功能

举例在这里插入图片描述

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){

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

//State角色
interface State{
    void doClock(Context context, int hour);
    void doUse(Context context);
    void doAlarm(Context context);
    void doPhone(Context context);
}

//ConcreteState角色
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 || hour >= 17){
        	//管理状态迁移
            context.changeState(NightState.getInstance());
        }
    }
    public void doUse(Context context){
        context.recordLog("使用金库(白天)");
    }
    public void doAlarm(Context context){
        context.callSecurityCenter("按下警铃(白天)");
    }
    public void doPhone(Context context){
        context.callSecurityCenter("正常通话(白天)");
    }
    public String toString(){
        return "[白天]";
    }
}

//ConcreteState角色
class NightState implements  State{
    private static NightState singleton = new NightState();
    private NightState(){}
    public static State getInstance(){
        return singleton;
    }
    public void doClock(Context context,int hour){
        if(hour >= 9 && hour < 17){
            context.changeState(DayState .getInstance());
        }
    }
    public void doUse(Context context){
        context.recordLog("紧急,晚上使用金库!");
    }
    public void doAlarm(Context context){
        context.callSecurityCenter("按下警铃(晚上)");
    }
    public void doPhone(Context context){
        context.callSecurityCenter("晚上的童话录音");
    }
    public String toString(){
        return "[晚上]";
    }
}


//Context角色的抽象
interface Context{
    void setClock(int hour);
    void changeState(State state);
    void callSecurityCenter(String msg);
    void recordLog(String msg);
}

//Context角色
 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("use");
        private Button buttonAlarm = new Button("alarm");
        private Button buttonPhone = new Button("phone");
        private Button buttonExit = new Button("exit");
        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);
        }
        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("?");
            }
        }
        //设置时间
    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);
    }
    //改变状态
    public void changeState(State state){
        System.out.println("从"+this.state+"状态变为了"+state+"状态。");
        this.state = state;
    }

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

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

总结

  • 符合开闭原则
  • 分而治之的思路,将每个状态的处理分散在状态类上,省去了分支条件判断,当类非常多的时候有优势。
  • 举例中管理状态迁移(ChangeState)的是ConcreteState角色,我们也可以在Context角色中管理状态迁移。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑白程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值