Java 设计模式中的策略模式详情

 

策略模式的应用场景

策略模式是否要使用,取决于业务场景是否符合,有没有必要。

是否符合

如果业务是处于不同的场景时,采取不同的处理方式的话,就满足符合。 这里举几个业务栗子

如果今天我有1000块,那我就和朋友去游乐园玩、然后再去吃顿好吃的。如果有2000块的话,那我就先买一件好看的衣服,再和朋友去游乐园玩和吃好吃的商品收银业务,根据店中不同的活动分为正常收费、折扣收费、返利收费,每种收费的方式都不一样接收某安全设备的数据的时候,根据发过来不同的数据类型,执行设备报警、设备指定信息存储、设备本身状态信息修改等等的处理方式

这里上面三种业务场景都符合处于不同的场景时,采取不同的处理方式

有没有必要

这里等描述完例子后,再回到这个问题

 

例子

这里采用上面第三个例子的业务分别进行编写不用策略模式和用策略模式的效果

不用策略模式

public class Main {
  public static void main(String[] args) {
      //设备报警
      int deviceDataType=2;
      DeviceData deviceData=new DeviceData();
      deviceData.setName("安全设备A");

      switch (deviceDataType){
          case 1:
              DeviceCallPolice(deviceData);
              break;
          case 2:
              DeviceStatus(deviceData);
              break;
          default:
              System.out.println("没有相关的策略");
              break;
      }

  }
  private static void DeviceStatus(DeviceData deviceData) {
      System.out.println("正在修改"+deviceData.getName()+"状态为指定状态");
      System.out.println("正在记录本次修改状态的时间信息");
      System.out.println("根据修改后的状态判断是否要通知相关人");
  }
  private static void DeviceCallPolice(DeviceData deviceData) {
      System.out.println("正在修改"+deviceData.getName()+"状态为报警状态");
      System.out.println("正在记录本次报警时间信息");
      System.out.println("设备相关人的短信通知");
  }

}

效果:

使用策略模式

策略上下文

/**
* 
* 安全设备处理上下文
*/
public class SafetyDeviceContext {

  private DeviceHandlerStrategy deviceHandlerStrategy;

  public SafetyDeviceContext(int dataType){
      switch (dataType){
          case 1:
              deviceHandlerStrategy=new DeviceCallPoliceStrategy();
              break;
          case 2:
              deviceHandlerStrategy=new DeviceStatusStrategy();
              break;
          default:
              System.out.println("没有相关的策略");
              break;
      }

  }
  public void Handler(DeviceData deviceData){

      deviceHandlerStrategy.handler(deviceData);
  }
}

策略接口以及具体实现类

/**
* 设备处理策略
*/
public interface DeviceHandlerStrategy {

  void handler(DeviceData deviceData);
}
/**
* 安全设备报警策略
*/
public class DeviceCallPoliceStrategy implements DeviceHandlerStrategy {

  @Override
  public void handler(DeviceData deviceData) {

      System.out.println("正在修改设备状态为报警状态");
      System.out.println("正在记录本次报警时间信息");
      System.out.println("设备相关人的短信通知");
  }
}
/**
* 设备状态修改策略
*/
public class DeviceStatusStrategy implements DeviceHandlerStrategy {

  @Override
  public void handler(DeviceData deviceData) {
      System.out.println("正在修改设备状态为指定状态");
      System.out.println("正在记录本次修改状态的时间信息");
      System.out.println("根据修改后的状态判断是否要通知相关人");
  }
}

Main类

public class Main {

  public static void main(String[] args) {
      //设备报警
      int deviceDataType=2;
      DeviceData deviceData=new DeviceData();
      deviceData.setName("安全设备A");
      SafetyDeviceContext safetyDeviceContext=new SafetyDeviceContext(deviceDataType);
      safetyDeviceContext.handler(deviceData);
  }
}

效果:

 

两种方式的不同

从简易和易懂程度来说,自然是不用策略模式好一些,用策略模式会导致类的增多,而且如果不懂策略模式的人去看代码时可读性不高。但使用策略模式胜于可扩展性和可维护性要强于不用策略模式的

使用策略模式在Main函数(使用方),消除了类型的swich判断,把这些判断放到了Context,使用方调用的时候,就不必过多关心设备处理策略,只需要把值传进Context就好了。

之前我很不理解策略模式为什么能避免使用多重条件判断,因为上面的写法只是把判断的代码移到了Context而已,要写的判断还是要写,直到我知道了采用字典和反射的方式时,就理解了,往下会说

  • 如果后续我们要添加一种"设备定时报告信息"业务处理方式时,使用策略模式那种只需要在Context添加一层case(也可以不需要,可以用反射的方式)和添加一个策略的实现类就好了,而不用策略模式需要在原来使用方的代码添加一层case,可能会影响到原来的业务稳定性。
  • 算法独立出来也方便我们使用单元测试进行测试

 

策略模式有没有必要使用?

策略模式的目的是减少具体的算法方式使用方式之间的耦合,避免多重if判断,并让具体的算法方法尽可能独立。所以实现的时候,必然成本会高一些。因此我们要使用策略模式的时候,要确定该业务业务本身是否复杂,后续是不是会时不时添加其他的处理方式
例如上面的第三个例子,设备对应的处理措施本身具备了复杂性,例如会涉及数据本身的处理、设备状态的更改、设备事件的触发等等处理措施,而且后续因业务的扩展,可能会添加不同的设备场景,不同的设备处理方式。像这种就可以考虑使用

 

如何避免Context类使用判断逻辑

可以采用字典+反射的方式进行避免

定义策略字典的全局变量

/**
* 设备处理策略方式字典
*/
public class DeviceStrategyDictionary {
  public static HashMap<Integer,String> dictionary=new HashMap<>();

}

修改Context的代码

import java.util.HashMap;

/**
* 安全设备处理上下文
*/
public class SafetyDeviceContext {

  private DeviceHandlerStrategy deviceHandlerStrategy;

  public SafetyDeviceContext(int dataType) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
      HashMap<Integer,String> dic= DeviceStrategyDictionary.dictionary;
      if(!dic.containsKey(dataType)){
          System.out.println("不包含该处理方式");
      }
      //使用反射的方式
      Class<?> fz=Class.forName(dic.get(dataType));
      deviceHandlerStrategy=(DeviceHandlerStrategy) fz.newInstance();

  }
  public void handler(DeviceData deviceData){

      deviceHandlerStrategy.handler(deviceData);
  }
}

Main方法

public class Main {

  public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

      //程序启动的时候
      DeviceStrategyDictionary.dictionary.put(1,"DeviceCallPoliceStrategy");
      DeviceStrategyDictionary.dictionary.put(2,"DeviceStatusStrategy");

      //设备报警
      int deviceDataType=2;
      DeviceData deviceData=new DeviceData();
      deviceData.setName("安全设备A");

      SafetyDeviceContext safetyDeviceContext=new SafetyDeviceContext(deviceDataType);

      safetyDeviceContext.handler(deviceData);
  }

}

效果:

这样就可以避免使用if判断了,不过反射也有相关的性能消耗,这点也需要做权衡。

关于Java 设计模式中的策略模式详情的文章就介绍至此,更多相关Java 策略模式内容请搜索编程宝库以前的文章,希望以后支持编程宝库

 一. String类简介1. 介绍字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。Java的Strin ...