设计模式-策略模式

序言


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


定义


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值