常用设计模式之策略模式
创始人
2025-05-28 06:54:04

常用设计模式之策略模式

    • 策略模式
    • 代码实现

 面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。策略模式它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

策略模式

 试想这样一个场景,一个商场简单的商场收银系统,需要根据最开始只需要根据单价和数量来收钱就可以了,但是随着商场越来越大,需要不断地通过搞活动来促销,首先是打折活动,这时需要设置一个折扣值,所有的价格按这个折扣值来就可以了。后来又增加了一个活动,可以实现满多少减多少的活动,该如何设计这个系统呢?

 首先我们试一下不考虑设计模式,直接去实现:

 对于基本需求,似乎一个函数就可以实现了, 价格是多少就收多少就好了。

double charge(double unitPrice, double count) {return unitPrice*count;
}

 对于打折的需求,看上去加一个参数就可以了,

double charge(double unitPrice, double count, int rebate) {return unitPrice*count* rebate;
}

 对于满减的需求,再加两个参数

double charge(double unitPrice, double count, int rebate, int condition, int return) {...
}

 随着需求越来越多,这个函数要加的参数越来越多,里面的逻辑也会越来越复杂,导致开发人员和客户都非常头大,参数设置错了,就会出问题,功能耦合非常严重,拓展性极差。

 这时候就要用上设计模式了,根据这次的需求分析,我们需要一种能处理正常价格,打折价格,以及满减价格的系统,并且客户应该尽可能简单明了的去使用这个系统。策略模式就可以实现这个需求,先来看下策略模式的原理

  策略模式是一种定义一系列算法的方法,从概念上看,所有这些算法完成的都是相同的工作,只是实现不同,它可以用相同的方式调用所有的算法,减少各类算法类与使用算法类至之间的耦合[DPE]。策略模式的strategy类层次为Contenxt定义了一系列的可供重用的算法或行为,继承有助于提炼出这些算法中的公共功能[DP],并且策略模式简化了单元测试,每个算法都有自己的类,可以通过自己的接口单的测试[DPE]。

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

 根据我们的需求,我们可以根据策略模式,设计这样的一些类:

 以上图片来源于程杰的《大话设计模式》,需要的同学可以关注公众号程序员DeRozan,回复1207领取。

《C++ Primer》《Effective C++》是C++开发者必不可少的书籍,如果你想入门C++,以及想要精进C++开发技术,这两本书可以说必须要有。此外,《Linux高性能服务器编程》以及《Linux多线程服务端编程:使用muduo C++网络库》.(陈硕)》是快速提高你的linux开发能力的秘籍。《大话设计模式》可以增强我们的模型提取及设计能力,写出更优雅的代码。在网上搜索相关资源也要花费一些力气,需要的同学可以关注公众号【程序员DeRozan】,回复【1207】快速免费领取~

 设计一个抽象接口类,该类提供一个公共接口用来计算最终需要收银员最终要收多少钱,各个子类根据自己的需求,以不同的方式重写这这个接口,每个子类实现一个功能,根据需求,我们要实现三个子类,分别来处理 正常/打折/满减的情况。然后使用一个context类,持有一个抽象父类指针,根据不同的需求,使用不同的子类对象来初始化这个指针,context类提供一个接口,入参是原价,接口内调用子类的方法,计算出最终价格,下面直接看代码:

代码实现

#ifndef STRATEGY_H
#define STRATEGY_H
#include // 抽象基类,抽取一个公共接口
class AbstractStrategy
{
private:public:AbstractStrategy(){};virtual ~AbstractStrategy(){};virtual void CalculateMoney(double money)=0;
};//子类1  用来计算正常价格
class NormalCalculate: public AbstractStrategy {
public:void CalculateMoney(double money)override {printf("need charge %f money\n", money);}
};//子类2 计算打折后的价格
class RebateCalculate: public AbstractStrategy {
public: RebateCalculate(double rebate):m_rebate(rebate) {}void CalculateMoney(double money)override {printf("need charge %f money\n", money*m_rebate);} 
private: double m_rebate;
};//子类3 计算满减后的价格
class ReturnCalculate: public AbstractStrategy
{
private:int m_cond;int m_ret;
public:ReturnCalculate(int conditionMoney, int returnMoney) : m_cond(conditionMoney), m_ret(returnMoney){};~ReturnCalculate(){};void CalculateMoney(double money) override {if(money < m_cond) {printf("not satisfied condition\n");}else{int tmp = money / m_cond;printf("tmp=%d\n", tmp);int returnMoney = tmp*m_ret;printf("returnMoney=%d\n", returnMoney);money = money - static_cast(returnMoney);printf("need charge %f money\n", money);}}
};//context类,根据构造时传入的type参数不同,初始化不同的子类对象,提供一个chargeMoney接口,供使用这个系统
//的客户使用。
class Context{
public:Context(int type) {switch (type){case 1:m_strategy = new NormalCalculate();break;case 2:m_strategy = new RebateCalculate(0.6);break;case 3:m_strategy = new ReturnCalculate(300, 50);break;default:break;}}void chargeMoney(double money){m_strategy->CalculateMoney(money);}private:AbstractStrategy *m_strategy;
};#endif
#include 
#include "Strategy.h"
using namespace std;int main(int argc, char** argv)
{cout << "hello world!" << endl;Context cont(3);cont.chargeMoney(318.3);return 0;
}

 这样,客户只要创建一个context对象,指定活动类型,就可以直接得到价格了,上面代码还可以改进下,比如将打折的系数和满减条件作为参数,构造context类,这样就更灵活了。

相关内容

热门资讯

苹果新AI模型实现端侧GUI智... 【CNMO科技消息】近日,据外媒报道,苹果研究人员已成功开发出一款名为Ferret-UI Lite的...
春节去哪玩丨正月初三至初七 伊... 2月19日,农历正月初三,呼和浩特伊利健康谷2026年春节系列活动正式拉开帷幕。园区内营造出浓厚的节...
瓷房子张灯结彩 外国游客感受津... 央广网天津2月21日消息(记者褚夫晴)春节期间,春意盎然。天津瓷房子景区张灯结彩,年味浓郁。这座由七...