大话设计模式|大话设计模式 —— 第二章《策略模式》C++ 代码实现

目录
策略模式介绍
优点
缺点
使用场合
策略模式介绍 简单工厂模式只是解决对象的创建问题,而且由于工厂本身包括了所有的收费方式,商场可能经常性的更改打折额度和返利额度,每次维护或者扩展收费方式都要改动这个工厂,以致代码需要重新编译部署,这不是一种好方法。而且为了创建不同的对象产品使用了switch case(或if else)的形式实现代码,这样违背了开闭原则,即对扩展开放、对修改封闭,维护的成本会随着cese(或else)的增加而增加,而本文的策略模式能较好地解决这个问题。

策略模式是处理算法不同变体的一种行为型模式。,策略模式通过接口或抽象类提供公共的函数接口,即在抽象类中定义一个抽象方法,实现该接口的派生类将实现该抽象类中的抽象方法。策略模式把针对不同派生类一系列的具体算法分别封装在不同的派生类中,使得各个派生类给出的具体算法可以相互替换。
在策略模式中,抽象类提供公共的函数接口称作策略,实现该接口的派生类称作具体策略。

大话设计模式|大话设计模式 —— 第二章《策略模式》C++ 代码实现
文章图片

  • Strategy (抽象算法接口): 定义所有算法的公共函数接口(AlgorithmInterface)。Context 使用这个接口去调用 其派生类定义的具体算法。
  • 具体策略(ConcreteStrategy):具体策略是实现策略接口的类。具体策略实现策略接口所定义的抽象方法,即给出算法标识的具体算法。
  • Context : 用来维护 Strategy的派生类不同对象的不同算法实现。
  • Strategy是使用接口还是抽象类,这个取决于一系列的策略中是否有共同属性或方法;如果没有,使用接口更加灵活方便,反之使用抽象类,抽象类中便可存放公共的属性以及方法。

下面是通常的策略模式的实现:
#include using namespace std; class CashSuper//现金收费抽象类 { public: virtual double acceptCash(double money) = 0; virtual ~CashSuper() = default; }; class CashNormal :public CashSuper//子类:正常(原价)付费类型 { public: explicit CashNormal() = default; double acceptCash(double money) override { return money; } }; class CashRebate :public CashSuper//打折收费子类 { private: double m_moneyRebate = 1.0; public: explicit CashRebate(double rebate) :m_moneyRebate(rebate)//构造函数初始化成员数据 { } double acceptCash(double money) override { return money * m_moneyRebate * 0.1; //返回:原价*折扣=现价 } }; class CashReturn : public CashSuper//返利收费子类 { private: //返利收费,初始化时必须输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneReturn为100 double m_moneyCondition = 0.0; //返利条件数据成员 double m_moneReturn = 0.0; //返回值 数据成员 public: //构造函数初始化返利条件和返利值,用的是初始值列表的形式进行初始化 explicit CashReturn(double condition, double returnNum) :m_moneyCondition(condition), m_moneReturn(returnNum) { } double acceptCash(double money) override { double result = money; if (money >= m_moneyCondition)//若收正常价钱时。刚好大于等于返利条件,计算返利后的价钱 { result = money - floor(money / m_moneyCondition)*m_moneReturn; } return result; //返回计算返利后的价钱 }}; //#include "CashSuper.h" class CashContext { private: CashSuper *m_cs = nullptr; public: explicit CashContext(CashSuper *cs) { m_cs = cs; } double getResult(double money) { return m_cs->acceptCash(money); //利用基类指针调用虚函数形成多态 } }; int main() { CashSuper *cs = new CashRebate(0.8); CashContext *cc = new CashContext(cs); double money = 1000; cout << cc->getResult(money) << endl; money = 100; cs = new CashNormal(); cc = new CashContext(cs); cout << cc->getResult(money) << endl; delete cs; cs = nullptr; delete cc; cc = nullptr; system("pause"); return 0; }

优点
  • Context 和具体策略(各个派生类)是松耦合关系。因此Context 只需要知道它要使用某一个派生类的实例,但不需要知道具体是哪一个派生类。
  • 该策略模式满足“开-闭原则”。当增加新的具体策略时,不需要修改上下文类的代码,Context 就可以引用新的具体策略( 派生类)的实例。
  • 避免使用多重条件判断。
  • 方便拓展和增加新的算法(规则)。
  • 消除了一些if else条件语句 :Strategy模式提供了用条件语句选择所需的行为以外的另一种选择。当不同的行为堆砌在一个类中时 ,很难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的Strategy类中消除了这些条件语句。含有许多条件语句的代码通常意味着需要使用Strategy模式。
大话设计模式上总结的优点:
  • 策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,他可以以相同的方式调用所有的算法,如打折和返利都是为了完成计算价格的工作。它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。

  • 策略模式的Strategy类曾是为Context定义了一些列的可供重用的算法或行为。集成有助于析取出这些算法中的公共功能。
  • 策略模式简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。

  • 策略模式就是用来封装算法的,但在实践中,我们可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

  • 简单工厂模式需要让客户端认识两个类,而策略模式和简单工厂模式结合的用法,客户端只需要认识一个类Context即可。

  • 策略模式是为了适应不同的需求,只把变化点封装了,这个变化点就是实现不同需求的算法,但是,用户需要知道各种算法的具体情况。 就像上面的打折返利等活动,不同的打折返利情况,有不同的算法。我们不能在程序中将计算打折返利的算法进行硬编码,而是能自由的变化的。这就是策略模式。
缺点
  • 客户端必须知道所有的策略类,以及具体策略的使用方式,并自行决定使用哪一个策略类: 本模式有一个潜在的缺点,就是一个客户要选择一个合适的Strategy就必须知道这些Strategy到底有何不同。此时可能不得不向客户暴露具体的实现问题。因此仅当这些不同行为变体与客户相关的行为时 , 才需要使用Strategy模式。

  • Strategy和Context之间的通信开销 :无论各个ConcreteStrategy实现的算法是简单还是复杂, 它们都共享Strategy定义的接口。因此很可能某些 ConcreteStrategy不会都用到所有通过这个接口传递给它们的信息;简单的 ConcreteStrategy可能不使用其中的任何信息!这就意味着有时Context会创建和初始化一些永远不会用到的参数。如果存在这样问题 , 那么将需要在Strategy和Context之间更进行紧密的耦合。

  • 随着具体策略的增加,策略模式将造成产生很多策略类:可以通过使用享元模式在一定程度上减少对象的数量。 增加了对象的数目 Strategy增加了一个应用中的对象的数目。有时你可以将 Strategy实现为可供各Context共享的无状态的对象来减少这一开销。任何其余的状态都由 Context维护。Context在每一次对Strategy对象的请求中都将这个状态传递过去。共享的 Strategy不应在各次调用之间维护状态。
使用场合 当存在以下情况时使用Strategy(策略)模式:
  • 许多相关的类仅仅是实现的算法有异时。“策略”提供了一种用多个方法中的一个方法来配置一个类的方法;
  • 需要使用一个算法的不同变体;例如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。
  • 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构;
  • 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以替代这些条件语句。
  • 在某些设计中,一个类的设计人员经常可能涉及这样的问题:由于用户需求的变化,导致经常需要修改类中某个方法的方法体,即需要不断地变化算法。在这样的情况下可以考虑使用策略模式。
下面的代码是 策略模式和简单工厂模式的结合C++代码:
#include using namespace std; class CashSuper//现金收费抽象类 { public: virtual double acceptCash(double money) = 0; virtual ~CashSuper() = default; }; class CashNormal :public CashSuper//子类:正常(原价)付费类型 { public: explicit CashNormal() = default; double acceptCash(double money) override { return money; } }; class CashRebate :public CashSuper//打折收费子类 { private: double m_moneyRebate = 1.0; public: explicit CashRebate(double rebate) :m_moneyRebate(rebate)//构造函数初始化成员数据 { } double acceptCash(double money) override { return money * m_moneyRebate * 0.1; //返回:原价*折扣=现价 } }; class CashReturn : public CashSuper//返利收费子类 { private: //返利收费,初始化时必须输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneReturn为100 double m_moneyCondition = 0.0; //返利条件数据成员 double m_moneReturn = 0.0; //返回值 数据成员 public: //构造函数初始化返利条件和返利值,用的是初始值列表的形式进行初始化 explicit CashReturn(double condition, double returnNum) :m_moneyCondition(condition), m_moneReturn(returnNum) { } double acceptCash(double money) override { double result = money; if (money >= m_moneyCondition)//若收正常价钱时。刚好大于等于返利条件,计算返利后的价钱 { result = money - floor(money / m_moneyCondition)*m_moneReturn; } return result; //返回计算返利后的价钱 }}; //#include "CashSuper.h" class CashContext { private: CashSuper *m_cs = nullptr; public: explicit CashContext(int type = 1, double num1 = 0, doublenum2 = 0) { switch (type) { case 1: m_cs = new CashNormal; break; case 2: m_cs = new CashReturn(num1, num2); break; case 3: m_cs = new CashRebate(num1); break; default: break; } } double getResult(double money) { return m_cs->acceptCash(money); //利用基类指针调用虚函数形成多态 } }; int main() { while (true) { { cout << "\n*************************************************************" << endl << "********************收银软件功能展示.**********************" << endl << "*****************************************************************" << endl << "********************选择1——正常收费.**********************" << endl << "********************选择2——折扣收费.**********************" << endl << "********************选择3——返利收费.**********************" << endl << "********************选择4——清屏.**********************" << endl << "********************选择0——退出程序!**********************" << endl << "*****************************************************************" << endl; } cout << "\n********************请输入你想要使用的软件功能的序号***************" << endl; cout << "请输入你的选择:"; int userChoice(0); if (cin >> userChoice && userChoice == 0 ) { cout << "程序已退出,感谢您的使用!" << "\n" << endl; break; }CashContext *userMoney = nullptr; switch (userChoice) {case 1: { userMoney = new CashContext(1); //正常收费类型double totalPrices = 0; cout << "请输入产品的价格:"; double prices; cin >> prices; cout << "请输入产品的个数:"; intproductNumber = 0; cin >> productNumber; totalPrices = (userMoney->getResult(prices))*productNumber; cout << "正常收费为:" << totalPrices << "元! " << endl; break; } case 2: { double totalPrices = 0.0; double total = 0.0; cout << "请输入单个产品的价格:"; double prices = 0.f; cin >> prices; cout << "请输入产品的个数:"; intproductNumber = 0; cin >> productNumber; int discount = 0; cout << "请输入你想打多少折(只能输入1——9):"; while (cin >> discount) { if (discount > 0 && discount < 10) { break; } else { cout << "错误!请输入正确的打折范围(1-9),否则无法进行计算!" << endl; continue; } }userMoney = new CashContext(3, discount); total = prices * productNumber; totalPrices = (userMoney->getResult(total)); cout << "打折完后收费为:" << totalPrices << "元! " << endl; break; } case 3: { double total = 0.0; double totalPrices = 0.0; double rebates = 0.0; cout << "请输入返利多少钱:"; cin >> rebates; cout << "请输入产品的价格:"; double prices; cin >> prices; cout << "请输入产品的个数:"; intproductNumber = 0; cin >> productNumber; total = prices * productNumber; // 先计算不打折扣的总额{ userMoney = new CashContext(2, total, rebates); //打折收费类型 totalPrices = userMoney->getResult(total); } cout << "返利后收费为:" << totalPrices << "元! " << endl; break; } case 4: system("cls"); cout << "屏幕已经清屏,可以重新输入!" << "\n" << endl; break; default: cout << "输入的序号不正确,请重新输入!" << "\n" << endl; } delete userMoney; userMoney = nullptr; } system("pause"); return 0; }

【大话设计模式|大话设计模式 —— 第二章《策略模式》C++ 代码实现】

    推荐阅读