设计模式:状态模式

基本概念

定义

State is a behavioral design pattern that lets an object alter its behavior when its internal state changes. It appears as if the object changed its class.设计模式

当一个对象内在状态改变时容许其改变行为,这个对象看起来像改变了其类。 bash

FSM

状态模式的概念和 FSM (有限状态机)相似。FSM 表示有限个状态以及在这些状态之间的转移和动做等行为的数学计算模型。在任何给定时刻,程序能够处于有限数量的状态。在任何惟一状态下,程序的行为都不一样,而且能够将程序从一种状态切换到另外一种状态。固然,根据当前状态,程序可能会或者不会切换到某些其余状态。这些称为过渡的切换规则也是有限的和预约的。app

举例说明

iPhone 手机 home 键的功能取决于设备的当前状态:ide

  • 关机状态: 没有反应。
  • 开机后首次启动: 密码解锁。
  • 非首次启动: 密码解锁或者指纹解锁。
  • 启动后:返回主页面。

类图

状态模式建议为对象的全部可能状态建立新的类,并将全部只存在于特定状态的行为提取到这些类中。 原始对象称为上下文,而不是一个对象单独实现全部行为,上下文存储当前状态对象的引用,并将全部与状态相关的工做委托给该对象。测试

它的核心是封装,状态的变动引发了行为的变动,从外部看起来就好像这个对象对应的类发生了改变同样。状态模式的类图如图所示。 ui

状态模式中的3个角色。this

  • State——抽象状态角色spa

    接口或抽象类,负责对象状态定义,而且封装环境角色以实现状态切换。设计

  • ConcreteState——具体状态角色3d

    每个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要作的事情,以及本状态如何过渡到其余状态。

  • Context——环境角色

    定义客户端须要的接口,而且负责具体状态的切换。

这种结构看起来相似于“策略”模式,但有一个关键的区别。在状态模式中,特定状态可能彼此了解,并开始从一个状态过渡到另外一个状态,而策略几乎永远不会彼此了解。

代码示例 - 电梯

V 1.0

咱们用程序来实现一个简单的电梯类,电梯有以下动做:开门、关门、运行、中止。先看类图设计:

电梯接口:

public interface ILift {
     public void open();
     public void close();
     public void run();
     public void stop();
}
复制代码

电梯的实现类:

public class Lift implements ILift {
     public void close() {
        System.out.println("电梯门关闭...");
     }
     public void open() {
        System.out.println("电梯门开启...");
     }
     public void run() {
        System.out.println("电梯运行起来...");
     }
     public void stop() {
        System.out.println("电梯中止了...");
     }
}
复制代码

场景类调用

public class Client {
     public static void main(String[] args) {
        ILift lift = new Lift();
        lift.open();
        lift.close();
        lift.run();
        lift.stop();
     }
}
复制代码

V 2.0

为电梯的这4个动做的执行添加前置条件,具体点说就是在特定状态下才能作特定事。

  • 敞门状态

    电梯只能作的动做是关门动做。

  • 闭门状态

    能够进行的动做是:开门(我不想坐电梯了)、中止(忘记按路层号了)、运行。

  • 运行状态

    电梯只能作的是中止。

  • 中止状态

    电梯有两个可选动做:继续运行和开门动做。

在接口中定义4个常量,分别表示电梯的4个状态:敞门状态、闭门状态、运行状态、中止状态,而后在实现类中电梯的每一次动做发生都要对状态进行判断,判断是否能够执行。

电梯实现类:

public class Lift implements ILift {
     private int state;
     public void setState(int state) {
        this.state = state;
     }
     //电梯门关闭
     public void close() {
        //电梯在什么状态下才能关闭
        switch(this.state) {
            case OPENING_STATE:
                this.closeWithoutLogic();  
                this.setState(CLOSING_STATE);
                break;
            case CLOSING_STATE:  
                break;
            case RUNNING_STATE: 
                break;
            case STOPPING_STATE:  
                break;
        }
     }
     //电梯门开启
     public void open() {
        //电梯在什么状态才能开启
        switch(this.state){
            ... ...            
        }
     }
     //电梯开始运行起来
     public void run() {
        switch(this.state){
            ... ...    
        }
     }
     //电梯中止
     public void stop() {
        switch(this.state){
            ... ... 
        }
     }
}
复制代码

一旦咱们开始添加愈来愈多的状态和与状态相关的行为,基于条件判断的弱点便会暴露出来。大多数方法将包含糟糕的条件判断,这些条件会根据当前状态选择方法的正确行为。这样的代码很难维护,由于对转换逻辑的任何更改均可能须要在每种方法中更改状态条件。

随着项目的发展,这个问题会变得愈来愈大。在设计阶段很难预测全部可能的状态和转换。所以,随着时间的推移,这个类可能会变得臃肿。

V 3.0

如何用状态模式来解决这个问题?

抽象电梯代码:

public abstract class LiftState{
     protected Context context;
     public void setContext(Context _context){
        this.context = _context;
     }
     public abstract void open();
     public abstract void close();
     public abstract void run();
     public abstract void stop();
}
复制代码

敞门状态类:

public class OpenningState extends LiftState {
     //开启固然能够关闭了,我就想测试一下电梯门开关功能
     @Override
     public void close() {
        //状态修改
        super.context.setLiftState(Context.closeingState);
        //动做委托为CloseState来执行
        super.context.getLiftState().close();
     }
     //打开电梯门
     @Override
     public void open() {
        System.out.println("电梯门开启...");
     }
     //门开着时电梯就运行跑,这电梯,吓死你!
     @Override
     public void run() {
        //do nothing;
     }
     //开门还不中止?
     public void stop() {
        //do nothing;
     }
}
复制代码

关门状态类:

public class ClosingState extends LiftState {
     //电梯门关闭,这是关闭状态要实现的动做
     @Override
     public void close() {
        System.out.println("电梯门关闭...");
     }
     //电梯门关了再打开
     @Override
     public void open() {
        super.context.setLiftState(Context.openningState);  //置为敞门状态
        super.context.getLiftState().open();
     }
     //电梯门关了就运行,这是再正常不过了
     @Override
     public void run() {
        super.context.setLiftState(Context.runningState); //设置为运行状态
        super.context.getLiftState().run();
     }
     //电梯门关着,我就不按楼层
     @Override
     public void stop() {
        super.context.setLiftState(Context.stoppingState);  //设置为中止状态
        super.context.getLiftState().stop();
     }
}
复制代码

上下文类

public class Context {
     //定义出全部的电梯状态
     public final static OpenningState openningState = new OpenningState();
     public final static ClosingState closeingState = new ClosingState();
     public final static RunningState runningState = new RunningState();
     public final static StoppingState stoppingState = new StoppingState();
     //定义一个当前电梯状态
     private LiftState liftState;
     public LiftState getLiftState() {
        return liftState;
     }
     public void setLiftState(LiftState liftState) {
        this.liftState = liftState;
        //把当前的环境通知到各个实现类中
        this.liftState.setContext(this);
     }
     public void open(){
        this.liftState.open();
     }
     public void close(){
        this.liftState.close();
     }
     public void run(){
        this.liftState.run();
     }
     public void stop(){
        this.liftState.stop();
     }
}
复制代码

调用方:

public class Client {
     public static void main(String[] args) {
             Context context = new Context();
             context.setLiftState(new ClosingState());
             context.open();
             context.close();
             context.run();
             context.stop();
     }
}
复制代码

总结

状态模式的优势

  1. 符合单一职责原则:将与特定状态相关的代码组织到单独的类中。

  2. 符合开放封闭原则:在不更改现有状态类或上下文的状况下引入新状态。

  3. 避免了过长的if 或者是switch判断。

状态模式的缺点

  1. 若是有不少种状态,子类会太多,类膨胀。一个事物有不少个状态也不稀奇,若是彻底使用状态模式就会有太多的子类,很差管理。
  2. 若是类只有几个状态或不多更改,应用状态模式可能会过分设计。

参考:

  1. 设计模式之禅
  2. State
相关文章
相关标签/搜索