1.组件协作模式
现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。
- Template Method
- Strategy
- Observer/Event
定义一系列算法,把他们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
-《设计模式》Gof
2.举例代码分析
下面是电子商务系统中订单里税的计算的伪代码,当支持跨国结算的计算场景,有中国,美国,德国三个国家;
enum TaxBase{
CN_Tax;
US_Tax;
DE_Tax;
};
class SalesOrder{
TaxBase tax;
public:
double CalculateTax(){
//...
if(tax == CN_Tax){
//CN...
}
else if(tax == US_Tax){
//US...
}
else if(tax == DE_Tax){
//DE...
}
//...
}
};
- 在实际软件开发中,需求不断的变化。倘若需要增加一个国家呢,譬如法国,枚举类增加了一个FR_Tax,而且还要编写一段else-if法国的税算法。于是问题来了…
- 代价–重新编译,重新测试,重新部署…
- 以上代码的设计违背了开放封闭原则,即对扩展开放,对更改封闭。
- 以下是改进的代码
class TaxStrategy{
public:
virtual double Calculate(const context& context)=0;
virtual ~TaxStrategy(){}
};
class CNTax:public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//***
}
};
class USTax:public TaxStrategy{...};
class DETax:public TaxStrategy{...};
class SalesOrder{
private:
TaxStrategy* strategy;
public:
SalesOrder(StrategyFactory* strategyFactory){
this->strategy = stategyFactory->NewStrategy;
}
~SalesOrder(){
delete this->strategy;
}
public double CalculateTax(){
Context context()l
double val = Strategy->Calculate(context);//多态调用
}
}
- 基类必须写一个虚的析构函数,不然多态的delete会出问题
- 规范时,应该把一个类放在一个文件里,采用多文件的方式,工程上的推荐做法
- 在SalesOrder类里实现多态变量,一般用指针,引用也可以用,但是会有一些问题,具体的老师也没有深挖
- 将前面的if-else里面一个国家的汇率转换成TaxStrategy一个一个的子类
- 采用了工厂模式—StrategyFactory,new出来不确定的Tax,返回类型由工厂模式确定。
- 增设法国,只需要编写一个法国的子类,调用代码不改变
“复用”面向对象与设计模式所讲的复用性所指的是编译单位,二进制层面的复用性。
3.结构图
4.要点总结
- Strategy及其子类为组件提供一系列可重用的算法,在运行时方便的根据需要在各个算法之间进行切换。
- 运行时进行多态的调用
- 提供了条件判断语句以外的另一种选择,解耦合的手法,出现条件判断语句时需要strategy的一种特征,《重构》里面提及的if-else和switch-case。
- if-else结构化分而治之的手法,但是可以用面向对象的抽象来解决问题。
- 时间轴的想法,即未来可能出现的第四种情况,第五种情况…这个就是典型的strategy模式。
- if-else里面的情况是绝对不变的时候,用这种模式。但是当业务频繁变化时,这个时候应该特别小心,“坏味道”使用策略模式应该小心。
- 代码具有良好的本地性
有时候支持不使用的算法也是一个性能负担—if-else很多的情况下,譬如只使用中国区的代码,但是其余的代码都装载内存里。
最好的执行模式(快):CPU的高级缓存>主存>硬盘