策略模式——分离算法,选择实现

  策略模式的本质就是选择不同的算法实现,其适用的是经常变动的业务场景,比如某个软件的计费方式,某个商品的活动打折等。在这类场景中,随着业务的发展,就会有新的算法出现,而且我们软件需要快速的响应,以便满足业务场景的要求。实现的思路是:我们需要抽象算法的整体接口,然后将具体的实现方式和使用的地方区分开来,使的有新的算法出现的时候,客户端可以自由的选择需要的算法,快速的满足业务的要求。

策略模式的结构和说明

策略模式结构图

  • 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. 活动示例类图

促销活动示例图

总结

  策略模式的本质是分离算法,选择实现,其主要的思路就是将变化的算法部分单独出来,利用调用上下文的角色来选择具体的策略实现。策略模式是面向对象编程的很好的体现,上下文调用类只知道具体的接口,在实际调用的时候才会去调用具体的实现类,从而获得了更好的扩展性。


后记
  个人总结,欢迎转载、评论、批评指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值