策略模式是什么?
策略模式(Strategy Pattern)是一种行为型的设计模式,它定义了一系列的算法,将每一种算法封装起来,让它们可以相互替换使用。此模式让算法的变化独立于使用算法的客户端。
UML 类图
用 UML 类图来描述策略模式的结构,在模式中各个角色之间的关系:
根据上图,总结了模式中各个角色的职责以及它们之间的关系:
- 抽象策略是所有具体策略的通用接口,它声明了公共的策略方法。
- 具体策略实现了具体的策略算法。
- 上下文维护一个指向具体策略的引用,并且仅通过策略接口与该对象交互。
- 客户端创建一个特定的策略对象,并将其传递给上下文。
案例
让我们通过一个案例来帮助我们进一步理解策略模式。假设现在有一个在线商城系统,为了促进消费,商城经常会有打折促销活动。不同的时节会有不同的打折策略,比如周末有满 200 减 20 的活动;双十一有五折的活动。为此,我们定义了一个打折策略的接口,屏蔽不同时节打折策略的差异。
public interface DiscountStrategy {
BigDecimal applyDiscount(final BigDecimal originalPrice);
}
现在,我们可以为各种时节实现具体的打折策略。
// No discount
public class NoDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal applyDiscount(final BigDecimal originalPrice) {
return new BigDecimal(originalPrice.toPlainString());
}
}
// discount strategy of the weekends
public class WeekendStrategy implements DiscountStrategy {
@Override
public BigDecimal applyDiscount(BigDecimal originalPrice) {
// 20 off on purchase of 200 or above
BigDecimal salePrice;
if (originalPrice.compareTo(new BigDecimal("200")) > 0) {
salePrice = originalPrice.subtract(new BigDecimal("20"));
} else {
salePrice = new BigDecimal(originalPrice.toPlainString());
}
return salePrice;
}
}
// discount strategy of Double Eleven
public class DoubleElevenStrategy implements DiscountStrategy {
@Override
public BigDecimal applyDiscount(final BigDecimal originalPrice) {
// discount of 50%.
return originalPrice.multiply(new BigDecimal("0.5"));
}
}
顾客购买商品后,系统会生成一张订单,订单会根据打折策略计算最后的实际总价。
public class Order {
private List<OrderItem> orderItems = new ArrayList<>();
// constructor, getters and setters
public BigDecimal calculateSalePrice(DiscountStrategy discountStrategy) {
// calculate the total price of all order items
BigDecimal totalPrice = orderItems.stream()
.map(item -> item.getTotalPrice())
.reduce(new BigDecimal("0"), (x, y) -> x.add(y));
// calculate the real sale price with discount
BigDecimal salePrice = discountStrategy.applyDiscount(totalPrice);
return salePrice;
}
}
最后,我们通过一个用例来模拟一下各个时节订单是怎么计费的。
public class StrategyMain {
public static void main(String[] args) {
Order order = new Order();
order.addItem(new OrderItem("Apple", new BigDecimal("9.8"), 10));
order.addItem(new OrderItem("Orange", new BigDecimal("6.6"), 10));
order.addItem(new OrderItem("Banana", new BigDecimal("3.9"), 15));
BigDecimal fullPrice = order.calculateSalePrice(new NoDiscountStrategy());
System.out.printf("This order costs $%s at ordinary times.\n", fullPrice);
BigDecimal priceOnWeekends = order.calculateSalePrice(new WeekendStrategy());
System.out.printf("This order costs $%s on weekends.\n", priceOnWeekends);
BigDecimal priceOnDoubleEleven = order.calculateSalePrice(new DoubleElevenStrategy());
System.out.printf("This order costs $%s on Double Eleven.\n", priceOnDoubleEleven);
}
}
案例源码
可在 GitHub 上查看上述案例的完整代码。
参考资料
本文参考的资料如下:
- Head First Design Patterns - Elisabeth Freeman, Chapter 1.
- https://www.baeldung.com/java-strategy-pattern
- https://refactoring.guru/design-patterns/strategy
- https://refactoringguru.cn/design-patterns/strategy
- https://sourcemaking.com/design_patterns/strategy