What
策略模式,英文全称是 Strategy Design Pattern。在 GoF 的《设计模式》一书中,它是这样定义的:
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. 定义一组算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。
即:定义一组策略类,每个策略类实现相同的接口,让他们可以在运行时(创建、使用)相互替换,解耦策略的定义、创建、使用 这三部分。
- 作用
策略模式主要的作用还是解耦策略的定义、创建和使用,控制代码的复杂度,让每个部分都不至于过于复杂、代码量过多。除此之外,对于复杂代码来说,策略模式还能让其满足开闭原则,添加新策略的时候,最小化、集中化代码改动,减少引入 bug 的风险。
How
经典的策略模式实现
/**
* 经典实现方式
*
* @author yiy
* @date 12/30/2021
*/
public class ClassicWay {
public static void main(String[] args) {
//运行时动态获取type(在程序运行期间,根据配置、用户输入、计算结果等这些不确定因素,动态决定使用哪种策略)
String type = "A";
IStrategy strategy = StrategyFactory.getStrategy(type);
strategy.algorithmInterface();
}
}
/**
* 策略接口
*/
interface IStrategy {
/**
* 策略函数
*/
void algorithmInterface();
}
/**
* 策略类A
*/
class StrategyImplA implements IStrategy {
@Override
public void algorithmInterface() {
//具体的算法...
}
}
/**
* 策略类B
*/
class StrategyImplB implements IStrategy {
@Override
public void algorithmInterface() {
//具体的算法...
}
}
/**
* 策略工厂
* 策略类无状态,可复用(也可每次都new新的策略类,使其不可复用,相互隔离)
*/
class StrategyFactory {
/**
* 策略缓存
* 注意:更优雅的方式,可以把key用枚举来替换,使其更加符合内聚性
*/
private static final Map<String, IStrategy> strategies = new HashMap<>();
static {
strategies.put("A", new StrategyImplA());
strategies.put("B", new StrategyImplB());
}
public static IStrategy getStrategy(String type) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type should not be empty.");
}
return strategies.get(type);
}
}
Why
vs 其他模式
应用案例
通过策略模式避免分支判断
根据不同类型的订单来实现不同的打折方式。
非策略模式实现
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;
}
}
策略模式实现
- 无状态(不包含成员变量,只是纯粹的算法实现),可复用 ```java // 策略的定义 public interface DiscountStrategy { double calDiscount(Order order); } // 省略实现类NormalDiscountStrategy、GrouponDiscountStrategy、PromotionDiscountStrategy类代码…
// 策略的创建
public class DiscountStrategyFactory {
private static final Map
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); } }
- 有状态,不可复用
```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;
}
}
这种实现方式相当于把原来的 if-else 分支逻辑,从 OrderService 类中转移到了工厂类中,并没有真正将它移除。
- 注意:
本质上是借助“查表法”,根据 type 查表(代码中的 strategies 就是表)替代根据 type 分支判断,如需完全避免分支判断可以通过反射来动态创建对象实现。