设计模式-策略模式

来源:互联网 发布:淘宝差评可以申诉吗 编辑:程序博客网 时间:2024/06/16 10:53

序言


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


定义


封装一系列算法,并在内部实现可以互相替换,可以让算法独立于使用者而变化。简单点说就是:一家商场,会针对客户的消费等级比如普通客户,高级客户,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;        }else{discount += 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;        }else{discount += 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 代码的文章中再次对策略模式进行实例分析。

原创粉丝点击