策略模式详解
最近公司培训聊到策略模式,打算记录下自己对策略模式的理解。先说下策略模式的概念:
- 策略模式是一种定义一系列算法的方法,把使用算法的责任与算法本身分隔开,委派给不同的对象管理。
一般一个新概念出现都是为解决某个问题而诞生的,那策略模式是解决什么问题呢?最常见的就是平常我们在实际开发中需要做一些逻辑判断,而如果写的代码有很多if-else会让代码显得很臃肿,策略模式解决了这个问题。下面通过一个例子来详细理解策略模式:
场景:
一个商场收银软件,营业员根据客户所购买商品的单价和数量,向客户收费。
先来看个简单的例子:
// 声明一个变量计算总计
double total = 0.0d;
// 计算每个商品的价格
double totalPrice = 0.0d;
String[] cbxType = {"正常收费", "打八折", "打七折"};
public void calculate(String cbxType, double price, double num){
switch (cbxType){
case "正常收费":
totalPrice = price * num;
break;
case "打八折":
totalPrice = (price * num) * 0.8;
break;
case "打七折":
totalPrice = (price * num) * 0.7;
break;
}
total = total + totalPrice;
System.out.println("总计: "+ total+ "单价: "+ price+ "数量: "+ num);
}
public static void main(String[] args) {
ClientMain p1 = new ClientMain();
p1.calculate("打八折",100,8);
}
如果该商场需要增加新的打折优惠,那我们需要重新修改String数组中的代码和Switch中的条件,并且如果该商场又出其他优惠政策,判断的条件会越来越多。这样的方式完全是面向过程的思想写出的代码。
一个优秀的项目不会有特别强的耦合度,设计模式中对修改封闭对扩展开放的概念解决了这个问题。下面来用面向对象的思想结合策略模式来写该程序:
整体结构:
超类
策略1:
策略2:
策略3:
public class CashReturn extends CashSpuer{
// 达到返利条件值
public double moneyCondition = 0.0d;
// 返利值
public double moneyReturn = 0.0d;
public CashReturn(double moneyCondition, double moneyReturn){
this.moneyCondition = moneyCondition;
this.moneyReturn = moneyReturn;
}
@Override
public double acceptCash(double money) {
double result = money;
if(money > moneyCondition){
result = money - (money / moneyCondition) * moneyReturn;
}
return result;
}
}
这里通过构造方法,传入具体的收费策略:
public class CashContext {
static CashSpuer cs = null;
public CashContext(String type){
switch (type){
case "正常收费":
CashNormal cn = new CashNormal();
cs = cn;
break;
case "满300返100":
CashReturn cr = new CashReturn(300,100);
cs = cr;
break;
case "打8折":
CashRebate cb = new CashRebate(0.8);
cs = cb;
break;
}
}
public double GetResult(double money){
return cs.acceptCash(money);
}
测试,可以看到实际操作的类只认识一个类就好了,如果再增加其他收费策略,我们只需扩展一个类并继承CashSuper父类就可以了,实现了对修改封闭对扩展开发的策略:
总结:策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。我们发现策略模式只是减少了具体实现类对象的压力,在CashContext依然有if判断,但没办法有需求就要改呀,任何需求的变更都是需要成本的。而且我们也要注意,不要为了技术而技术,面向对象的编程不是类越多越好,如果只是一个小需求还要用设计模式那就得不偿失了。类的划分是为了封装,分类的基础是抽象。