设计模式之状态模式

146 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情

定义

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

我们来看类图

image.png

首先一个状态模式需要三个角色

  • 上下文环境类
  • 抽象状态类
  • 具体状态类

上下文环境类主要是来设置环境,然后去决定调用那个具体状态类

抽象状态类主要是将这些状态所需要的功能方法聚合在一起,供具体状态类去实现

具体实现类是根据环境的状态真正处理的类

举个例子实现一下

我们每天应该都会坐电梯

而电梯也是有很多的状态,并且不同的状态所能做的动作也是不一样的。

需求分析

电梯的状态所能做的动作(粗略的)

  • 敞开门的状态:开门的动作;可以关门
  • 门关闭的状态:关门的动作;可以开门,可以运行,可以停止
  • 电梯运行的状态:运行的动作;只能停止
  • 电梯停止的状态:停止的动作;可以开门,可以运行

代码实现一下

首先定义一个状态抽象类

根据上面的分析,我们知道一个电梯有哪些动作,这些动作就是类的方法

我们也要引入电梯环境类,来传递电梯的状态,再执行对象状态的动作

/**
 * 电梯抽象状态类,定义电梯的功能方法
 */
abstract class LiftState{

    /**
     * 引入电梯环境类
     */
    protected LiftContext context;

    public void setContext(LiftContext context) {
        this.context = context;
    }

    //首先电梯门开启动作
    public void open(){
        throw new UnsupportedOperationException(context.getLiftState().getClass() + "不支持电梯开门的动作");
    }

    //电梯门关闭动作
    public void close() {
        throw new UnsupportedOperationException(context.getLiftState().getClass() + "不支持电梯关门的动作");
    }

    //电梯要能上能下,运行起来
    public void run() {
        throw new UnsupportedOperationException(context.getLiftState().getClass() + "不支持电梯上下楼的动作");
    }

    //电梯还要能停下来
    public void stop() {
        throw new UnsupportedOperationException(context.getLiftState().getClass() + "不支持电梯停止的动作");
    }

}

所有方法都给了空实现,为了让具体实现类只需要重写自己需要重写的方法,不用再去实现与自己职责无关的方法

具体的状态类

根据上面的分析,也能得知,电梯的各个状态,每个状态也就对应着每一个具体的状态实现类。每次调用与非这个状态直接操作的方法都需要改变上下文环境,再用对象的状态对象调用对应的方法

/**
 * 敞开门状态
 */
class OpenningState extends LiftState{

    @Override
    public void open(){
        System.out.println("电梯开门");
    }

    @Override
    public void close() {
        super.context.setLiftState(LiftContext.closeingState);
        super.context.close();
    }
}

/**
 * 关门状态
 */
class ClosingState extends LiftState{

    @Override
    public void open(){
        super.context.setLiftState(LiftContext.openningState);
        super.context.open();
    }

    @Override
    public void close() {
        System.out.println("电梯关门");
    }

    @Override
    public void run() {
        super.context.setLiftState(LiftContext.runningState);
        super.context.run();
    }

    @Override
    public void stop() {
        super.context.setLiftState(LiftContext.stoppingState);
        super.context.stop();
    }
}

/**
 * 运行状态
 */
class RunningState extends LiftState{

    @Override
    public void run() {
        System.out.println("电梯在运行中");
    }

    @Override
    public void stop() {
        super.context.setLiftState(LiftContext.stoppingState);
        super.context.stop();
    }
}

/**
 * 停止状态
 */
class StoppingState extends LiftState{

    @Override
    public void open() {
        super.context.setLiftState(LiftContext.openningState);
        super.context.open();
    }

    @Override
    public void run() {
        super.context.setLiftState(LiftContext.runningState);
        super.context.run();
    }

    @Override
    public void stop() {
        System.out.println("电梯停住");
    }
}

最后就是上下文的环境类了

/**
 * 电梯环境类
 */
class LiftContext{

    //定义出所有的电梯状态
    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();
    }
}

最后

其实这个电梯的案例实现还是可以优化的,本篇具体定义了好几个方法完全是因为这样更好的理解状态模式。

这些开门,关门,运行,停止的方法完全可以统一定义一个处理方法。每一个状态只有一个处理方法,然后根据环境状态的不同去选择相应的具体状态类去处理。

大家可以动动小手去实现一下。

参考文献、资料

《设计模式之禅》