Appearance
策略设计模式能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。
淘宝天猫双十一,正在搞活动有打折的、有满减的、有返利的等等,这些算法只是一种策略,并且是随时都可能互相替换的, 我们就可以定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
应用刨析
老王计划外出旅游,选择骑自行车、坐汽车、飞机等,每一种旅行方式都是一个策略。如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么可以使用策略模式。不希望暴露复杂的、与算法有关的数据结构,那么可以使用策略模式来封装算法
角色
- Context上下文:屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化
- Strategy策略角色:抽象策略角色,是对策略、算法家族的抽象,定义每个策略或算法必须具有的方法和属性
- ConcreteStrategy具体策略角色:用于实现抽象策略中的操作,即实现具体的算法
需求分析
业务需求:老王面试进了大厂,是电商项目的营销活动组,负责多个营销活动,有折扣、优惠券抵扣、满减等,项目上线后,产品经理找茬,经常新增营销活动,导致代码改动多,加班严重搞的老王很恼火。
他发现这些都是活动策略,商品的价格是根据不同的活动策略进行计算的,因此用策略设计模式进行了优化,后续新增策略后只要简单配置就行了,不用大动干戈
编码实现
创建订单对象
java
@Data
public class ProductOrder {
// 旧的价格
private double oldPrice;
// 用户id 本案例中无意义,模仿真实场景
private int userId;
// 商品id 本案例中无意义,模仿真实场景
private int productId;
public ProductOrder(double oldPrice, int userId, int productId) {
this.oldPrice = oldPrice;
this.userId = userId;
this.productId = productId;
}
}
创建抽象类
java
public abstract class Strategy {
/**
* 根据简单订单对象,计算商品折扣后的价格
* @return
*/
public abstract double computePrice(ProductOrder productOrder);
}
创建促销活动对象
java
public class PromotionContext {
private Strategy strategy;
public PromotionContext(Strategy strategy) {
this.strategy = strategy;
}
/**
* 根据策略计算最终价格
* @param productOrder
* @return
*/
public double executeStrategy(ProductOrder productOrder){
return strategy.computePrice(productOrder);
}
}
创建策略对象,没有活动
java
public class NormalActivity extends Strategy {
@Override
public double computePrice(ProductOrder productOrder) {
return productOrder.getOldPrice();
}
}
创建策略对象,打折策略
java
public class DiscountActivity extends Strategy {
/**
* 具体的折扣
*/
private double rate;
public DiscountActivity(double rate) {
this.rate = rate;
}
@Override
public double computePrice(ProductOrder productOrder) {
//TODO 复杂的策略计算
return productOrder.getOldPrice() * rate;
}
}
创建策略对象,优惠减价
java
public class VoucherActivity extends Strategy{
/**
* 优惠券金额
*/
private double voucher;
public VoucherActivity(double voucher) {
this.voucher = voucher;
}
@Override
public double computePrice(ProductOrder productOrder) {
if (productOrder.getOldPrice() > voucher){
return productOrder.getOldPrice() - voucher;
}
// 如果折扣价格大于商品价格则免费
return 0;
}
}
测试
java
public static void main(String[] args) {
// 构建一个原价800的商品
ProductOrder productOrder = new ProductOrder(800, 1, 1);
// 合并策略
PromotionContext context;
// 最终价格
double finalPrice;
// 不同策略算出不同价格
context = new PromotionContext(new NormalActivity());
finalPrice = context.executeStrategy(productOrder);
System.out.println("NormalActivity = " + finalPrice);
// 打8折
context = new PromotionContext(new DiscountActivity(0.8));
finalPrice = context.executeStrategy(productOrder);
System.out.println("DiscountActivity = " + finalPrice);
// 折扣减80元
context = new PromotionContext(new VoucherActivity(80));
finalPrice = context.executeStrategy(productOrder);
System.out.println("VoucherActivity = " + finalPrice);
}
控制台
java
NormalActivity = 800.0
DiscountActivity = 640.0
VoucherActivity = 720.0
小结
优点
- 满足开闭原则,当增加新的具体策略时,不需要修改上下文类的代码,上下文就可以引用新的具体策略的实例
- 避免使用多重条件判断,如果不用策略模式可能会使用多重条件语句不利于维护,和工厂模式的搭配使用可以很好地消除代码if-else的多层嵌套(工厂模式主要是根据参数,获取不同的策略)
缺点
- 策略类数量会增多,每个策略都是一个类,复用的可能性很小
- 对外暴露了类所有的行为和算法,行为过多导致策略类膨胀