设计模式—状态模式
状态模式
当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。
状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象 对应的类发生了改变一样。
优点
- 结构清晰。避免了过多的switch...case或者if...else语句的使用, 避免了程序的复杂性,提高系统的可维护性 。
- 遵循设计原则 。
- 封装性非常好 。
缺点
子类会太多, 也就是类膨胀。
适用场景
- 行为随状态改变而改变的场景。例如权限设计,人员的状态不同即使执行相同的行为结果也会不同, 在这种情况下需要考虑使用状态模式。
- 条件、 分支判断语句的替代者。
注意事项
 状态模式适用于当某个对象在它的状态发生改变时, 它的行为也随着发生比较大的变化, 也就是说在行为受状态约束的情况下可以使用状态模式, 而且使用时对象的状态最好不要超过5个。 —来源《设计模式之禅 第二版》
通用类图
classDiagram
      Context o.. State
      State <|-- ConcreteState
      class State{
          +handle()
      }
      class Context{
          +request()
      }
例子
电梯的开门、关门、运行、停止状态的变化
- 上下文
/**
 * 上下文
 *
 * @author admin
 */
public class Context {
    //定义出所有的电梯状态
    public final static OpenningState OPENNING_STATE = new OpenningState();
    public final static ClosingState CLOSEING_STATE = new ClosingState();
    public final static RunningState RUNNING_STATE = new RunningState();
    public final static StoppingState STOPPING_STATE = 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();
    }
}
- 抽象状态角色
/**
 * 抽象状态角色
 *
 * @author admin
 */
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();
}
- 开门状态
/**
 * 开门状态
 *
 * @author admin
 */
public class OpenningState extends LiftState {
    //开启当然可以关闭了, 我就想测试一下电梯门开关功能
    @Override
    public void close() {
        //状态修改
        super.context.setLiftState(Context.CLOSEING_STATE);
        //动作委托为CloseState来执行
        super.context.getLiftState().close();
    }
    //打开电梯门
    @Override
    public void open() {
        System.out.println("电梯门开启...");
    }
    @Override
    public void run() {
        //do nothing;
    }
    //开门还不停止?
    public void stop() {
        //do nothing;
    }
}
- 关门状态
/**
 * 关闭状态
 *
 * @author admin
 */
public class ClosingState extends LiftState {
    //电梯门关闭, 这是关闭状态要实现的动作
    @Override
    public void close() {
        System.out.println("电梯门关闭...");
    }
    //电梯门关了再打开
    @Override
    public void open() {
        super.context.setLiftState(Context.OPENNING_STATE); //置为敞门状态
        super.context.getLiftState().open();
    }
    //电梯门关了就运行,这是再正常不过了
    @Override
    public void run() {
        super.context.setLiftState(Context.RUNNING_STATE); //设置为运行状态
        super.context.getLiftState().run();
    }
    //电梯门关着,我就不按楼层
    @Override
    public void stop() {
        super.context.setLiftState(Context.STOPPING_STATE); //设置为停止状态
        super.context.getLiftState().stop();
    }
}
- 运行状态
/**
 * 运行状态
 *
 * @author admin
 */
public class RunningState extends LiftState {
    //电梯门关闭? 这是肯定的
    @Override
    public void close() {
        //do nothing
    }
    //运行的时候开电梯门?你疯了!电梯不会给你开的
    @Override
    public void open() {
        //do nothing
    }
    //这是在运行状态下要实现的方法
    @Override
    public void run() {
        System.out.println("电梯上下运行...");
    }
    //这绝对是合理的,只运行不停止还有谁敢坐这个电梯? !估计只有上帝了
    @Override
    public void stop() {
        //环境设置为停止状态
        super.context.setLiftState(Context.STOPPING_STATE);
        super.context.getLiftState().stop();
    }
}
- 停止状态
/**
 * 关闭状态
 *
 * @author admin
 */
public class StoppingState extends LiftState {
    //停止状态关门? 电梯门本来就是关着的!
    @Override
    public void close() {
        //do nothing;
    }
    //停止状态,开门,那是要的!
    @Override
    public void open() {
        super.context.setLiftState(Context.OPENNING_STATE);
        super.context.getLiftState().open();
    }
    //停止状态再运行起来,正常得很
    @Override
    public void run() {
        super.context.setLiftState(Context.RUNNING_STATE);
        super.context.getLiftState().run();
    }
    //停止状态是怎么发生的呢?当然是停止方法执行了
    @Override
    public void stop() {
        System.out.println("电梯停止了...");
    }
}
- 客户端
/**
 * 客户端
 *
 * @author admin
 */
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();
    }
}
- 运行结果
电梯门开启...
电梯门关闭...
电梯上下运行...
电梯停止了...
状态模式与策略模式的差异
状态模式通用类图
classDiagram
    Context o.. State : +state
    State <|-- ConcreteState
    class State{
    +handle()
    }
    class Context{
    +ContextInterface()
    +request()
    }
策略模式通用类图
classDiagram
    Context o.. Strategy : +strategy
    Strategy <|-- ConcreteStrategy
    class Strategy{
    	+AlgorithmInterface()
    }
    class Context{
    	+ContextInterface()
    }
区别:策略模式封装的是不同的算法, 算法之间没有交互, 以达到算法可以自由切换的目的; 而状态模式封装的是不同的状态, 以达到状态切换行为随之发生改变的目的。
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号