在23种设计模式当中,策略模式也是一个十分常用的模式。也许在写业务逻辑的过程中,我们可能需要根据不同的条件写一大堆的if
和else
条件语句,这直接造成的影响是可扩展性很差并且可读性也很差。而借助策略模式,我们可以写出扩展性很好的优雅代码。
假如我们有这样一个需求,在设计商户打折系统中,要针对不同的情况对商户进行折扣计算,并且如果有多种折扣重叠,取最大的折扣(有时候也可以累加),具体的折扣计算规则如下:
根据上述的打折条件,我们该怎么设计一个可扩展可维护的系统呢?
一般我们需要遵循面向接口编程和开放封闭原则,在Java设计中,我们首先要定义功能的接口以及实现类,我们首先去定义所需要的类。然后使用if
和else
依次判断所有的情况。
消费记录类
@Data
public class ConsumeRecode {
/**
* 消费时间
*/
private LocalDateTime time;
/**
* 消费用户
*/
private Customer customer;
}
消费者类
@Data
public class Customer {
/**
* 是否为新的用户
*/
private boolean isNewCustomer;
/**
* 当月消费次数
*/
private int count;
}
打折服务接口
public interface DiscountService {
int getDiscountPercent(ConsumeRecode consumeRecode);
}
打折服务实现类
public class DiscountServiceImpl implements DiscountService {
@Override
public int getDiscountPercent(ConsumeRecode consumeRecode) {
return 0;
}
}
然后在getDiscountPercent
当中实现判断逻辑
public int getDiscountPercent(ConsumeRecode consumeRecode) {
int discount = 0;
LocalDateTime time = consumeRecode.getTime();
if (time.getHour() >= 9 && time.getHour() <= 10) {
discount = 90;
}
if (consumeRecode.getCustomer().isNewCustomer()) {
discount = 90;
}
if (consumeRecode.getCustomer().getCoupon() == Coupon.ONE) {
discount = 90;
} else if (consumeRecode.getCustomer().getCoupon() == Coupon.TWO) {
discount = 80;
} else if (consumeRecode.getCustomer().getCoupon() == Coupon.THREE) {
discount = 70;
}
if (consumeRecode.getCustomer().getCount() >= 10) {
discount = 60;
}
return discount;
}
实现起来扩展性很差
我们可以针对四种情况定义四种策略,然后通过Java的stream
特性进行数组的map操作即可,这样具有非常高的可扩展性。
首先定义一个接口,用来约束四种情况的行为
public interface Discount {
/**
* 是否适用于打折条件
* @return
*/
boolean applicable();
/**
* @return 购物打折的百分比
*/
int getDiscountPercent();
}
新顾客打折策略类
public class DiscountNewCustomer implements Discount {
private ConsumeRecode consumeRecode;
public DiscountNewCustomer(ConsumeRecode consumeRecode) {
this.consumeRecode = consumeRecode;
}
@Override
public boolean applicable() {
return consumeRecode.getCustomer().isNewCustomer();
}
@Override
public int getDiscountPercent() {
return 90;
}
}
优惠券打折策略类
public class DiscountCoupon implements Discount {
private ConsumeRecode consumeRecode;
public DiscountCoupon(ConsumeRecode consumeRecode) {
this.consumeRecode = consumeRecode;
}
@Override
public boolean applicable() {
return consumeRecode.getCustomer().getCoupon() != null;
}
@Override
public int getDiscountPercent() {
if (consumeRecode.getCustomer().getCoupon() == Coupon.ONE) {
return 90;
} else if (consumeRecode.getCustomer().getCoupon() == Coupon.TWO) {
return 80;
} else if (consumeRecode.getCustomer().getCoupon() == Coupon.THREE){
return 70;
} else {
throw new RuntimeException("Coupon type is not supported");
}
}
}
常客打折策略类
public class DiscountCount implements Discount {
private ConsumeRecode consumeRecode;
public DiscountCount(ConsumeRecode consumeRecode) {
this.consumeRecode = consumeRecode;
}
@Override
public boolean applicable() {
return consumeRecode.getCustomer().getCount() >= 10;
}
@Override
public int getDiscountPercent() {
return 60;
}
}
时间打折策略类
public class DiscountTime implements Discount {
private ConsumeRecode consumeRecode;
public DiscountTime(ConsumeRecode consumeRecode) {
this.consumeRecode = consumeRecode;
}
@Override
public boolean applicable() {
return consumeRecode.getTime().getHour() >= 9 && consumeRecode.getTime().getHour() <= 10;
}
@Override
public int getDiscountPercent() {
return 90;
}
}
然后是核心服务类
public class DiscountServiceImpl implements DiscountService {
private List<Discount> discounts;
public DiscountServiceImpl(ConsumeRecode consumeRecode) {
discounts = Arrays.asList(new DiscountNewCustomer(consumeRecode), new DiscountCoupon(consumeRecode), new DiscountTime(consumeRecode), new DiscountCount(consumeRecode));
}
@Override
public int getDiscountPercent() {
return discounts.stream().filter(Discount::applicable).map(Discount::getDiscountPercent).min(Comparator.naturalOrder()).orElse(0);
}
}
这样的话就能动态扩展我们的需求了
上面的实例其实也有一些不足,比如优惠券的情况,我们也可以建立不同的优惠券策略类,不过大体的思路还是相同的。