一个阳光明媚的上午,靓仔正在开心的划水摸鱼,耳机里传来音乐“不是吧不是吧,难道单压也算压......”
产品经理突然出现在身后,拍了拍我的肩膀
产品经理:又在摸鱼,看来工作不饱和啊,正好有个需求你做一下。公司推出了会员制度,分普通会员和超级会员,普通会员购买商品打九折,超级会员购买商品打八折。
靓仔:就这?简单
public Double computePrice(String type, Double price) {
if ("VIP".equals(type)) {
// 普通会员9折优惠
return price * 0.9;
} else if ("S_VIP".equals(type)) {
// 超级会员8折优惠
return price * 0.8;
} else {
// 非会员无优惠
return price;
}
}
几天后。。。
产品经理:我们增加了活动送的一个月体验会员,与普通会员享受同等九折待遇,但是只能享受最高20的优惠金额。
靓仔:明白,不就是“穷逼vip”吗
于是就在原来的代码上改了改
public Double computePrice(String type, Double price) {
if ("VIP".equals(type)) {
// 普通会员9折优惠
return price * 0.9;
} else if ("S_VIP".equals(type)) {
// 超级会员8折优惠
return price * 0.8;
} else if ("BEGGAR_VIP".equals(type)) {
// 穷逼vip 9折优惠,最大优惠金额20
return price > 200 ? (price - 20) : price * 0.9;
}else {
// 非会员无优惠
return price;
}
}
有没有觉得 if-else 特别多,而且一旦再增加会员种类,那么看上去就更繁琐,代码耦合严重,维护起来十分不方便。
怎么办,重构一下代码呗
首先提取出价格计算的接口类
public interface PriceStrategy {
Double computePrice(Double price);
}
然后针对不同的会员类型,实现不同会员价格计算接口,提供算法
// 普通会员
public class VipPriceStrategy implements PriceStrategy{
@Override
public Double computePrice(Double price) {
return price * 0.9;
}
}
// 超级会员
public class SVipPriceStrategy implements PriceStrategy{
@Override
public Double computePrice(Double price) {
return price * 0.8;
}
}
// 穷逼会员
public class BeggarVipPriceStrategy implements PriceStrategy{
@Override
public Double computePrice(Double price) {
return price > 200 ? (price - 20) : price * 0.9;
}
}
增加一个上下文角色,封装算法对高层屏蔽,高层模块只用访问Context
public class PriceContext {
private PriceStrategy priceStrategy;
public PriceContext(PriceStrategy priceStrategy) {
this.priceStrategy = priceStrategy;
}
public Double computePrice(Double price) {
return priceStrategy.computePrice(price);
}
}
外部调用,计算价格
public Double getPrice(String type, Double price) {
PriceStrategy priceStrategy;
if ("VIP".equals(type)) {
priceStrategy = new VipPriceStrategy();
} else if ("S_VIP".equals(type)) {
priceStrategy = new SVipPriceStrategy;
} else if ("BEGGAR_VIP".equals(type)) {
priceStrategy = new BeggarVipPriceStrategy;
} else {
return price;
}
PriceContext priceContext = new PriceContext(priceStrategy);
return priceContext.computePrice(price);
}
没错,这就是策略模式
- 环境(Context):持有一个 Strategy 的引用。
- 抽象策略(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 具体策略(ConcreteStrategy):包装了相关的算法或行为。
有朋友可能会问了,这和工厂模式有什么区别吗?
我们再来看下工厂模式。
简单工厂模式:
看上去简直一摸一样吧。
其实工厂模式和设计模式一直给人一种错觉,总感觉是一样的,没有丝毫的区别。
直到我看到一个网友的解读:
一个注重的是实例的生产,一个注重的是策略方法。
好了,这个时候再来看我们的代码,好像越来越复杂了,虽然用策略模式将具体的算法都抽离出来了,但是 if-else 的问题还是没有解决啊
思考一下,我们可不可以结合以下工厂模式,来去掉烦人的 if-else
可以把策略对象初始化到一个 map 进行管理
public interface PriceStrategy {
Map<String, PriceStrategy> map = new ConcurrentHashMap(){{
put("VIP", new VipPriceStrategy());
put("S_VIP", new SVipPriceStrategy());
put("BEGGAR_VIP", new BeggarVipPriceStrategy());
}};
Double computePrice(Double price);
}
public class PriceContext {
private PriceStrategy priceStrategy;
public PriceContext(String type) {
this.priceStrategy = PriceStrategy.map.get(type);
}
public Double computePrice(Double price) {
return priceStrategy.computePrice(price);
}
}
外部调用
public Double computePrice(String type, Double price) {
PriceContext priceContext = new PriceContext(type);
return priceContext.computePrice(price);
}
舒服了,终于干掉了 if-else
策略模式优缺点
优点:
- 策略模式遵循开闭原则,实现代码的解耦合,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
- 算法的使用就和算法本身分开,符合单一职责原则
缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
- 策略模式可能会造成系统产生很多具体策略类
总结
其实我们在工作中使用设计模式的时候,不需要被条条框框所束缚,设计模式可以有很多变种,也可以结合几种设计模式一起使用,别忘了使用设计模式的初衷是什么,不要为了使用设计模式而使用设计模式。
END
往期推荐