网站建设属于移动互联网,网站建设步骤 高清教 程,公司网站建设的策划方案,angularjs 网站模版接上篇#xff0c;本篇将会介绍C设计模式中的Strategy 策略模式#xff0c;和上篇模板方法Template Method一样#xff0c;仍属于“组件协作”模式#xff0c;它与Template Method有着异曲同工之妙。 文章目录 1. 动机#xff08; Motivation#xff09;2. 代码演示Stra…接上篇本篇将会介绍C设计模式中的Strategy 策略模式和上篇模板方法Template Method一样仍属于“组件协作”模式它与Template Method有着异曲同工之妙。 文章目录 1. 动机 Motivation2. 代码演示Strategy 策略模式2.1 传统方法处理2.2 怎么用扩展的方式来支持未来的变化呢- Strategy 策略模式2.3 两种方法的对比分析 3. 模式定义4. 结构 Structure5. 要点总结6.其他参考博文 1. 动机 Motivation 在软件构建过程中某些对象使用的算法可能多种多样经常改变如果将这些算法都编码到对象中将会使对象变得异常复杂而且有时候支持不使用的算法也是一个性能负担。结合下面的代码如果算法只是在中国使用其他国家的算法就不会被使用到可能会占用内存资源因此也就是一种性能负担 如何在运行时根据需要透明地更改对象的算法将算法与对象本身解耦从而避免上述问题
2. 代码演示Strategy 策略模式
以下是一种税的计算比如在电子商务系统中常常需要进行订单中税的计算假如支持跨国结算就需要考虑不同国家税的计算方法不同。例如以下代码中中国、美国、德国之间的税率相差是很大的初始是没有法国的因此在代码中需要支持不同的税的计算方法。
2.1 传统方法处理
最简单的方法就是下面形式使用枚举类型if…elseswitch…case这样的组合来支持不同的税的计算。这种形式是我们更容易想到的初看起来也是没有问题的但是作为面向对象设计特别是学习过设计模式的应该有一种思维层次不要静态的去看一个软件的设计而是要动态的去看。用简单话来说就是要有时间轴的概念加上时间轴也就是考虑问题未来的一些变化的时候也是上面动机 Motivation讲到的未来会不会有可能支持法国假设有这个需求的时候就是以下完整的代码。但是这样的改动就违背了开放封闭原则即对扩展开放对更改封闭类模块尽可能用扩展的方式支持未来的变化而不是修改源代码来支持未来的变化。
enum TaxBase {CN_Tax,US_Tax,DE_Tax,FR_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***********}else if (tax FR_Tax){ //更改//...}//....}};
2.2 怎么用扩展的方式来支持未来的变化呢- Strategy 策略模式
以下代码不用枚举进行实现实现了一个TaxStrategy的基类内部有一个Calculate的纯虚方法以context作为形参取参数。对于不同的税法将第一种方法中的一个个的算法变成了TaxStrategy的子类。 SalesOrder类中放了一个多态指针TaxStrategy* strategy极特殊的情况下也是可以使用引用的但是引用还有其他毛病一般来讲要实现多态就是需要使用指针。 这个指针怎么去创建呢推荐使用后面会讲到的工厂模式的方式来创建此处先做简单的了解它不需要new一个实际对象硬编码而是使用外界传来的StrategyFactory调用一个NewStrategy()可以返回某个国家子类的对象具体返回哪个是由工厂决定的不是由真正本身类决定。这个对象是在工厂内部返回的也是一个堆对象而不是栈对象。 CalculateTax()中就需要构建上下文的参数调用double val strategy-Calculate(context); //多态调用这个地方是典型的多态可能调用某个国家的税法依赖于NewStrategy()返回的对象类型。 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{
public:virtual double Calculate(const Context context){//***********}
};class DETax : public TaxStrategy{
public:virtual double Calculate(const Context context){//***********}
};//扩展
//*********************************
class FRTax : public TaxStrategy{
public:virtual double Calculate(const Context context){//.........}
};class SalesOrder{
private:TaxStrategy* strategy;public:SalesOrder(StrategyFactory* strategyFactory){this-strategy strategyFactory-NewStrategy();}~SalesOrder(){delete this-strategy;}public double CalculateTax(){//...Context context();double val strategy-Calculate(context); //多态调用//...}};
此处再次强调 任何基类的析构函数必须是虚的那怕是你觉得析构函数不需要写编译器自动生成是够用的你也应该去写一个虚的析构函数否则多态的delete会出问题。 工程上不同的类是放在不同的文件中。
2.3 两种方法的对比分析
第二种方法相对于第一种方法有什么好处呢 只管来看功能一样但是要比较好处就要放到时间轴去看假设出现了需要支持法国的业务。
可以看到除了增加了法国的子类SalesOrder类中不需要做变动而法国对象怎样被弄进来就需要看StrategyFactory的选择。SalesOrder不用做变化在面向对象设计中也就说得到了复用性新增的法国的子类是一种扩展这种写法遵循了开放封闭原则。
面向对象特别是设计模式讲的复用性指的是编译单位也就是二进制层面的复用性一般认为源代码级别例如源码从一个地方拷贝到另一个地方这个不叫复用叫做粘贴源代码。真正的复用指的是你编译、测试、部署之后是原封不动是二进制意义的单位复用而不是源代码片段级的复用拷贝粘贴。
在一段代码下补一段代码很容易打破方法前面的代码给前面的代码引入bug这是开发工程学中经常会出现的因此对源代码级别的拷贝粘贴是不推荐的而且压根不能成为复用性。
第二种下才叫做二进制意义的复用性才满足开闭原则。
3. 模式定义
定义一系列算法把它们一个个封装起来并且使它们可互 相替换变化。该模式使得算法可独立于使用它的客户程 序(稳定)而变化扩展子类化 。 --《设计模式》 GoF
什么是互相替换就是支持变化。上面程序中SalesOrder独立于税法的变化SalesOrder是稳定的。
4. 结构 Structure 上图是《设计模式》GoF中定义的Strategy 策略模式的设计结构。结合上面的代码看图中最重要的是看其中稳定和变化部分也就是下图中红框和蓝框框选的部分。 5. 要点总结
Strategy及其子类为组件提供了一系列可重用的算法从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。
结合上面的代码这里运行时就指的是 this-strategy strategyFactory-NewStrategy();运行时传递多态的对象double val strategy-Calculate(context); //多态调用运行时支持多态的调用
Strategy模式提供了用条件判断语句以外的另一种选择消除条件判断语句就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
绝对不变的情况下是可以使用if…else的但是在实际应用需要扩展的情况就要使用Strategy模式
如果Strategy对象没有实例变量那么各个上下文可以共享同一个Strategy对象从而节省对象开销。
Strategy从某种层面来讲可以使用后面要讲的Single来设计的从而节省对象开销。例如中国的税法里面没有实例变量的话只创建全局一个对象就可以了
6.其他参考博文
Strategy 策略模式