设计模式之Strategy策略模式

18次阅读

共计 2836 个字符,预计需要花费 8 分钟才能阅读完成。

1、动机与意图

动机:在软件构建的过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂,增加与改变现有算法可能带来严重的逻辑问题;而且有时候支持不使用的算法也是一个性能上的负担(具体上体现在代码太长会使得其在保存的位置出现问题),因此将对象与算法解耦,避免上述问题才是本算法动机。
意图:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而改变。

2、分析

示例代码:

#ifndef STRATEGY
#define STRATEGY
#include<iostream>
using std::cout;
using std::ends;
using std::endl;
class Tax_cal
{
public:
    Tax_cal();
    ~Tax_cal();
    virtual double get_tax(double source_money)const = 0;
private:

};
Tax_cal::Tax_cal()
{}
Tax_cal::~Tax_cal()
{}
class chinese_tax:public Tax_cal
{
public:
    chinese_tax();
    ~chinese_tax();
    double get_tax(double source_money) const
    {return source_money * 5.6/100 ;}
};
chinese_tax::chinese_tax()
{}
chinese_tax::~chinese_tax()
{}
class Japanese:public Tax_cal
{
public:
    Japanese();
    ~Japanese();
    double get_tax(double source_money)const
    {return source_money * 17 / 100;}
private:

};

Japanese::Japanese()
{
}

Japanese::~Japanese()
{
}
class bank_calcu 
{
public:
    bank_calcu(double);
    ~bank_calcu() {};
    void Calculate(const Tax_cal*);
private:
    double money;
};
bank_calcu::bank_calcu(double money) :money(money) {};
void  bank_calcu::Calculate(const Tax_cal* tax_demo) 
{cout << tax_demo->get_tax(money) << endl;
}
#endif // !STRATEGY

我们定义了一个客户端的类 bank_calcu 用于计算不同地方的税率,在成员函数 Calculate()中计算税率,但是不同的地方有不同的税率,譬如说代码中的中国与日本(当然数据是瞎编的),传统的方法是在 Calculate()内部写上一个 if-else 或者 switch 语句来进行区别,但是依旧存在问题:
1、用不到的代码块就应该被去除,否则影响执行效率;
2、违反了开闭原则 (对增加开放,对修改封闭);
3、容易出错,现在的逻辑还没有那么复杂,判断逻辑十分复杂的话,修改会造成严重的逻辑问题。
税率计算的过程我们定义一个抽象类 Tax_cal,使用其进行税率的具体计算,不同的计算规则在其子类中进行具体实现,通过这种方式能够有效地实现税率计算的功能。
运行代码为:

#include"strategy.h"
int main() 
{bank_calcu bc(16000);
    bc.Calculate(new chinese_tax());
    bc.Calculate(new Japanese());
    std::cin.get();}

结果为

Strategy 模式的优点主要为:
1、相关算法系列
Strategy 类层次为 bank_calcu 定义了一系列可供重用的算法与行为。继承有助于析取出这些算法中的公共功能。
2、一个代替继承的方法
在代码中我们还可以这样设计,也就是给 bank_calcu 设计派生类,每个派生类支持独有的算法,但是这样的派生类不仅存在大量的代码重复,也就是未做到有效地复用,也会带来难以拓展与难以维护的问题。将算法封装在 Strategy 类中有利于理解与扩展。
3、消除了一些条件语句
Strategy 提供了用条件语句选择所需的行为以外的另一种选择。
4、实现了选择
Strategy 模式可以提供相同的行为的不同实现。
其主要缺点为:
1、客户必须要了解不同的 Strategy
也就是说既然需要选择合适的 Strategy 就需要客户了解不同的 Strategy 的区别在哪,这可能需要将策略的具体实现过程暴露给了用户。
2、Strategy 与 bank_calcu 的通信开销
在本代码范例中 Strategy 的具体实现共享一个接口,但是实际情况下由于某些差异,入口参数并不是所有的派生类成员函数都需要的,但是这种方式能够降低两者之间的耦合度;另一种方法是将 bank_calcu 的引用作为参数传入,或者直接在 Strategy 存储一个 bank_calcu 引用,这样会造成一定程度的高耦合。
3、增加了对象的个数
Strategy 虽说消除了 bank_calcu 中的代码臃肿与难以扩展的问题,但是其代价就是需要大量的类。
4、一些小的 tip
在进行 Strategy 的选择的时候需要传入一个具体 Strategy 指针,但是还有其他的办法,也就是通过模板,修改后的 bank_calcu 可以通过模板选择:

template<class Tax_cal>
class bank_calcu 
{
public:
    bank_calcu(double);
    ~bank_calcu();
    void Calculate();
private:
    double money;
    Tax_cal* tax_type;// 保存一个 Abstrategy 指针
};
template<class Tax_cal>
bank_calcu< Tax_cal>::bank_calcu(double money) :money(money) 
{tax_type = new Tax_cal();// 给该指针分配内存
};
template<class Tax_cal>
bank_calcu< Tax_cal>::~bank_calcu() 
{delete tax_type;// 析构该内存};
template<class Tax_cal>
void  bank_calcu< Tax_cal>::Calculate()
{cout << tax_type->get_tax(money) << endl;
}
#endif // !STRATEGY

调用的过程为:

#include"strategy.h"
int main() 
{bank_calcu<chinese_tax> t1(16000);
    t1.Calculate();
    std::cin.get();}

这样调用起来就更加顺手了。

正文完
 0