我们如何看待设计模式?
设计模式需要站在时间轴上来看待,需要看到未来。如果一个系统是静态的,那我们也不需要设计模式,毕竟引入了某种设计模式之后,系统的复杂性以及可理解性就会大打折扣。
策略模式
在软件设计的过程中,某些对象使用的算法可能多种多样,经常会发生改变,如果将这些算法都硬编码到对象中,通过 if…else… 或者 switch…case… 来进行管理,将会使对象变得臃肿。
策略模式要解决的问题就是如何在运行时根据需要透明地更改对象所使用的算法?将对象与算法解耦,从而避免上述问题。
场景模拟
例如我们有一个零售销售品工单的场景,工单里面需要计算税收,于是就存在按照不同国家税收政策进行税收计算的工单。
一开始我们用枚举来标识不同国家的税务类型,用switch…case… 来进入不同的算法分支进行税务计算,代码如下。
#ifndef _SALES_ORDER_H_
#define _SALES_ORDER_H_
enum TaxType{
CN_TAX,
US_TAX,
UK_TAX
//....如果增加税收品种就在这里扩展
};
class SalesOrder{
public:
SalesOrder():m_iTaxTyoe(-1){}
SalesOrder(TaxType taxtype):m_iTaxTyoe(taxtype){}
void setTaxTye(TaxType taxtype)//设置税收政策
{
m_iTaxTyoe = taxtype;
}
void calculateTax()
{
switch (m_iTaxTyoe)
{
case CN_TAX:
/* 中国税务算法 */
break;
case US_TAX:
/* 美国税务算法 */
break;
case UK_TAX:
/* 英国税务算法 */
break;
//如果税收种类增加,则在这里增加case...
default:
/*未定义*/
break;
}
}
//....
private:
int m_iTaxTyoe;
};
#endif
实际上,如果我们业务是静态的不需要再进行业务扩展变化的话,这样写也就足够了。
但实际上公司要发展,业务要拓展,后面我们就可能会引入德国的税收算法、日本的税收算法等等。那时候如果我们需要扩展我们的代码的话,就需要在枚举下面新增,以及我们的**switch…case…**下面新增对应case。
我们梳理一下思路,上述场景变化的地方在于税收算法,我们工单类与算法存在依赖关系。于是我们可以把税率算法提取出来一个税率算法抽象类,让工单类依赖于这个税率算法抽象类。UML图大致如下:
代码实现(C++)
工单类:
/*filename:SalesOrder.h*/
#ifndef _SALES_ORDER_H_
#define _SALES_ORDER_H_
#include "ITaxalgorithm.h"
class TaxContext{};//税率计算上下文
class SalesOrder
{
public:
SalesOrder(ITaxalgorithm* pTax = nullptr):m_pTax(pTax){}
~SalesOrder(){}
void setTaxType(ITaxalgorithm* pTax)
{
m_pTax = pTax;
}
void calculateTax()
{
TaxContext context;
if(m_pTax)
{
m_pTax->calculate(context);//多态调用
}
}
private:
ITaxalgorithm* m_pTax;//税率算法基类指针
};
#endif
税率算法抽象类:
/*filename:ITaxalgorithm.h*/
#ifndef _ITAXALGORITHM_H_
#define _ITAXALGORITHM_H_
class TaxContext;//税率计算上下文
class ITaxalgorithm{
public:
virtual void calculate(const TaxContext & context);
virtual ~ITaxalgorithm();
};
#endif
具体算法类:
/*filename:CNTax.h*/
#ifndef _ITAXALGORITHM_H_
#define _ITAXALGORITHM_H_
class TaxContext;//税率计算上下文
class ITaxalgorithm{
public:
virtual void calculate(const TaxContext & context);
virtual ~ITaxalgorithm();
};
#endif
使用,构造一个以中国税率算法计算的工单:
/*filename:main.cc*/
#include "CNTax.h"
#include "SalesOrder.h"
int main()
{
CNTax cntax;
SalesOrder order;
order.setTaxType(&cntax);
order.calculateTax();
return 0;
}
总结:
可以看出,设计模式主要还是抓住系统中的变与不变,以时间轴来看,把将来可能会变动的地方剥离出来,提取出一个抽象类,让其他组件来依赖这个抽象类而不是具体的某个实现,从而达到模块解耦的目的。
注意:
我们这里说的依赖若不做特殊说明皆是指编译时依赖,而非运行时依赖。