什么是策略模式
经典的定义是这样的:定义一系列的算法,把他们封装起来,并且使他们可以互相替换,让算法独立于他的客户而独立变化。
大概明白了,就是说我们在本生的业务逻辑类,会做一些场景判断,不同的场景我们用不同的逻辑来处理。我们现在把处理不同场景的代码不再写到这个业务逻辑类本身里面去了,而是把它封装到其他类里面。
结构图大概是这样:
从上图看到,策略模式分为Strategy(策略:定义所有支持的算法的公共接口),ConcreteStrategy(具体策略:以Strategy接口实现某具体算法),Context(上下文:用一个ConcreteStrategy对象来配置,维护一个Strategy对象的引用,可以定义一个接口让Strategy访问它的数据)。
为啥要用策略模式
我们先来看一个简单的示例:
public class SalePage {
public void accountPrice(String userLevel, double price) {
if ("normal".equals(userLevel)) {
price = price * 0.95;
System.out.println("该商品价格为 " + String.valueOf(price));
System.out.println("我们将为你返回消费积分" + String.valueOf(price));
//Something else to do
} else if ("golden".equals(userLevel)) {
price = price * 0.9;
System.out.println("该商品价格为 " + String.valueOf(price));
System.out.println("我们将为你返回一个购物券,价值 " + String.valueOf(price));
//Something else to do
} else if ("diomand".equals(userLevel)) {
price = price * 0.8;
System.out.println("该商品价格为 " + String.valueOf(price));
System.out.println("我们将为你返回一个购物券,价值 " + String.valueOf(price));
//Something else to do
}
}
}
跑一把测试,结果是这样的了:
这是一个商城针对不同用户做折扣活动策略的例子,没有使用策略模式,我们把所有的折扣逻辑处理都放到了都放到了SalePage类中,当前是只有普通,黄金,钻石用户三个身份。
好像问题不大,试想一下这个逻辑如果今天折扣活动策略变了(这个经常发生),或者用户等级划分增加或者修改了,是不是都要跑到SalePage类中去操作一把。
嗯,这好像违反了开闭原则(关于软件开发的六大原则一直就只弄明白单一职责和开闭,哈哈)。这有啥,我觉得至少有两点可以警惕,工程场景可能遇到的情况要杂的多,示例没写而已(something else to do处略去五百行代码哈),你都放到SalePage日积月累每次修改会搞崩掉的。还有就是不便于扩展,如果今天又增加了个用户等级,你又跑到里面去加个if else,方法过长不说,IDE如果集成了p3c之类的插件还会报警告。
看到没有,Martin Fowler(对,就是那位提出微服务概念的教父级人物)和阿里都告诉你了,我已经嗅到了你代码里面的坏味道了。改吧,主要是改起来也很简单哈。
怎么用策略模式呢
其实上面提到策略模式的定义大概已经告诉你要怎么改了,定义说了,封装起来,让算法独立于他的客户而独立变化。
封装?咋封装,搞个接口定义折扣,不同用户折扣策略放不同类里面呗,这里就分别对应了上面结构提到的策略和具体策略了。
于是我们可以这样写,折扣策略接口:
public interface DiscountStrategy {
double discount(double price);
}
普通用户具体策略实现类:
@Service
public class NormalStrategy implements DiscountStrategy {
public double discount(double price) {
price = price * 0.95;
System.out.println("该商品价格为 " + String.valueOf(price));
System.out.println("我们将为你返回消费积分" + String.valueOf(price));
//Something else to do
return price;
}
}
黄金用户具体策略实现类:
@Service
public class GoldenStrategy implements DiscountStrategy {
public double discount(double price) {
price = price * 0.9;
System.out.println("该商品价格为 " + String.valueOf(price));
System.out.println("我们将为你返回消费积分" + String.valueOf(price));
//Something else to do
return price;
}
}
钻石用户具体策略实现类:
@Service
public class DiamondStrategy implements DiscountStrategy{
public double discount(double price) {
price = price * 0.8;
System.out.println("该商品价格为 " + String.valueOf(price));
System.out.println("我们将为你返回消费积分" + String.valueOf(price));
//Something else to do
return price;
}
}
再来跑一把测试,效果和上面一样:
好了至此我们的代码结构已经改成下图这样了:
看到了,SalePage是我们一开始的逻辑处理类,原来我们有啥修改时,只能跑到里面去找对应的逻辑分支去修改,或者扩展的时候再加一个if else分支。
修改以后,折扣策略一旦有啥修改,你只要跑到对应的Startegy类里面去修改了。如果用户等级扩展了,加一个类便是。你不会每次都去跑到SalePage里面去修改,减少了修改时出错的可能(不同用户处理逻辑已经隔离开了)。
参考资料:
<<设计模式--可复用面向对象软件的基础>>
网易设计模式公开课