【描述】策略模式本質上利用的是面向對象的多態特性,構建者不必自身包含實現的邏輯,而是根據需要利用其他對象中的算法。
【UML圖】
圖1 UML圖
(1) 定義了一個Methods基類,包含一個策略的接口MethodsInterface。
(2) 定義了MethodsStrategyA、MethodsStrategyB兩種策略。
(3) 定義了一個策略構建者Context,包含ContextInterface方法。
【示例代碼】
methods.h
[html]
#ifndef METHODS_H
#define METHODS_H
class Methods
{
public:
Methods();
public:
virtual void MethodsInterface();
};
#endif // METHODS_H
#ifndef METHODS_H
#define METHODS_H
class Methods
{
public:
Methods();
public:
virtual void MethodsInterface();
};
#endif // METHODS_H
methods.cpp
[html]
#include <QDebug>
#include "methods.h"
Methods::Methods()
{
qDebug()<<"construct Methods";
}
void Methods::MethodsInterface()
{
qDebug()<<"Methods::MethodsInterface";
}
#include <QDebug>
#include "methods.h"
Methods::Methods()
{
qDebug()<<"construct Methods";
}
void Methods::MethodsInterface()
{
qDebug()<<"Methods::MethodsInterface";
}
methodstrategya.h
[html]
#ifndef METHODSTRATEGYA_H
#define METHODSTRATEGYA_H
#include "methods.h"
class MethodStrategyA : public Methods
{
public:
MethodStrategyA();
public:
void MethodsInterface();
};
#endif // 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
[html]
#include <QDebug>
#include "methodstrategya.h"
MethodStrategyA::MethodStrategyA()
{
qDebug()<<"construct MethodStrategyA";
}
void MethodStrategyA::MethodsInterface()
{
qDebug()<<"Strategy A";
}
#include <QDebug>
#include "methodstrategya.h"
MethodStrategyA::MethodStrategyA()
{
qDebug()<<"construct MethodStrategyA";
}
void MethodStrategyA::MethodsInterface()
{
qDebug()<<"Strategy A";
}
methodstrategyb.h
[html]
#ifndef METHODSTRATEGYB_H
#define METHODSTRATEGYB_H
#include "methods.h"
class MethodStrategyB : public Methods
{
public:
MethodStrategyB();
public:
void MethodsInterface();
};
#endif // 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
[html] ‘
#include <QDebug>
#include "methodstrategyb.h"
MethodStrategyB::MethodStrategyB()
{
qDebug()<<"construct MethodStrategyB";
}
void MethodStrategyB::MethodsInterface()
{
qDebug()<<"Strategy B";
}
#include <QDebug>
#include "methodstrategyb.h"
MethodStrategyB::MethodStrategyB()
{
qDebug()<<"construct MethodStrategyB";
}
void MethodStrategyB::MethodsInterface()
{
qDebug()<<"Strategy B";
}
context.h
[html]
#ifndef CONTEXT_H
#define CONTEXT_H
#include "methods.h"
class Context
{
public:
Context(Methods *method);
private:
Methods *_method;
public:
void ContextInterface();
};
#endif // 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
[html]
#include "context.h"
Context::Context(Methods *method)
{
_method = method;
}
void Context::ContextInterface()
{
_method->MethodsInterface();
}
#include "context.h"
Context::Context(Methods *method)
{
_method = method;
}
void Context::ContextInterface()
{
_method->MethodsInterface();
}
main.cpp
[html]
#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;
}
#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;
}
【運行結果】
[html]
construct Methods
construct MethodStrategyA
Strategy A
construct Methods
construct MethodStrategyB
Strategy B
construct Methods
construct MethodStrategyA
Strategy A
construct Methods
construct MethodStrategyB
Strategy B
【結果分析】
利用多態特性,實現了不同策略的調用,由於是測試代碼,本身很簡單,省略了析構函數。
【實例剖析】
*本例來源於《重構-改善既有代碼的設計》第1章。原例采用Java語言編寫,本例利用Qt C++進行了改寫。
看下面一個關於電影租賃計價的例子。
movie.h
[html]
#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
#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
[html]
#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;
}
#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
[html]
#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
#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
[html]
#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;
}
#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
[html]
#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;
}
#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;
}
運行結果如下:
[html]
"construct Movie::Movie(Book1,1)"
construct Movie::Movie()
construct Rental
1
90
"construct Movie::Movie(Book1,1)"
construct Movie::Movie()
construct Rental
1
90
示例調用了getCharge,並返回了priceCode相應的計價。相對於《重構》一書最原始代碼,這段代碼經過了一定的重構處理。
先看下述代碼,
[html]
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;
}
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
[html]
#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
#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
[html]
#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;
}
#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
[html]
#ifndef REGULARPRICE_H
#define REGULARPRICE_H
#include "price.h"
class RegularPrice : public Price
{
public:
RegularPrice();
public:
double getCharge(int daysRented);
};
#endif // 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
[html]
#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;
}
#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
[html]
#ifndef NEWRELEASEPRICE_H
#define NEWRELEASEPRICE_H
#include "price.h"
class NewReleasePrice : public Price
{
public:
NewReleasePrice();
public:
double getCharge(int daysRented);
};
#endif // 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
[html]
#include <QDebug>
#include "newreleaseprice.h"
NewReleasePrice::NewReleasePrice()
{
qDebug()<<"construct NewReleasePrice";
}
double NewReleasePrice::getCharge(int daysRented)
{
qDebug()<<"NewReleasePrice::getCharge";
return daysRented*3;;
}
#include <QDebug>
#include "newreleaseprice.h"
NewReleasePrice::NewReleasePrice()
{
qDebug()<<"construct NewReleasePrice";
}
double NewReleasePrice::getCharge(int daysRented)
{
qDebug()<<"NewReleasePrice::getCharge";
return daysRented*3;;
}
discountprice.h
[html]
#ifndef DISCOUNTPRICE_H
#define DISCOUNTPRICE_H
#include "price.h"
class DiscountPrice : public Price
{
public:
DiscountPrice();
public:
double getCharge(int daysRented);
};
#endif // 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
[html]
#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;
}
#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
[html]
#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
#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
[html]
#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);
}
#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
[html]
#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
#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
[html]
#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);
}
#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
[html]
#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;
}
#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;
}
【運行結果】
[html]
"construct Movie::Movie(Book1,1)"
construct Price
construct Movie::Movie()
construct Rental
construct Price
construct NewReleasePrice
NewReleasePrice::getCharge
"construct Movie::Movie(Book1,1)"
construct Price
construct Movie::Movie()
construct Rental
construct Price
construct NewReleasePrice
NewReleasePrice::getCharge
【結果分析】
[html]
movie.setPriceCode(NEW_RELEASE, new NewReleasePrice);
movie.setPriceCode(NEW_RELEASE, new NewReleasePrice);
傳入priceCode和Price子對象作為參數,從運行結果可以看出,調用了NewReleasePrice的getCharge方法。
應用策略模式後,Rental::getCharge中僅有一行代碼。
[html] view plaincopyprint?double Rental::getCharge(int daysRented)
{
return _movie.getCharge(daysRented);
}
double Rental::getCharge(int daysRented)
{
return _movie.getCharge(daysRented);
}
將原來的getCharge做了搬移處理,而Movie::getCharge也很簡短:
[html] view plaincopyprint?double Movie::getCharge(int daysRented)
{
return _price->getCharge(daysRented);
}
double Movie::getCharge(int daysRented)
{
return _price->getCharge(daysRented);
}
*應用策略模式後,消除了switch-case語句,函數更為簡潔。將Price單獨抽出,成為一個類組,提高了代碼的可重用性。
作者:tandesir