我们在实际的开发过程中可能会遇到这样的情况,同一个业务逻辑,在不同的外在条件下,需要使用不同的算法来实现,如果这时把各种功能的算法都堆砌到同一个类中,会做各种判断,然后再实现不同的算法功能,这样的代码一定非显得非常庞杂。
策略模式就是针对复杂业务逻辑功能,实现特定算法的封装,就是底层算法的实现和客户端完全的解耦。这样业务逻辑无论做怎样的修改,客户端都不用关心。策略模式实质上就是封装了变化。
下面上代码分析,一个商场促销打折的例子。
业务背景:商场中的商品可能会根据不同的节日进行打折促销活动,本demo是根据商品实际的金额经过特定的促销策略后,得出一个最终的价格。
1、创建出打折策略的抽象接口,定义出策略的抽象行为。
public interface ICash {
public double getCashResult(double money);
}
2、根据不同过的打折策略,分别实现策略接口。
//不打折,正常收费的算法(策略)
public class CashNomal implements ICash {
@Override
public double getCashResult(double money) {
return money;
}
}
//打特定的折扣的算法(策略)
public class CashRebate implements ICash {
private double rate = 1;
public CashRebate(double rate) {
this.rate = rate;
}
@Override
public double getCashResult(double money) {
return money * rate;
}
}
//满减算法(策略)
public class CashReturn implements ICash {
private double moneyCondition = 0;
private double moneyReturn = 0;
public CashReturn(double moneyCondition,double moneyReturn){
this.moneyCondition = moneyCondition;
this.moneyReturn = moneyReturn;
}
@Override
public double getCashResult(double money) {
double result = money ;
if(money > moneyCondition){
result = (moneyCondition%money)*moneyReturn;
}
return result;
}
}
3、创建出上下文(有一种承上启下的作用),策略模式的关键,封装变化,对外只暴露统一接口
public class ContextStrategy {
private ICash cashStatery;
public ContextStrategy(ICash cashStatery){
this.cashStatery = cashStatery;
}
//对外暴露的统一接口,封装了策略
public double getFinalResult(double money){
return cashStatery.getCashResult(money);
}
}
4、测试代码
public static void main(String[] args) {
ICash cashStatery = null;
switch (statery) {//判断使用那种具体策略
case "正常收费":
cashStatery = new CashNomal();
break;
case "打7折":
cashStatery = new CashRebate(0.7);
break;
case "满300减100":
cashStatery = new CashReturn(300,100);
break;
default:
break;
}
//将策略传入上下文
ContextStrategy cxStatery = new ContextStrategy(cashStatery);
double result = cxStatery.getFinalResult(500);
}
5、优化,看到测试代码中还是需要使用switch来做各种判断,这样显得前端的代码特别繁琐,封装的不够彻底,应该把创建特定策略对象的任务封装起来,怎么办呢?可以引用简单工厂模式(上一篇博文,学以致用嘛),创建对象的工作交给工厂来做。客户端只需要传入特定的策略标识就OK。首先创建出工厂。
public class CashFactory {
//构造方法略,构造方法的作用是给策略使用到的一些数据赋值,比如满多少减多少等,为了方便下面直接传入了具体数值
public ICash getCashObj(String statery){
ICash cashStatery = null;
switch (statery) {//判断使用那种具体策略
case "正常收费":
cashStatery = new CashNomal();
break;
case "打折":
cashStatery = new CashRebate(0.7);
break;
case "满减":
cashStatery = new CashReturn(300,100);
break;
default:
break;
}
return cashStatery;
}
}
修改策略上下文中的代码:
public class ContextStrategy {
private ICash cashStatery;
public ContextStrategy(ICash cashStatery){
this.cashStatery = cashStatery;
}
//扩展了一种创建策略上下文的方式,直接传入策略标识,由工厂来创建出特定的策略对象
public ContextStrategy(String strategy){//传入具体的策略字符串
this.cashStatery = new CashFactory().getCashObj(strategy);
}
public double getFinalResult(double money){
return cashStatery.getCashResult(money);
}
}
如果这样修改的话,测试代码就可以这么写了:
public static void main(String[] args) {
ICash cashStatery = null;
//将策略标识传入上下文
String strategy = "满减";
ContextStrategy cxStatery = new ContextStrategy(strategy);
double result = cxStatery.getFinalResult(500);
}
6、总结:
- 策略模式是一种定义了一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
- 在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。
[注:学习模板为-大话设计模式 程杰]