第六章 6.5 状态模式

分类:01_设计模式

标签:

6.5.1 状态模式介绍

自然界很多事物都有多种状态,而且不同状态下会具有不同的行为,这些状态在特定条件下还会发生相互转换,比如水

117.jpg

在软件系统中,有些对象也像水一样具有多种状态,这些状态在某些情况下能够相互转换,而且对象在不同状态下也将具有不同的行为.

状态模式(state pattern)的定义:  允许一个对象在其内部状态改变时改变它的行为. 对象看起来似乎修改了它的类.

状态模式就是用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题. 状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中(用类来表示状态) ,使得对象状态可以灵活变化.

6.5.2 状态模式结构

状态模式结构图:

118.jpg

从这个 UML 图中,我们能看出状态模式包含的关键角色有三个。

6.5.3 状态模式实现

代码示例


/**
* 抽象状态接口
* @author spikeCong
* @date 2022/10/17
**/
public interface State {

   //声明抽象方法,不同具体状态类可以有不同实现
   void handle(Context context);
}

/**
* 上下文类
* @author spikeCong
* @date 2022/10/17
**/
public class Context {

   private State currentState; //维持一个对状态对象的引用

   public Context() {
       this.currentState = null;
   }

   public State getCurrentState() {
       return currentState;
   }

   public void setCurrentState(State currentState) {
       this.currentState = currentState;
   }

   @Override
   public String toString() {
       return "Context{" +
               "currentState=" + currentState +
               '}';
   }
}

public class ConcreteStateA implements State {

   @Override
   public void handle(Context context) {
       System.out.println("进入状态模式A......");
       context.setCurrentState(this);
   }

   @Override
   public String toString() {
       return "当前状态: ConcreteStateA";
   }
}

public class ConcreteStateB implements State{

   @Override
   public void handle(Context context) {
       System.out.println("进入状态模式B......");
       context.setCurrentState(this);
   }

   @Override
   public String toString() {
       return "当前状态: ConcreteStateB";
   }
}


public class Client {

   public static void main(String[] args) {

       Context context = new Context();

       State state1 = new ConcreteStateA();
       state1.handle(context);
       System.out.println(context.getCurrentState().toString());

       System.out.println("========================");

       State state2 = new ConcreteStateB();
       state2.handle(context);
       System.out.println(context.getCurrentState().toString());
   }
}

6.5.4 状态模式应用实例

模拟交通信号灯的状态转换. 交通信号灯一般包括了红、黄、绿3种颜色状态,不同状态之间的切换逻辑为: 红灯只能切换为黄灯,黄灯可以切换为绿灯或红灯,绿灯只能切换为黄灯.

 119.jpg                                                            

1) 不使用设计模式


/**
* 交通灯类
*    红灯(禁行) ,黄灯(警示),绿灯(通行) 三种状态.
* @author spikeCong
* @date 2022/10/17
**/
public class TrafficLight {

   //初始状态红灯
   private String state = "红";

   //切换为绿灯(通行)状态
   public void switchToGreen(){

       if("绿".equals(state)){//当前是绿灯
           System.out.println("当前为绿灯状态,无需切换!");
       }else if("红".equals(state)){
           System.out.println("红灯不能切换为绿灯!");
       }else if("黄".equals(state)){
           state = "绿";
           System.out.println("绿灯亮起...时长: 60秒");
       }
   }

   //切换为黄灯(警示)状态
   public void switchToYellow(){

       if("黄".equals(state)){//当前是黄灯
           System.out.println("当前为黄灯状态,无需切换!");
       }else if("红".equals(state) || "绿".equals(state)){
           state = "黄";
           System.out.println("黄灯亮起...时长:10秒");
       }
   }

   //切换为黄灯(警示)状态
   public void switchToRed(){

       if("红".equals(state)){//当前是绿灯
           System.out.println("当前为红灯状态,无需切换!");
       }else if("绿".equals(state)){
           System.out.println("绿灯不能切换为红灯!");
       }else if("黄".equals(state)){
           state = "红";
           System.out.println("红灯亮起...时长: 90秒");
       }
   }
}

问题: 状态切换的操作全部在一个类中,如果有很多的交通灯进行联动,这个程序的逻辑就会变得非常复杂,难以维护.

2) 使用状态模式,将交通灯的切换逻辑组织起来,把跟状态有关的内容从交通灯类里抽离出来,使用类来表示不同的状态.


/**
* 交通灯类
*    红灯(禁行) ,黄灯(警示),绿灯(通行) 三种状态.
* @author spikeCong
* @date 2022/10/17
**/
public class TrafficLight {

   //初始状态红灯
   State state = new Red();

   public void setState(State state) {
       this.state = state;
   }

   //切换为绿灯状态
   public void switchToGreen(){
       state.switchToGreen(this);
   }

   //切换为黄灯状态
   public void switchToYellow(){
       state.switchToYellow(this);
   }

   //切换为红灯状态
   public void switchToRed(){
       state.switchToRed(this);
   }
}

/**
* 交通灯状态接口
* @author spikeCong
* @date 2022/10/17
**/
public interface State {

   void switchToGreen(TrafficLight trafficLight); //切换为绿灯

   void switchToYellow(TrafficLight trafficLight); //切换为黄灯

   void switchToRed(TrafficLight trafficLight); //切换为红灯
}

/**
* 红灯状态类
* @author spikeCong
* @date 2022/10/17
**/
public class Red implements State {

   @Override
   public void switchToGreen(TrafficLight trafficLight) {
       System.out.println("红灯不能切换为绿灯!");
   }

   @Override
   public void switchToYellow(TrafficLight trafficLight) {
       System.out.println("黄灯亮起...时长:10秒!");
   }

   @Override
   public void switchToRed(TrafficLight trafficLight) {
       System.out.println("已是红灯状态无须再切换!");
   }
}

/**
* 绿灯状态类
* @author spikeCong
* @date 2022/10/17
**/
public class Green implements State {

   @Override
   public void switchToGreen(TrafficLight trafficLight) {
       System.out.println("已是绿灯无须切换!");
   }

   @Override
   public void switchToYellow(TrafficLight trafficLight) {
       System.out.println("黄灯亮起...时长:10秒!");
   }

   @Override
   public void switchToRed(TrafficLight trafficLight) {
       System.out.println("绿灯不能切换为红灯!");
   }
}

/**
* 黄灯状态类
* @author spikeCong
* @date 2022/10/17
**/
public class Yellow implements State {

   @Override
   public void switchToGreen(TrafficLight trafficLight) {
       System.out.println("绿灯亮起...时长:60秒!");
   }

   @Override
   public void switchToYellow(TrafficLight trafficLight) {
       System.out.println("已是黄灯无须切换!");
   }

   @Override
   public void switchToRed(TrafficLight trafficLight) {
       System.out.println("红灯亮起...时长:90秒!");
   }
}

public class Client {

   public static void main(String[] args) {
       TrafficLight trafficLight = new TrafficLight();
       trafficLight.switchToYellow();
       trafficLight.switchToGreen();
       trafficLight.switchToRed();
   }
}

通过代码重构,将"状态" 接口化、模块化,最终将它们从臃肿的交通类中抽了出来, 消除了原来TrafficLight类中的if...else,代码看起来干净而优雅.

6.5.5 状态模式总结

1) 状态模式的优点:

2) 状态模式的缺点:

3) 状态模式常见的使用场景:


修改内容