C++设计模式 - 策略模式

2022-01-18 16:00:19 浏览数 (2)

策略模式

策略模式定义多种处理同一种场景的不同算法,这些算法可在不影响用户的情况下互相替换。

场景

应用场景

某会员制商场有两种级别会员:银卡会员、黄金会员,分别享有9折、8折购物优惠。同时不同的会员用户在生日当天购物,会赠送不同的生日礼物。

分析

场景比较简单,需要解决的问题是区分不同类型的顾客享有不同的权益(折扣和生日礼物)。

按照平常的编码习惯,通常会在需要区分用户等级的业务上加上if判断,实现不同等级顾客应享有的不同权益。这种方案能快速的解决实际问题,但是随着业务需要,商场又要引进更高逼格的会员类型,譬如白金会员、钻石会员等。此时,需要在散落在各处的业务代码上加上if补丁。这种做法会引来如下问题:

  • 业务代码散落各处,容易出现漏加if的情况,又难以验证。
  • 时间推移,if会越来越多,导致分支变多,代码走向模糊,影响代码维护。

解决方案

引入策略模式,将用户等级抽象,分别定义出用户所有的行为,不同等级的用户各自实现该等级应享有的权益。

类图

策略模式策略模式

CShop:商场类。实现不同等级顾客的折扣结算、生日礼物等。持有CConsumer指针,根据需要指向具体的顾客实例(CCommonUser、CSilverUser、CGoldUser)。

CConsume:顾客抽象类。定义顾客所有的行为接口。

CCommonUser、CSilverUser、CGoldUser:具体顾客类。不同等级顾客实现有差异部分的接口。

效果

执行效果

代码语言:c 复制
$ ./exe 

---------------------------------
 All Cost      : 1000.00.   
 User Type     : Common User.      
 Discount      : 1.00.   
 Actual Payment: 1000.00.   

---------------------------------
 All Cost      : 1000.00.   
 User Type     : Silver User.      
 Discount      : 0.90.   
 Actual Payment: 900.00.   

---------------------------------
 All Cost      : 1000.00.   
 User Type     : Gold User.      
 Discount      : 0.80.   
 Actual Payment: 800.00.   

客户端实现

代码语言:c 复制
int main(int argc, char *argv[])
{
    CShop theShop;
    float cost = 1000.0;

    // 普通用户
    MAIN_LOG("n---------------------------------n");
    MAIN_LOG(" All Cost      : %0.2f.   n"
             " User Type     : %s.      n"
             " Discount      : %0.2f.   n"
             " Actual Payment: %0.2f.   n",
             cost,
             theShop.GetUserDesc().c_str(),
             theShop.GetCurrentDiscountRate(),
             theShop.GetRealPrice(cost));


    // 切换白银会员
    MAIN_LOG("n---------------------------------n");
    theShop.SetConsumer(COSUMER_SILVER);
    MAIN_LOG(" All Cost      : %0.2f.   n"
             " User Type     : %s.      n"
             " Discount      : %0.2f.   n"
             " Actual Payment: %0.2f.   n",
             cost,
             theShop.GetUserDesc().c_str(),
             theShop.GetCurrentDiscountRate(),
             theShop.GetRealPrice(cost));

    // 切换黄金会员
    MAIN_LOG("n---------------------------------n");
    theShop.SetConsumer(COSUMER_GOLD);
    MAIN_LOG(" All Cost      : %0.2f.   n"
             " User Type     : %s.      n"
             " Discount      : %0.2f.   n"
             " Actual Payment: %0.2f.   n",
             cost,
             theShop.GetUserDesc().c_str(),
             theShop.GetCurrentDiscountRate(),
             theShop.GetRealPrice(cost));

    return 0;
}

总结

  • 策略模式的实现原理比较简单,主要是改变持有指针的指向,即可实现不同方案的切换。更改为外部条件输入匹配对应的实例,便可以做到客户端代码无需改变自动切换不同方案的效果。
  • 满足开闭原则。当需增加策略时,只需要派生一个新的策略即可,而无需修改现有代码。相对以前的做法,更加安全快捷。
  • 在代码运行时,也可以动态切换策略。
  • 策略模式命令模式有点相像。两者的实现方式类似,应对的场景不同。策略模式针对的是同一个行为不同的算法实现;命令模式针对是一个命令对应一个解决的方法。
  • 全部源码可在公众号后台输入标题获取。

源码

商店类接口

代码语言:c 复制
class CShop
{
public:
    CShop();

    ~CShop();

    std::string GetUserDesc() { return mConsumer->mUserDesc; }

    float GetRealPrice(float price);

    int BirthdayPresent();

    int SetConsumer(EConsumerType type);

    void SetCurrentDiscountRate(float rate);

    float GetCurrentDiscountRate();

private:
    CConsumer* mConsumer;
};

更新顾客类型

代码语言:c 复制
int CShop::SetConsumer(EConsumerType type)
{
    switch (type)
    {
        case COSUMER_COMMON:
            mConsumer = CCommonUser::GetInstance();
        break;

        case COSUMER_SILVER:
            mConsumer = CSilverUser::GetInstance();
        break;

        case COSUMER_GOLD:
            mConsumer = CGoldUser::GetInstance();
        break;

        default:
        break;
    }

    if (NULL == mConsumer) {
        return -1;
    }

    return 0;
}

顾客类抽象接口

代码语言:c 复制
class CConsumer
{
public:
    float mDiscountRate;
    std::string mUserDesc;

    CConsumer() : mDiscountRate(1.0)
    {

    }

    virtual ~CConsumer()
    {

    }

    void SetDiscountRate(float rate)
    {
        mDiscountRate = rate;
    }

    float GetDiscountRate()
    {
        return mDiscountRate;
    }

    float GetRealPrice(float price)
    {
        return mDiscountRate * price;
    }

    virtual int GetBirthdayPresent() = 0;
};

具体顾客类:黄金会员

代码语言:c 复制
class CGoldUser : public CConsumer
{
public:
    CGoldUser();

    ~CGoldUser();

    static CGoldUser* GetInstance();

    int GetBirthdayPresent();
};

客户端接口

代码语言:c 复制
int main(int argc, char *argv[])
{
    CShop theShop;
    float cost = 1000.0;

    // 普通用户
    MAIN_LOG("n---------------------------------n");
    MAIN_LOG(" All Cost      : %0.2f.   n"
             " User Type     : %s.      n"
             " Discount      : %0.2f.   n"
             " Actual Payment: %0.2f.   n",
             cost,
             theShop.GetUserDesc().c_str(),
             theShop.GetCurrentDiscountRate(),
             theShop.GetRealPrice(cost));


    // 切换白银会员
    MAIN_LOG("n---------------------------------n");
    theShop.SetConsumer(COSUMER_SILVER);
    MAIN_LOG(" All Cost      : %0.2f.   n"
             " User Type     : %s.      n"
             " Discount      : %0.2f.   n"
             " Actual Payment: %0.2f.   n",
             cost,
             theShop.GetUserDesc().c_str(),
             theShop.GetCurrentDiscountRate(),
             theShop.GetRealPrice(cost));

    // 切换黄金会员
    MAIN_LOG("n---------------------------------n");
    theShop.SetConsumer(COSUMER_GOLD);
    MAIN_LOG(" All Cost      : %0.2f.   n"
             " User Type     : %s.      n"
             " Discount      : %0.2f.   n"
             " Actual Payment: %0.2f.   n",
             cost,
             theShop.GetUserDesc().c_str(),
             theShop.GetCurrentDiscountRate(),
             theShop.GetRealPrice(cost));

    return 0;
}

0 人点赞