天天看点

[设计模式] 行为型:状态模式(State Pattern)

文章目录

    • 什么是状态模式
    • 设计与实现
      • 初级方案
      • 状态模式

什么是状态模式

状态模式是针对状态机模型的编程套路,首先要知道状态机是什么。

状态机是一种数学模型,用于对事物状态的变化进行描述。不说那么学术的概念了,用具体的例子来解释会更容易理解。

电梯应该都知道吧,电梯工作过程可以简化为几种状态:开门状态、关门状态、运行状态、停止状态。

  1. 开门状态下可以关门
  2. 关门状态下可以运行或者再次开门
  3. 运行状态下可以停止
  4. 停止状态下可以开门或者再次运行

用状态图表示就是:

[设计模式] 行为型:状态模式(State Pattern)

在现实中,电梯停止后会自动开门,我这里的状态模型把“自动化”因素给剔除了,所以停止后的电梯还有可能进入运行状态。

编程时如果遇到这样的状态机模型,就可以考虑使用状态模式对其进行编码。

设计与实现

初级方案

这里先给出一个初级方案,也是通常情况下,大多数人最容易想到的方案。如果不感兴趣,请直接去下一节看状态模式的实现。

初级方案实现思路很简单,就是熟练使用各种条件判断。

代码大概就像下面的样子:

// 四种状态枚举
public enum StateEnum {
    OPENING, CLOSING, RUNNING, STOPPING
}

// 电梯接口定义
public interface Elevator {
    void setState(StateEnum state); // 设置当前状态
    void open(); // 开门指令
    void close(); // 关门指令
    void run(); // 运行指令
    void stop(); // 停止指令
}

// 电梯接口实现
public class ElevatorImpl implements Elevator {

    private StateEnum state;
    
    @Override
    public void setState(StateEnum state) {
        this.state = state;
    }
    
    @Override
    public void open() {
        if (this.state == StateEnum.CLOSING || this.state == StateEnum.STOPPING) {
            System.out.println("电梯开门");
            this.setState(StateEnum.OPENING);
        }
    }
    
    @Override
    public void close() {
        if (this.state == StateEnum.OPENING) {
            System.out.println("电梯关门");
            this.setState(StateEnum.CLOSING);
        }
    }
    
    @Override
    public void run() {
        if (this.state == StateEnum.CLOSING || this.state == StateEnum.STOPPING) {
            System.out.println("电梯运行");
            this.setState(StateEnum.RUNNING);
        }
    }
    
    @Override
    public void stop() {
        if (this.state == StateEnum.RUNNING) {
            System.out.println("电梯停止");
            this.setState(StateEnum.STOPPING);
        }
    }
}

// 测试方法
public static void main(String[] args) {
    Elevator elevator = new ElevatorImpl();
    elevator.setState(StateEnum.STOPPING);
    elevator.open();
    elevator.close();
    elevator.run();
    elevator.stop();
}
           

这样的代码容易想到,实现起来也很容易,但是有两个明显的缺点:

  1. 判断条件太多,令人眼花缭乱,写的时候容易出错
  2. 如果有新增状态,每个指令方法都得修改,违反开闭原则

状态模式

初级方案是将电梯作为一个主体,状态为其属性,这样封装代码符合人类的自然思维,必然也会导致内部属性状态判断较为繁杂。

状态模式则是在初级方案的基础上,将状态从电梯属性中拆分出来,作为独立的主体。电梯只提供工作指令的接口,指令实际执行过程则是委托给各个状态主体。

来体会一下具体的代码设计方案:

// 电梯状态定义
public abstract class ElevatorState {
    protected Elevator elevator;
    
    public void setElevator(Elevator elevator) {
        this.elevator = elevator;
    }
    
    public abstract void open();
    public abstract void close();
    public abstract void run();
    public abstract void stop();
}

// 电梯接口
public interface Elevator {
    ElevatorState getState(); // 获取电梯状态
    void setState(ElevatorState state); // 设置电梯状态
    void open(); // 开门指令
    void close(); // 关门指令
    void run(); // 运行指令
    void stop(); // 停止指令
}

// 开门状态
public class OpeningState extends ElevatorState {
    @Override
    public void open() {
        System.out.println("电梯开门");
    }
    
    @Override
    public void close() {
        super.elevator.setState(ElevatorImpl.CLOSING_STATE);
        super.elevator.getState().close();
    }
    
    @Override
    public void run() {}
    
    @Override
    public void stop() {}
}

// 关门状态
public class ClosingState extends ElevatorState {
    @Override
    public void open() {
        super.elevator.setState(ElevatorImpl.OPENING_STATE);
        super.elevator.getState().open();
    }
    
    @Override
    public void close() {
        System.out.println("电梯关门");
    }
    
    @Override
    public void run() {
        super.elevator.setState(ElevatorImpl.RUNNING_STATE);
        super.elevator.getState().run();
    }
    
    @Override
    public void stop() {}
}

// 运行状态
public class RunningState extends ElevatorState {
    @Override
    public void open() {}
    
    @Override
    public void close() {}
    
    @Override
    public void run() {
        System.out.println("电梯运行");
    }
    
    @Override
    public void stop() {
        super.elevator.setState(ElevatorImpl.STOPPING_STATE);
        super.elevator.getState().stop();
    }
}

// 停止状态
public class StoppingState extends ElevatorState {
    @Override
    public void open() {
        super.elevator.setState(ElevatorImpl.OPENING_STATE);
        super.elevator.getState().open();
    }
    
    @Override
    public void close() {}
    
    @Override
    public void run() {
        super.elevator.setState(ElevatorImpl.RUNNING_STATE);
        super.elevator.getState().run();
    }
    
    @Override
    public void stop() {
        System.out.println("电梯停止");
    }
}

// 电梯实现类
public class ElevatorImpl implements Elevator {
    public final static OpeningState OPENING_STATE = new OpeningState();
    public final static ClosingState CLOSING_STATE = new ClosingState();
    public final static RunningState RUNNING_STATE = new RunningState();
    public final static StoppingState STOPPING_STATE = new StoppingState();
    
    private ElevatorState state;
    
    @Override
    public ElevatorState getState() {
        return state;
    }
    
    @Override
    public void setState(ElevatorState state) {
        this.state = state;
        this.state.setElevator(this);
    }
    
    @Override
    public void open() {
        this.state.open();
    }
    
    @Override
    public void close() {
        this.state.close();
    }
    
    @Override
    public void run() {
        this.state.run();
    }
    
    @Override
    public void stop() {
        this.state.stop();
    }
}

// 测试方法
public static void main(String[] args) {
    Elevator elevator = new ElevatorImpl();
    elevator.setState(new ClosingState());
    elevator.open();
    elevator.close();
    elevator.run();
    elevator.stop();
}
           

看,类里面的条件判断语句都没有了,新增状态也很方便。

这就是状态模式的作用,实现状态机的运行过程,但是却消除了各种条件判断,很好的解耦了状态之间的联系。