策略模式的本质就是选择不同的算法实现,其适用的是经常变动的业务场景,比如某个软件的计费方式,某个商品的活动打折等。在这类场景中,随着业务的发展,就会有新的算法出现,而且我们软件需要快速的响应,以便满足业务场景的要求。实现的思路是:我们需要抽象算法的整体接口,然后将具体的实现方式和使用的地方区分开来,使的有新的算法出现的时候,客户端可以自由的选择需要的算法,快速的满足业务的要求。
策略模式的结构和说明
- Strategy:策略接口,用来约束一些列具体的策略算法。Context使用这个接口来调用具体的策略实现算法。
- ConcreteStrategy:具体的策略实现。
- Context:调用上下文,负责具体的策略类的交互。上下文是一个角色,是一个抽象的概念,而不是一个具体的类的名称,这个是非常重要的,比如一个商品选择不同的策略定价,上下文可以是商品本身,也可以是单独写的一个类。
用策略模式实现一个产品促销的活动
我们在《装饰器模式》中演示过一个促销活动的示例,当时那个场景是各个活动可以自有组合,也就是活动可以叠加起来使用,我们本次用促销活动不能叠加使用的场景来举例,在这种场景中,每一个活动来获取活动价格就是一个单独的策略,不同的策略不能叠加,只能取其中的一种来实现。
1. 首先抽象一个顶层的策略接口PromotionStrategy,用来获取活动的名称和活动的价格
/**
* 促销活动策略接口
*/
public interface PromotionStrategy {
/**
* 获取活动名称
*
* @return :
*/
String getName();
/**
* 计算活动价格
* @param price :商品价格
* @return :计算出来的活动价格
*/
double calculatePromotionPrice(double price);
}
2. 分别实现没有活动、团购、节假日和周年庆三个活动的策略实现
/**
* 没有活动策略
*/
public class EmptyPromotionStrategy implements PromotionStrategy {
@Override
public String getName() {
return "没有参与活动";
}
@Override
public double calculatePromotionPrice(double price) {
System.out.println("没有参与活动,不打折!!");
return price;
}
}
/**
* 团购活动策略
*/
public class GroupBuyPromotionStrategy implements PromotionStrategy {
@Override
public String getName() {
return "团购活动";
}
@Override
public double calculatePromotionPrice(double price) {
System.out.println("团购活动,打9折");
return price * 0.9;
}
}
/**
* 节假日活动策略
*/
public class HolidayPromotionStrategy implements PromotionStrategy {
@Override
public String getName() {
return "节假日活动";
}
@Override
public double calculatePromotionPrice(double price) {
System.out.println("节假日活动,打8折");
return price * 0.8;
}
}
/**
* 周年庆活动策略
*/
public class AnniversaryPromotionStrategy implements PromotionStrategy {
@Override
public String getName() {
return "周年庆活动";
}
@Override
public double calculatePromotionPrice(double price) {
System.out.println("周年庆活动,打7折");
return price * 0.7;
}
}
3. 现实的开发中,为了快速上线一个活动,一般会将某种策略的实现类配置在数据库中,当有一个新的活动需要上线的时候,我们就把这个类的class上传到代码库,将类的配置等信息插入数据库中,调用反射的方式加载类,这样就可以快速的发布一个策略类。在这里,我们用一个策略工厂来模拟这个过程,用map来缓存策略
/**
* 策略工厂
*/
public class StrategyFactory {
private static final Map<String, PromotionStrategy> strategyMap;
public static final String PROMOTION_KEY_DEFAULT = "default";
public static final String PROMOTION_KEY_GROUP_BUY = "group_buy";
public static final String PROMOTION_KEY_HOLIDAY = "holiday";
public static final String PROMOTION_KEY_ANNIVERSARY = "anniversary";
/**
* 初始化策略工厂
*/
static {
strategyMap = new HashMap<>();
strategyMap.put(PROMOTION_KEY_DEFAULT, new EmptyPromotionStrategy());
strategyMap.put(PROMOTION_KEY_GROUP_BUY, new GroupBuyPromotionStrategy());
strategyMap.put(PROMOTION_KEY_HOLIDAY, new HolidayPromotionStrategy());
strategyMap.put(PROMOTION_KEY_ANNIVERSARY, new AnniversaryPromotionStrategy());
}
public static PromotionStrategy getStrategyByKey(String key) {
if (strategyMap.containsKey(key)) {
return strategyMap.get(key);
}
return strategyMap.get(PROMOTION_KEY_DEFAULT);
}
}
4. 实现一个产品Product类,在这个类中,用getPromotionPrice方法来获取活动的价格,参数就是策略工厂中缓存策略的key
/**
* 产品类,代替策略模式的context类
*/
public class Product {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
public double getPromotionPrice(String strategyKey) {
PromotionStrategy strategy = StrategyFactory.getStrategyByKey(strategyKey);
System.out.println("产品名称:" + this.name);
System.out.println("产品原价:" + this.price);
System.out.println("活动名称:" + strategy.getName());
double promotionPrice = strategy.calculatePromotionPrice(this.price);
System.out.println("活动价格:" + promotionPrice);
return promotionPrice;
}
}
5. 编写一个测试用例
public class Client {
@Test
public void testStrategy(){
Product product = new Product("珠宝",10000);
System.out.println("-----------------------------没有活动-----------------------");
product.getPromotionPrice(StrategyFactory.PROMOTION_KEY_DEFAULT);
System.out.println("-----------------------------团购活动-----------------------");
product.getPromotionPrice(StrategyFactory.PROMOTION_KEY_GROUP_BUY);
System.out.println("-----------------------------节假日活动-----------------------");
product.getPromotionPrice(StrategyFactory.PROMOTION_KEY_HOLIDAY);
System.out.println("-----------------------------周年庆活动-----------------------");
product.getPromotionPrice(StrategyFactory.PROMOTION_KEY_ANNIVERSARY);
}
}
结果如下:
-----------------------------没有活动-----------------------
产品名称:珠宝
产品原价:10000.0
活动名称:没有参与活动
没有参与活动,不打折!!
活动价格:10000.0
-----------------------------团购活动-----------------------
产品名称:珠宝
产品原价:10000.0
活动名称:团购活动
团购活动,打9折
活动价格:9000.0
-----------------------------节假日活动-----------------------
产品名称:珠宝
产品原价:10000.0
活动名称:节假日活动
节假日活动,打8折
活动价格:8000.0
-----------------------------周年庆活动-----------------------
产品名称:珠宝
产品原价:10000.0
活动名称:周年庆活动
周年庆活动,打7折
活动价格:7000.0
6. 活动示例类图
总结
策略模式的本质是分离算法,选择实现,其主要的思路就是将变化的算法部分单独出来,利用调用上下文的角色来选择具体的策略实现。策略模式是面向对象编程的很好的体现,上下文调用类只知道具体的接口,在实际调用的时候才会去调用具体的实现类,从而获得了更好的扩展性。
后记
个人总结,欢迎转载、评论、批评指正