2022年2月17日 作者 zeroheart

状态模式和策略模式

这两个模式常常可以拿来做对比,两个比较相似。

状态模式,对于已经定义好的状态,如果状态变化,那么后续的操作是固定的,是“自动”的,“无感知”的,也就是说没有一个策略的选择,而是状态流转时候,这个状态下有哪些可行的操作是固定的。

策略模式主要是对算法(策略)的封装,在不同场景下,选用不同的算法,使用的是setStrategy(xxx),来设置某种策略,这个策略就是一整个完整的流程动作。

状态模式 | 菜鸟教程 (runoob.com)

策略模式 | 菜鸟教程 (runoob.com)

策略模式 VS 状态模式 | 菜鸟教程 (runoob.com)

状态模式

意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。

主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

何时使用:代码中包含大量与对象状态有关的条件语句。

如何解决:将各种具体的状态类抽象出来。

优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

使用场景: 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。

注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。


上面文章中,糖果机的例子就很好,显示使用if else来处理整个流程,然后使用状态模式来改写。

改写前,投币这个动作,会判断不同的状态,来做不同的处理
    /**
     * 投币
     */
    public void insertQuarter() {
        if(NO_QUARTER == state){
            System.out.println("投币");
            state = HAS_QUARTER;
        }
        else if(HAS_QUARTER == state){
            System.out.println("请不要重复投币!");
            returnQuarter();
        }
        else if(SOLD == state){
            System.out.println("已投币,请等待糖果");
            returnQuarter();
        }else if(SOLD_OUT == state){
            System.out.println("糖果已经售尽");
            returnQuarter();
        }
    }

改写后,每个状态都会有一个投币的动作,但是投币的动作产生的结果是不同的
public abstract class State {
    /**
     * 投币
     */
    public abstract void insertQuarter();

    /**
     * 退币
     */
    public abstract void ejectQuarter();

    /**
     * 转动出糖曲轴
     */
    public abstract void turnCrank();

    /**
     * 发糖
     */
    public abstract void dispense();

    /**
     * 退还硬币
     */
    protected void returnQuarter() {
        System.out.println("退币……");
    }
}

简单的理解,if else是在动作里面,判断状态,每个状态触发后结果不同,状态模式是在特定状态下面触发动作,这个动作会产生什么结果已经被提前固定下来了。
策略模式的例子

创建一个接口
Strategy.java
public interface Strategy {
   public int doOperation(int num1, int num2);
}


创建实现接口的实体类。
OperationAdd.java
public class OperationAdd implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 + num2;
   }
}

OperationSubtract.java
public class OperationSubtract implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 - num2;
   }
}

OperationMultiply.java
public class OperationMultiply implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 * num2;
   }
}

上下问,实现对策略的设置
public class Context {
   private Strategy strategy;
 
   public Context(){
      
   }

   public SetStrategy(Strategy strategy){
      this.strategy = strategy;
   }

   public int executeStrategy(int num1, int num2){
      return strategy.doOperation(num1, num2);
   }
}

调用:
public static void main(String[] args) {
      Context context = new Context();    
      context.SetStrategy(new OperationAdd());    
      System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
      context.SetStrategy(new OperationSubtract());    
      System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
      context.SetStrategy(new OperationMultiply());          System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}