参考文章:
公众号:漫话编程
https://juejin.im/post/5dad23685188251d2c4ea2b6
1.为什么用策略模式?
通常在业务代码中会使用大量的if-else,比如说关于会员折扣的业务逻辑,不同的会员等级就有不同程度的折扣,通常我们用if-else来组织代码。
问题:在会员等级类型增加或者任何一种会员类型的折扣策略发生变化的情况下,那么这整个算法都要修改。
====》久而久之,这段代码会变得越来越臃肿,并且有极低的可读性、可维护性、可扩展性,并且回归成本较高。
2.什么是策略模式?
实现某一个功能有很多途径(有很多情况需要考虑),此时可以使用一种设计模式使得系统灵活地选择解决途径,也能够方便地增加解决途径。这就是策略模式。
策略模式(Strategy Pattern),指的是定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化。
注意:策略模式只适用管理一组同类型的算法,并且这些算法是完全互斥的情况。
3.如何使用策略模式?
为了保证策略的一致性,一般会用一个抽象的策略类来做算法的定义,而具体每种算法则对应一个具体策略类。
抽象策略类:
public interface Buyer {
/**
* 计算应付价格
*/
public BigDecimal calPrice(BigDecimal orderPrice);
}
具体策略类:
针对不同的会员,定义三种具体的策略类,每个类中分别实现计算方法
/**
* 专属会员
*/
public class ParticularlyVipBuyer implements Buyer {
@Override
public BigDecimal calPrice(BigDecimal orderPrice) {
if (orderPrice.compareTo(new BigDecimal(30)) > 0) {
return orderPrice.multiply(new BigDecimal(0.7));
}
}
}
/**
* 超级会员
*/
public class SuperVipBuyer implements Buyer {
@Override
public BigDecimal calPrice(BigDecimal orderPrice) {
return orderPrice.multiply(new BigDecimal(0.8));
}
}
/**
* 普通会员
*/
public class VipBuyer implements Buyer {
@Override
public BigDecimal calPrice(BigDecimal orderPrice) {
int superVipExpiredDays = getSuperVipExpiredDays();
int superVipLeadDiscountTimes = getSuperVipLeadDiscountTimes();
if(superVipExpiredDays < 7 && superVipLeadDiscountTimes =0){
return orderPrice.multiply(new BigDecimal(0.8));
}
return orderPrice.multiply(new BigDecimal(0.9));
}
}
定义好了抽象策略类和具体策略类之后,我们再来定义上下文类,所谓上下文类,就是集成算法的类。这个例子中就是收银台系统。采用组合的方式把会员集成进来。
!!!!!集成算法类:这是策略模式的核心
将抽象策略类作为集成算法类的构造函数参数之一,这样可以根据传参来调用不同的算法。
即传参传的就是实现了抽象策略类的那些具体策略类。
public class Cashier {
/**
* 会员,策略对象
*/
private Buyer buyer;
public Cashier(Buyer buyer){
buyer = buyer;
}
public BigDecimal quote(BigDecimal orderPrice) {
return this.buyer.calPrice(orderPrice);
}
}
该Cashier类就是一个上下文类,该类的定义体现了多用组合,少用继承、针对接口编程,不针对实现编程两个设计原则。
实现效果:
增加会员类型时,只需要增加一个对应的具体策略类即可;修改某会员类型的相关折扣时,只需要更改那个对用的具体策略类即可,控制了变更的范围,大大降低了成本。
4.对策略模式的总结
<1> 最重要一点就是策略模式中的算法是平等的。对于那一系列的具体策略类,大家的地位是一样的,正因为这个平等性,才能实现算法之间可以相互替换。
<2> 如果所有的具体策略类都有一些公有的行为。这时候,就应当把这些公有的行为放到共同的抽象策略角色Strategy类里面。当然这时候抽象策略角色必须要用Java抽象类实现,而不能使用接口。
缺点:
<1> 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。这种策略类的创建及选择其实也可以通过工厂模式来辅助进行。
<2> 由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。可以通过使用享元模式在一定程度上减少对象的数量。