设计模式(3)-策略模式(Strategy)

【描述】策略模式本质上利用的是面向对象的多态特性,构建者不必自身包含实现的逻辑,而是根据需要利用其他对象中的算法。

【UML图】

图1 UML图

(1) 定义了一个Methods基类,包含一个策略的接口MethodsInterface。

(2) 定义了MethodsStrategyA、MethodsStrategyB两种策略。

(3) 定义了一个策略构建者Context,包含ContextInterface方法。

 

【示例代码】

methods.h

#ifndef METHODS_H
#define METHODS_H

class Methods
{
public:
    Methods();

public:
    virtual void MethodsInterface();
};

#endif // METHODS_H

methods.cpp

#include <QDebug>
#include "methods.h"

Methods::Methods()
{
    qDebug()<<"construct Methods";
}

void Methods::MethodsInterface()
{
    qDebug()<<"Methods::MethodsInterface";
}

methodstrategya.h

#ifndef METHODSTRATEGYA_H
#define METHODSTRATEGYA_H

#include "methods.h"

class MethodStrategyA : public Methods
{
public:
    MethodStrategyA();

public:
    void MethodsInterface();
};

#endif // METHODSTRATEGYA_H

methodstrategya.cpp

#include <QDebug>
#include "methodstrategya.h"

MethodStrategyA::MethodStrategyA()
{
    qDebug()<<"construct MethodStrategyA";
}

void MethodStrategyA::MethodsInterface()
{
    qDebug()<<"Strategy A";
}

methodstrategyb.h

#ifndef METHODSTRATEGYB_H
#define METHODSTRATEGYB_H

#include "methods.h"

class MethodStrategyB : public Methods
{
public:
    MethodStrategyB();

public:
    void MethodsInterface();
};

#endif // METHODSTRATEGYB_H

methodstrategyb.cpp

#include <QDebug>
#include "methodstrategyb.h"

MethodStrategyB::MethodStrategyB()
{
    qDebug()<<"construct MethodStrategyB";
}

void MethodStrategyB::MethodsInterface()
{
    qDebug()<<"Strategy B";
}

context.h

#ifndef CONTEXT_H
#define CONTEXT_H

#include "methods.h"

class Context
{
public:
    Context(Methods *method);

private:
    Methods *_method;

public:
    void ContextInterface();
};

#endif // CONTEXT_H

context.cpp

#include "context.h"

Context::Context(Methods *method)
{
    _method = method;
}

void Context::ContextInterface()
{
    _method->MethodsInterface();
}

main.cpp

#include <QDebug>
#include "context.h"
#include "methods.h"
#include "methodstrategya.h"
#include "methodstrategyb.h"

int main()
{
    Context *context;

    context = new Context(new MethodStrategyA);
    context->ContextInterface();

    context = new Context(new MethodStrategyB);
    context->ContextInterface();

    return 0;
}

 

【运行结果】

construct Methods 
construct MethodStrategyA 
Strategy A 
construct Methods 
construct MethodStrategyB 
Strategy B 


【结果分析】

利用多态特性,实现了不同策略的调用,由于是测试代码,本身很简单,省略了析构函数。

 

【实例剖析】

*本例来源于《重构-改善既有代码的设计》第1章。原例采用Java语言编写,本例利用Qt C++进行了改写。

看下面一个关于电影租赁计价的例子。

movie.h

#ifndef MOVIE_H
#define MOVIE_H

#include <QString>
enum {REGULAR = 0,NEW_RELEASE = 1,DISCOUNT = 2};

class Movie
{
public:
    Movie();
    Movie(QString title, int priceCode);

private:
    QString _title;
    int _priceCode;

public:
    QString getTitle();
    int getPriceCode();
    void setPriceCode(int arg);
};

#endif // MOVIE_H

movie.cpp

#include <QDebug>
#include "movie.h"

Movie::Movie()
{
    qDebug()<<"construct Movie::Movie()";
}

Movie::Movie(QString title, int priceCode)
{
    qDebug()<<QString("construct Movie::Movie(%1,%2)").arg(title).arg(priceCode);
    _title = title;
    _priceCode = priceCode;
}

QString Movie::getTitle()
{
    return _title;
}

int Movie::getPriceCode()
{
    return _priceCode;
}

void Movie::setPriceCode(int arg)
{
    _priceCode = arg;
}

rental.h

#ifndef RENTAL_H
#define RENTAL_H

#include "movie.h"

class Rental
{
public:
    Rental(Movie movie, int daysRented);

private:
    Movie _movie;
    int _daysRented;

public:
    Movie getMovie();
    int getDaysRented();
    double getCharge(int daysRented);
};

#endif // RENTAL_H

rental.cpp

#include <QDebug>
#include "rental.h"

Rental::Rental(Movie movie, int daysRented)
{
    qDebug()<<"construct Rental";
    _movie = movie;
    _daysRented = daysRented;
}

Movie Rental::getMovie()
{
    return _movie;
}

int Rental::getDaysRented()
{
    return _daysRented;
}

double Rental::getCharge(int daysRented)
{
    double result = 0;
    qDebug()<<getMovie().getPriceCode();
    switch(getMovie().getPriceCode())
    {
    case REGULAR:
        result +=2;
        if(daysRented>2)
        {
            result += (daysRented-2)*1.5;
        }
        break;
    case NEW_RELEASE:
        result += daysRented*3;
        break;
    case DISCOUNT:
        result +=2;
        if(daysRented>2)
        {
            result += (daysRented-2)*1;
        }
        break;
    }
    qDebug()<<result;
    return result;
}


main.cpp

#include "movie.h"
#include "rental.h"

int main()
{
    int daysRented = 30;
    Movie movie("Book1",NEW_RELEASE);
    Rental rental(movie,daysRented);
    movie.setPriceCode(NEW_RELEASE);
    rental.getCharge(daysRented);
    return 0;
}


运行结果如下:

"construct Movie::Movie(Book1,1)" 
construct Movie::Movie() 
construct Rental 
1 
90 

示例调用了getCharge,并返回了priceCode相应的计价。相对于《重构》一书最原始代码,这段代码经过了一定的重构处理。

先看下述代码,

double Rental::getCharge(int daysRented)
{
    double result = 0;
    qDebug()<<getMovie().getPriceCode();
    switch(getMovie().getPriceCode())
    {
    case REGULAR:
        result +=2;
        if(daysRented>2)
        {
            result += (daysRented-2)*1.5;
        }
        break;
    case NEW_RELEASE:
        result += daysRented*3;
        break;
    case DISCOUNT:
        result +=2;
        if(daysRented>2)
        {
            result += (daysRented-2)*1;
        }
        break;
    }
    qDebug()<<result;
    return result;
}

这段代码,有一些不好的地方:

(1) 这段代码通过priceCode判断电影的租金,采用了switch-case语言,实质上还是面向过程的范畴,代码可重用性很低;

(2) 如果情形较多,switch-case会变得十分庞大。

 

有什么办法能去掉switch-case吗?下面通过引入策略模式实现这一目的。

先看UML图,

图2

(1) 引入了一个Price基类,提供了getCharge()接口供子类实现;

(2) 将switch-case语句中的3种情形抽取为3个Price子类;

(3) Movie调用了Price提供的接口。

 

【代码清单】

price.h

#ifndef PRICE_H
#define PRICE_H

class Price
{
public:
    Price();

private:
    int _priceCode;

public:
    int getPriceCode();
    void setPriceCode(int arg);
    virtual double getCharge(int daysRented);
};

#endif // PRICE_H

price.cpp

#include <QDebug>
#include "price.h"

Price::Price()
{
    qDebug()<<"construct Price";
    _priceCode = 0;
}

int Price::getPriceCode()
{
    qDebug()<<_priceCode;
    return _priceCode;
}

void Price::setPriceCode(int arg)
{
    _priceCode = arg;
}

double Price::getCharge(int daysRented)
{
    qDebug()<<"Price::getCharge";
    return daysRented;
}

regularprice.h

#ifndef REGULARPRICE_H
#define REGULARPRICE_H

#include "price.h"

class RegularPrice : public Price
{
public:
    RegularPrice();

public:
    double getCharge(int daysRented);
};

#endif // REGULARPRICE_H

regularprice.cpp

#include <QDebug>
#include "regularprice.h"

RegularPrice::RegularPrice()
{
    qDebug()<<"construct RegularPrice";
}

double RegularPrice::getCharge(int daysRented)
{
    qDebug()<<"RegularPrice::getCharge";
    double result = 2;
    if(daysRented>2)
    {
        result += (daysRented-2)*1.5;
    }
    return result;
}

newreleaseprice.h

#ifndef NEWRELEASEPRICE_H
#define NEWRELEASEPRICE_H

#include "price.h"

class NewReleasePrice : public Price
{
public:
    NewReleasePrice();

public:
    double getCharge(int daysRented);
};

#endif // NEWRELEASEPRICE_H

newreleaseprice.cpp

#include <QDebug>
#include "newreleaseprice.h"

NewReleasePrice::NewReleasePrice()
{
    qDebug()<<"construct NewReleasePrice";
}

double NewReleasePrice::getCharge(int daysRented)
{
    qDebug()<<"NewReleasePrice::getCharge";
    return daysRented*3;;
}

discountprice.h

#ifndef DISCOUNTPRICE_H
#define DISCOUNTPRICE_H

#include "price.h"

class DiscountPrice : public Price
{
public:
    DiscountPrice();

public:
    double getCharge(int daysRented);
};

#endif // DISCOUNTPRICE_H

discountprice.cpp

#include <QDebug>
#include "discountprice.h"

DiscountPrice::DiscountPrice()
{
    qDebug()<<"construct DiscountPrice";
}

double DiscountPrice::getCharge(int daysRented)
{
    qDebug()<<"DiscountPrice::getCharge";
    double result = 2;
    if(daysRented>2)
    {
        result += daysRented;
    }

    return result;
}

movie.h

#ifndef MOVIE_H
#define MOVIE_H

#include <QString>
#include "price.h"
enum {REGULAR = 0,NEW_RELEASE = 1,DISCOUNT = 2};

class Movie
{
public:
    Movie();
    Movie(QString title, int priceCode);
    ~Movie();

private:
    QString _title;
    int _priceCode;
    Price *_price;

public:
    QString getTitle();
    int getPriceCode();
    void setPriceCode(int arg, Price *_price);
    double getCharge(int daysRented);
};

#endif // MOVIE_H

movie.cpp

#include <QDebug>
#include "movie.h"
#include "regularprice.h"
#include "newreleaseprice.h"
#include "discountprice.h"

Movie::Movie()
{
    qDebug()<<"construct Movie::Movie()";

}

Movie::Movie(QString title, int priceCode)
{
    qDebug()<<QString("construct Movie::Movie(%1,%2)").arg(title).arg(priceCode);
    _price = new Price;
    _title = title;
    _priceCode = priceCode;
}

Movie::~Movie()
{
    delete _price;
}

QString Movie::getTitle()
{
    return _title;
}

int Movie::getPriceCode()
{
    return _price->getPriceCode();
}

void Movie::setPriceCode(int arg, Price *price)
{
    _price = price;
    _price->setPriceCode(arg);
}

double Movie::getCharge(int daysRented)
{
    return _price->getCharge(daysRented);
}

rental.h

#ifndef RENTAL_H
#define RENTAL_H

#include "movie.h"

class Rental
{
public:
    Rental(Movie movie, int daysRented);

private:
    Movie _movie;
    int _daysRented;

public:
    Movie getMovie();
    int getDaysRented();
    double getCharge(int daysRented);
};

#endif // RENTAL_H

rental.cpp

#include <QDebug>
#include "rental.h"

Rental::Rental(Movie movie, int daysRented)
{
    qDebug()<<"construct Rental";
    _movie = movie;
    _daysRented = daysRented;
}

Movie Rental::getMovie()
{
    return _movie;
}

int Rental::getDaysRented()
{
    return _daysRented;
}

double Rental::getCharge(int daysRented)
{
    return _movie.getCharge(daysRented);
}

main.cpp

#include <QDebug>
#include "movie.h"
#include "rental.h"
#include "regularprice.h"
#include "newreleaseprice.h"
#include "discountprice.h"

int main()
{
    int daysRented = 30;
    Movie movie("Book1",NEW_RELEASE);
    Rental rental(movie,daysRented);
    movie.setPriceCode(NEW_RELEASE, new NewReleasePrice);
    rental.getCharge(daysRented);
    return 0;
}


【运行结果】

"construct Movie::Movie(Book1,1)" 
construct Price 
construct Movie::Movie() 
construct Rental 
construct Price 
construct NewReleasePrice 
NewReleasePrice::getCharge


【结果分析】

movie.setPriceCode(NEW_RELEASE, new NewReleasePrice);

传入priceCode和Price子对象作为参数,从运行结果可以看出,调用了NewReleasePrice的getCharge方法。

应用策略模式后,Rental::getCharge中仅有一行代码。

double Rental::getCharge(int daysRented)
{
    return _movie.getCharge(daysRented);
}

将原来的getCharge做了搬移处理,而Movie::getCharge也很简短:

double Movie::getCharge(int daysRented)
{
    return _price->getCharge(daysRented);
}


 

*应用策略模式后,消除了switch-case语句,函数更为简洁。将Price单独抽出,成为一个类组,提高了代码的可重用性。

 

【学习参考】

(1) 设计模式之三 --- 策略模式(Strategy Pattern)

 

【源码下载】

Qt设计模式1-8测试源码:http://download.csdn.net/detail/tandesir/4984275
 声明:该源码仅供学习交流,勿用于商业目的。


 

转载请标明出处,仅供学习交流,勿用于商业目的

Copyright @ http://blog.csdn.net/tandesir





 


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值