Strategy模式
《设计模式》中的三个原则:
1、针对接口编程而不是实现
2、优先使用对象组合而不是类继承
3、考虑设计中哪些是可变的,对变化的概念进行封装。
例:国际电子商务系统案列研究
1、最初需求
系统处理来自不同国家的订单。总框架中有一个控制器TaskController对象,处理销售请求时,将请求转发给SalesOrder对象进行处理。
SalesOrder对象的功能包括:
1、允许顾客通过GUI填写订单;
2、处理税额计算;
3、处理订单,打印销售收据
有些功能要借助其他对象实现。比如SalesOrder对象没有必要自己打印,它的作用是充当一个存放销售订单的容器。具体的SalesOrder对象可以调用SalesTicket对象来打印。
2、处理新需求
需要修改处理税额的方法。比如处理美国以外的客户的订单税额。
一般来说处理概念相同但不同实现的任务有哪些方案?
- 复制粘贴【×,不利于修改和debug】
- switch和if【×,条件或情况太多则不适用】
- 函数指针或委托【×,C++中的函数指针和C#的委托,但是无法维持每个对象的状态,使用受限】
- 继承(子类用新的方式处理)【×,太深的继承层次导致多概念耦合(这里的继承指直接把包含需要改变的功能的类整个使用继承)】
- 将整个功能委托给新的对象
遵循开头列出的三个原则,应该这样做:
1、寻找变化,并将它封装到一个单独的类中
2、将这个类包含在另一个类中
创建CalcTax对象,为完成税额计算定义接口,由它派生所需的特定版本。
此处我把原书上的代码按自己的理解微调了一下。
public class TaskController{
private CalcTax myTax;//根据用户GUI填写信息设置myTax到底是USTax还是CanTax,如果这个参数是直接和SalesOrder打交道的话,直接转到SalesOrder里即可。
private SalesOrder so;
public void process(){
so.calcTax(myTax,itemSold,qty,price);//信息都由GUI得到
}
}
public class SalseOrder{
pubic double calcTax(CalcTax myTax,Salable itemSold, int qty, double price){
myTax.taxAmount(itemSold, qty, price);
}
public abstract class CalcTax{
abstract public double taxAmount(Salable itemSold, int qty, double price);
}
public class CanTax extends CalcTax{
public double taxAmount(Salable itemSold, int qty, double price){
//some code
}
}
public class USTax extends CalcTax{
public double taxAmount(Salable itemSold, int qty, double price)
//some code
}
这样我们就不必要用继承实现SalesOrder的多态,而只要实现变化部分的多态。
Strategy模式
定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换,Strategy模式使算法可独立于使用它的客户而变化。
Strategy模式以下列几条原则为基础:
1、对象具有职责
2、职责的不同实现是通过多态完成的
3、概念上相同的算法不同的实现,需要进行管理
Strategy模式 | 关键特征 |
---|---|
意图 | 可以根据上下文使用不同的业务规则或算法 |
问题 | 对于所需的算法取决于发请求的用户或者要处理的数据。如果是固定不变的算法就不需要使用 |
解决方案 | 对于算法的选择和算法的实现相分离。允许根据上下文进行选择。 |
参与者与协作者 | 1、Strategy指定了如何使用不同的算法 2、各ConcreteStrategy实现了这些不同的算法 3、Context通过类型为Strategy的引用使用具体的ConcreteStrategy。Strategy与Context(可以看做获得GUI的数据)相互作用以实现所需算法。Context将来自用户的请求转发给Strategy。 |
效果 | 1、Strategy模式定义了一系列的算法 2、可以不使用switch和if 3、必须以相同的方式调用所有算法(多态) |
实现 | 让使用算法的类包含一个抽象类,抽象类有一个抽象方法指定如何调用算法,子类按需求实现具体算法。 |
参考《设计模式解析》第二版