前言
在日常开发中,我们经常需要在相同的上下文环境中针对不同的业务主体采用不同的处理逻辑。举个简单的例子,当我们在电商网站上购物结算时,往往会根据自身会员等级的不同而享受不同的优惠政策,而从系统实现的角度来讲,需要其运行时根据不同需求决定使用某一具体算法策略。策略模式便能较好的解决这个问题,本篇博客就来探讨一下策略模式。
策略模式
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。
策略模式的类图如下:
从上图可以看出,在策略模式中有三个组成部分:
环境(Context) 持有一个Strategy的引用;
抽象策略(Strategy) 抽象角色,通常由一个接口或抽象类实现;
具体策略(ConcreteStrategy) 包装了相关的算法或行为。
典型实现
我们以开篇提到的电商优惠政策为例子,看一下策略模式的典型实现代码。首先我们需要定义一个计算优惠金额的策略接口
public interface DiscountStrategy {
BigDecimal getDiscountAmount(BigDecimal amount);
}
以及三个实现类,代表三种不同的计费策略
//普通价,不享受任何折扣
public class OrdinaryStrategy implements DiscountStrategy {
public BigDecimal getDiscountAmount(BigDecimal amount) {
return amount;
}
}
//会员价,享受95折优惠
public class MemberStrategy implements DiscountStrategy{
public BigDecimal getDiscountAmount(BigDecimal amount) {
return amount.multiply(new BigDecimal("0.95"));
}
}
//高级会员价,享受9折优惠
public class SeniorStrategy implements DiscountStrategy {
public BigDecimal getDiscountAmount(BigDecimal amount) {
return amount.multiply(new BigDecimal("0.9"));
}
}
我们还要定义一个消费者的业务主体,代表上述的环境角色
public class Consumer {
//单次消费金额
private BigDecimal amount;
//累计消费金额
private BigDecimal totalAmount;
//优惠策略
private DiscountStrategy strategy;
public Consumer() {
this.amount = BigDecimal.ZERO;
this.totalAmount = BigDecimal.ZERO;
this.strategy = new OrdinaryStrategy();
}
public void consume(BigDecimal amount){
this.amount = amount;
if(this.totalAmount.compareTo(new BigDecimal(3000)) < 0){
//累计消费金额小于3000元,适用普通优惠策略
this.strategy = new OrdinaryStrategy();
}else if(this.totalAmount.compareTo(new BigDecimal(6000)) < 0){
//累计消费金额小于6000元,适用普通优惠策略
this.strategy = new MemberStrategy();
}else {
//适用高级优惠策略
this.strategy = new SeniorStrategy();
}
}
public BigDecimal getAmount() {
this.totalAmount = this.totalAmount.add(this.amount);
return this.strategy.getDiscountAmount(this.amount);
}
}
最后我们来测试一下代码功能
public class Client {
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.consume(new BigDecimal(2000));
System.out.println("应付金额:"+consumer.getAmount().setScale(1, BigDecimal.ROUND_UP).doubleValue());
consumer.consume(new BigDecimal(2000));
System.out.println("应付金额:"+consumer.getAmount().setScale(1,BigDecimal.ROUND_UP).doubleValue());
consumer.consume(new BigDecimal(2000));
System.out.println("应付金额:"+consumer.getAmount().setScale(1,BigDecimal.ROUND_UP).doubleValue());
consumer.consume(new BigDecimal(2000));
System.out.println("应付金额:"+consumer.getAmount().setScale(1,BigDecimal.ROUND_UP).doubleValue());
}
}
运行结果如下