设计模式-策略模式

Posted 哇小明

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式-策略模式相关的知识,希望对你有一定的参考价值。

序言


设计模式是编程所要掌握的重要技能,其实这之前这些模式都看过相关例子源代码,但是时间一久,每次别人问起,什么是策略模式,或者其他。总是说不清道不明,后来干脆说我忘记了。后来仔细想来,其实我当初根本不知道什么是策略模式,只是看了定义,看了软件结构,看了例子代码。然而这些远远不够,前辈们总结下来的精华,怎么可能在短短几篇博客或者几页书就能完全掌握呢。深挖其中的精髓,并且能够自己设计出来,才应该是真正的掌握。固然为了模式,而去强行写设计模式,这听起来似乎有点不妥,但是强行写设计模式都写不出来,何谈你懂的设计模式呢。


定义


封装一系列算法,并在内部实现可以互相替换,可以让算法独立于使用者而变化。简单点说就是:一家商场,会针对客户的消费等级比如普通客户,高级客户,vip客户对商品进行不同的打折情况。打折情况就是算法的封装,比如vip打折80%,普通客户打折95%。


软件结构


Strategy 类作为算法的一个容器,也就是所有具体算法的父类,定义一系列通用接口,一般这个类为虚基类,算法接口定义为纯虚函数,实例话必须通过子类来实现。

CustomerClient这个类事面向用户的客户端,这个类的构造函数需要传递一个Strategy 类型的参数,一般这里都会把具体要调用的算法子类的实例作为参数,传进去。原因就是会在doAction中去调用访问子类算法的具体方法。

ConcretStrategy 为具体的算法实现类,一般都会有多个存在,不同的算法子类可以算是一个策略。这里面会实现父类的公共接口。


简单的代码框架实现:

/*
*策略封装类,必须定义一个纯虚公共接口,子类中需要实现这个接口。
*/
class Strategy
public:
    virtual ~Strategy()
    virtual void AlgrithmInterface() = 0;
protected:
        Strategy()//声明为protected属性,是为了防止客户实例化这个类
;
/*
*具体策略算法,实现父类的接口
*/
class ConcretStrategyA : public Strategy
public:
    ConcretStrategyA()
    virtual ~ConcretStrategyA()

    virtual void AlgrithmInterface()
        cout<<"This is ConcretStrategyA interface!"<<endl;
    
;
/*
*具体策略算法,实现父类的接口
*/
class ConcretStrategyB : public Strategy
public:
    ConcretStrategyB()
    virtual ~ConcretStrategyB()

    virtual void AlgrithmInterface()
        cout<<"This is ConcretStrategyB interface!"<<endl;
    
;


class CustomerClient
public:
    CustomerClient(Strategy *stra)
        _stra = stra;//客户端初始化时必须指定要调用的算法
    

    virtual ~CustomerClient()
    void doAction()
        if(_stra != NULL)
            _stra->AlgrithmInterface();
    
private:
    Strategy *_stra;

;
//测试程序
#include <iostream>
using namespace std;

int main()
    cout<<"策略设计模式例子!"<<endl;
    Strategy *straA = new ConcretStrategyA();
    Strategy *straB = new ConcretStrategyB();

    CustomerClient *clientA = new CustomerClient(straA);
    CustomerClient *clientB = new CustomerClient(straB);

    clientA->doAction();
    clientB>doAction();
    //更简洁的写法
    /*
    CustomerClient *clientA = new CustomerClient(new ConcretStrategyA());
    clientA->doAction();
    CustomerClient *clientB = new CustomerClient(new ConcretStrategyB());
    client->doAction();
    */


    delete starA;
    delete clientA;
    delete starB;
    delete clientB;
    return 0;


如上述代码所示,要实现策略模式,至少包含三个类的实现。

上述代码应该可以作为所有策略模式代码的一个基本框架,客户端只需要考虑到CustomerClient的实现就可以,这个类里面不会有具体算法的实现细节,它通过一个保存在类中的实例化的策略封装类(Strategy)去调用具体的算法,而客户端只要明确的把要调用的具体算法的实例作为参数(new ConcretStrategyA())传给CustomerClient的构造函数,虽然每个具体算法的类都不一样,但是他们都有共同的父类Strategy,而CustomerClient构造函数的参数就是Strategy类型的,这就是C++中多态的作用了。


在什么场合下要使用策略模式?


(以下内容从Gof的设计模式书中总结而来)

  1. 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
  2. 需要使用一个算法的不同变体,当这些算法的变体实现为一个算法的类层次的时候,可以使用策略模式。
  3. 一个类定义多种行为,并且这些行为在这个类的操作中以多个条件语句或者switch形式存在,可以考虑使用策略模式。

具体应用例子


正如文章开头所提的商场针对客户的等级,而对衣服的不同打折策略这个情况,设计一个程序来满足这种情况。

先设计程序的UML图结构:

程序设计简单思路:

首先三个等级的客户打折情况会在三个分区,不会有交集,我们假设有year,buys,costs,birthday,new_styles这五个属性会影响最后的打折情况,也就是说同样为高级客户也有可能会出现最后的折扣不一样的情况,但是总体都属于高级用户打折区间内。但是每个类型的客户并不一定会同时被这五个属性所影响,比如我们设定:

普通客户受year,new_styles影响
高级客户受year,buys,new_styles影响
VIP客户受costs,birthday影响。

这个时候在设计公共接口的时候,就要全方位考虑到各种影响三种策略的属性,也就是让接口的扩展性更强,包含所有情况,至于子类具体算法要不要使用时后面算法的事,作为父类要考虑到所有情况。


代码实现:


/*
*策略封装类,必须定义一个纯虚公共接口,子类中需要实现这个接口。
*/
class DiscountStrategy
public:
    virtual ~DiscountStrategy()
    virtual  int computeDiscountPrice(int year,int buys, int all_cost,bool birthday, bool new_styles) = 0;
    static int vip_discount[2];
    static int higher_discount[2];
    static int normal_discount[2];
protected:
        DiscountStrategy()//声明为protected属性,是为了防止客户实例化这个类
;
//定义三种客户的打折区间
int DiscountStrategy::vip_discount[2]=70,80;
int DiscountStrategy::higher_discount[2]=80,90;
int DiscountStrategy::normal_discount[2]=90,100

/*
*普通客户策略算法,实现父类的接口
*/
class NormalDiscount : public DiscountStrategy
public:
    NormalDiscount()
    virtual ~NormalDiscount()

    virtual  int computeDiscountPrice(int year,int buys, int all_cost,bool birthday, bool new_styles)
        cout<<"This is NormalDiscount interface!"<<endl;
        int discount = 0;
        if(year < 4 && year >2)
        
            discount += 2;
        elsediscount += 3;

        if(!new_styles)discount += 3;
        return normal_discount[1]-discount;
    
;
/*
*高级客户策略算法,实现父类的接口
*/
class HigherDiscount : public DiscountStrategy
public:
    HigherDiscount()
    virtual ~HigherDiscount()

    virtual  int computeDiscountPrice(int year,int buys, int all_cost,bool birthday, bool new_styles)
        cout<<"This is HigherDiscount interface!"<<endl;
        int discount = 0;
        if(year < 4 && year >2)
        
            discount += 2;
        elsediscount += 3;
        if(buys > 20)discount += 2;
        if(!new_styles)discount += 3;
        return higher_discount[1]-discount;
    
;
/*
*VIP客户策略算法,实现父类的接口
*/
class VIPDiscount : public DiscountStrategy
public:
    VIPDiscount()
    virtual ~VIPDiscount()

    virtual  int computeDiscountPrice(int year,int buys, int all_cost,bool birthday, bool new_styles)
        cout<<"This is VIPDiscount interface!"<<endl;
        int discount = 0;
        if(all_cost > 5000)
        discount += 2;
        else if(all_cost >2000)
        
            discount += 1;
        
        if(birthday)discount += 2;
        return vip_discount[1]-discount;
    
;


class CustomerClient
public:
    CustomerClient(DiscountStrategy *stra)
        _stra = stra;//客户端初始化时必须指定要调用的算法
    

    virtual ~CustomerClient()
    void setInfomation(int years, int buys, int costs, bool birthday,bool new_styles)
        _year = years;
        _buys = buys;
        _all_cost = costs;
        _birthday = birthday;
        _new_styles = new_styles;
    
    void Discount()

        if(_stra != NULL)
            _stra->computeDiscountPrice(_year,_buys,_all_costs,_birthday,_new_styles);
        
    
private:
    DiscountStrategy *_stra;
    int _year;//用户的购物年龄
    int _buys;//用户所买物品的数量
    int _all_cost;//用户一共花的钱
    bool _birthday;//当月是否是用户的生日
    bool _new_styles;//所买物品是否是新上市

;

//主程序

#include <iostream>
using namespace std;

int main()
    int customer_type;
    int year;
    int buys;
    int costs;
    bool birthday;
    bool new_styles;
    while(true)
        cout<<"Please Input the Customer's type(normal=1,higher=2,vip=3)"<<endl;
        cin>>customer_type;
        cout<<"Input years:"<<endl;
        cin>>year;
        cout<<"Input buys:"<<endl;
        cin>>buys;
        cout<<"Input costs:"<<endl;
        cin>>costs;
        cout<<"Input birthday(0 yes, 1 no):"<<endl;
        cin>>birthday;
        cout<<"Input new_styles(0 yes,1 no):"<<endl;
        cin>>new_styles;
        if(customer_type == 1)
            CustomerClient *client = new CustomerClient(new NormalDiscount());


        
        if(customer_type == 2)
        CustomerClient *client = new CustomerClient(new HigherDiscount());

        
        //此处省略vip了.....
        client->setInfomation(year, buys, costs, birthday,new_styles);
        client->Discount();

    return 0

策略模式有个确定想必看了上面程序会发觉:

1.客户必须了解不同的策略,得知道这些策略有何不同,才会知道如何选择这些策略。此时可能不得不向客户暴露具体的实现问题,因此当这些不同行为变体于客户相关的行为时,才需要使用strategy模式

2.strategy和CustomerClient之间的通讯开销,正如前面所说不同的策略所需要的信息可能不一样,但是每次实例化具体策略的时候都要把这些信息传递过去,即使一个简单的策略不需要任何信息,也会有客户端这边初始化了一些永远也用不到的参数。

策略模式到此为止,后续会在分析android framework 代码的文章中再次对策略模式进行实例分析。

以上是关于设计模式-策略模式的主要内容,如果未能解决你的问题,请参考以下文章

策略设计模式详解C/Java/JS/Go/Python/TS不同语言实现

20.设计模式_策略者模式

设计模式四:策略模式

20策略者模式(Strategy Pattern)

php设计模式-策略模式

设计模式之策略模式(行为型)