【设计模式之美】策略模式方法论:解耦策略的定义、创建和使用

文章目录

  • [一. 策略的定义-封装策略,面向接口](#一. 策略的定义-封装策略,面向接口)
  • [二. 策略的创建-创建策略工厂](#二. 策略的创建-创建策略工厂)
    • [1. 对于无状态策略](#1. 对于无状态策略)
    • [2. 对于有状态策略](#2. 对于有状态策略)
  • [三. 策略的使用:动态选择](#三. 策略的使用:动态选择)
  • [四. 避免分支判断-策略的优雅](#四. 避免分支判断-策略的优雅)
    • [1. 对于无状态的策略](#1. 对于无状态的策略)
    • [2. 对于有状态的策略](#2. 对于有状态的策略)

策略模式是定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。

它解耦的是策略的定义、创建、使用这三部分。接下来我们看一下这三个部分的逻辑与要解决的问题


一. 策略的定义-封装策略,面向接口

策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。它是面向接口编程,所以可以灵活替换不同的策略。

public interface Strategy {
  void algorithmInterface();
}

public class ConcreteStrategyA implements Strategy {
  @Override
  public void  algorithmInterface() {
    //具体的算法...
  }
}

public class ConcreteStrategyB implements Strategy {
  @Override
  public void  algorithmInterface() {
    //具体的算法...
  }
}

二. 策略的创建-创建策略工厂

策略模式会包含一组策略,具体使用哪个策略,可以通过type来选定。我们可以把一组策略放到放到工厂类。

1. 对于无状态策略

策略模式是否有状态

如果策略类是无状态的,不包含成员变量,只是纯粹的算法实现,这样的策略对象是可以被共享使用的,不需要在每次调用 getStrategy() 的时候,都创建一个新的策略对象。即如上我们提前创建好每个策略对象,缓存到工厂类中,用的时候直接返回。

java 复制代码
public class StrategyFactory {
  private static final Map<String, Strategy> strategies = new HashMap<>();

  static {
    strategies.put("A", new ConcreteStrategyA());
    strategies.put("B", new ConcreteStrategyB());
  }

  public static Strategy getStrategy(String type) {
    ... check some
    return strategies.get(type);
  }
}

2. 对于有状态策略

如果策略类是有状态的,每次获取工厂中获取的策略都是新的策略对象。那可以如下创建:

java 复制代码
public class StrategyFactory {
  public static Strategy getStrategy(String type) {
    if (type == null || type.isEmpty()) {
      throw new IllegalArgumentException("type should not be empty.");
    }
    if (type.equals("A")) {
      return new ConcreteStrategyA();
    } else if (type.equals("B")) {
      return new ConcreteStrategyB();
    }
    return null;
  }
}

三. 策略的使用:动态选择

选定某一策略,一般根据业务逻辑,动态的选定某一个策略。

java 复制代码
// 策略接口:EvictionStrategy
// 策略类:LruEvictionStrategy、FifoEvictionStrategy、LfuEvictionStrategy...
// 策略工厂:EvictionStrategyFactory

public class UserCache {
  private Map<String, User> cacheData = new HashMap<>();
  private EvictionStrategy eviction;

  public UserCache(EvictionStrategy eviction) {
    this.eviction = eviction;
  }

  //...
}

// 运行时动态确定,根据配置文件的配置决定使用哪种策略
public class Application {
  public static void main(String[] args) throws Exception {
    EvictionStrategy evictionStrategy = null;
    Properties props = new Properties();
    props.load(new FileInputStream("./config.properties"));
    String type = props.getProperty("eviction_type");
    evictionStrategy = EvictionStrategyFactory.getEvictionStrategy(type);
    UserCache userCache = new UserCache(evictionStrategy);
    //...
  }
}


//或者直接根据type动态选择

四. 避免分支判断-策略的优雅

能够移除分支判断逻辑的模式不仅仅有策略模式,状态模式也可以。

如下判断分支:

java 复制代码
public class OrderService {
  public double discount(Order order) {
    double discount = 0.0;
    OrderType type = order.getType();
    if (type.equals(OrderType.NORMAL)) { // 普通订单
      //...省略折扣计算算法代码
    } else if (type.equals(OrderType.GROUPON)) { // 团购订单
      //...省略折扣计算算法代码
    } else if (type.equals(OrderType.PROMOTION)) { // 促销订单
      //...省略折扣计算算法代码
    }
    return discount;
  }
}

1. 对于无状态的策略

对上面的代码重构,将不同的逻辑设计成策略类,并由工厂类来负责创建策略对象。然后通过map码表来动态选择策略。这样就没有了 if-else 分支判断语句了。

java 复制代码
// 策略的定义
public interface DiscountStrategy {
  double calDiscount(Order order);
}
// 省略NormalDiscountStrategy、GrouponDiscountStrategy、PromotionDiscountStrategy类代码...

// 策略的创建
public class DiscountStrategyFactory {
  private static final Map<OrderType, DiscountStrategy> strategies = new HashMap<>();

  static {
    strategies.put(OrderType.NORMAL, new NormalDiscountStrategy());
    strategies.put(OrderType.GROUPON, new GrouponDiscountStrategy());
    strategies.put(OrderType.PROMOTION, new PromotionDiscountStrategy());
  }

  public static DiscountStrategy getDiscountStrategy(OrderType type) {
    return strategies.get(type);
  }
}

// 策略的使用
public class OrderService {
  public double discount(Order order) {
    OrderType type = order.getType();
    DiscountStrategy discountStrategy = DiscountStrategyFactory.getDiscountStrategy(type);
    return discountStrategy.calDiscount(order);
  }
}

2. 对于有状态的策略

对于有状态的策略,将判断逻辑放到了工厂类中

java 复制代码
public class DiscountStrategyFactory {
  public static DiscountStrategy getDiscountStrategy(OrderType type) {
    if (type == null) {
      throw new IllegalArgumentException("Type should not be null.");
    }
    if (type.equals(OrderType.NORMAL)) {
      return new NormalDiscountStrategy();
    } else if (type.equals(OrderType.GROUPON)) {
      return new GrouponDiscountStrategy();
    } else if (type.equals(OrderType.PROMOTION)) {
      return new PromotionDiscountStrategy();
    }
    return null;
  }
}

参考:《设计模式之美》--王争

相关推荐
信徒_1 小时前
常用设计模式
java·单例模式·设计模式
lxyzcm19 小时前
深入理解C++23的Deducing this特性(上):基础概念与语法详解
开发语言·c++·spring boot·设计模式·c++23
越甲八千19 小时前
重温设计模式--单例模式
单例模式·设计模式
Vincent(朱志强)19 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式
诸葛悠闲21 小时前
设计模式——桥接模式
设计模式·桥接模式
捕鲸叉1 天前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
小小小妮子~1 天前
框架专题:设计模式
设计模式·框架
先睡1 天前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
waicsdn_haha1 天前
Postman最新详细安装及使用教程【附安装包】
测试工具·api·压力测试·postman·策略模式·get·delete
Damon_X1 天前
桥接模式(Bridge Pattern)
设计模式·桥接模式