策略模式(strategy-pattern)
Posted 白龙码~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了策略模式(strategy-pattern)相关的知识,希望对你有一定的参考价值。
策略模式(strategy-pattern)
文章目录
一、情景建立
假如现在有这样的需求:把各种类型鸟类的行为,包括飞翔、叫声进行管理,并能够统一获取这些信息,你会如何去做?
有了面向对象的知识,这不轻轻松松:
class Bird
public:
virtual void fly() ;
virtual void chirp() ;
;
class Parrot : public Bird
public:
void fly()
std::cout << "I can fly slowly!" << std::endl;
void chirp()
std::cout << "I can chirp!" << std::endl;
;
class Sparrow : public Bird
public:
void fly()
std::cout << "I can fly fast!" << std::endl;
void chirp()
std::cout << "I can chirp!" << std::endl;
;
class ToyBird : public Bird
public:
void fly()
std::cout << "I can not fly!" << std::endl;
void chirp()
std::cout << "I can not chirp!" << std::endl;
;
定义一个Bird抽象类,以及虚函数fly(飞)、chirp(叫),然后利用继承和虚函数重写,就有了飞得慢且会叫的鹦鹉、飞得快且会叫的麻雀以及不会飞且不会叫的玩具鸟。
二、剖析现存问题
这样的代码简单,且初期维护的成本也不高,但是随着业务逐渐地扩展,鸟的类型可能由一开始的几种,再到后来的几十上百种,并且鸟的行为也进行了扩展,那岂不是每一次新增都要写很多重复的内容?
简单来说,问题有:
- 不易维护。每次新增类都要重写虚函数,太麻烦。
- 代码冗余。比如这里,飞得快或慢是单独的一种行为,完全可以从Bird这个类中解耦出来。假如以后要修改成"飞的像乌龟一样慢"、“飞的像火箭一样快”,就可以直接改"飞"这个行为类,而不是一个个修改Bird类!
三、策略模式
根据上述的问题剖析,我们可以将飞行、叫声解耦出来作为单独的两个类:
"fly.hpp"
class FlyBehavior
public:
virtual void fly()
;
class FlyFast : public FlyBehavior
public:
void fly()
std::cout << "I can fly fast" << std::endl;
;
class FlySlowly : public FlyBehavior
public:
void fly()
std::cout << "I can fly slowly" << std::endl;
;
class FlyDisabled : public FlyBehavior
public:
void fly()
std::cout << "I can not fly" << std::endl;
;
"chrip.hpp"
class ChripBehavior
public:
virtual void chrip()
;
class ChirpAble : public ChripBehavior
public:
void chrip()
std::cout << "I can chrip" << std::endl;
;
class ChirpDisabled : public ChripBehavior
public:
void chrip()
std::cout << "I can not chrip" << std::endl;
;
在此基础上,对于不同的鸟类,我们让它持有不同的fly对象和chrip对象即可:
class Bird
public:
virtual void fly() ;
virtual void chirp() ;
protected:
std::shared_ptr<FlyBehavior> fly_behavior_;
std::shared_ptr<ChripBehavior> chrip_behavior_;
;
class Parrot : public Bird
public:
Parrot()
fly_behavior_ = std::shared_ptr<FlyBehavior>(new FlySlowly);
chrip_behavior_ = std::shared_ptr<ChripBehavior>(new ChirpAble);
void fly()
fly_behavior_->fly();
void chirp()
chrip_behavior_->chrip();
;
class Sparrow : public Bird
public:
Sparrow()
fly_behavior_ = std::shared_ptr<FlyBehavior>(new FlyFast);
chrip_behavior_ = std::shared_ptr<ChripBehavior>(new ChirpAble);
void fly()
fly_behavior_->fly();
void chirp()
chrip_behavior_->chrip();
;
class ToyBird : public Bird
public:
ToyBird()
fly_behavior_ = std::shared_ptr<FlyBehavior>(new FlyDisabled);
chrip_behavior_ = std::shared_ptr<ChripBehavior>(new ChirpDisabled);
void fly()
fly_behavior_->fly();
void chirp()
chrip_behavior_->chrip();
;
测试:
int main()
Parrot parrot;
std::cout << "Parrot says : " << std::endl;
parrot.chirp();
parrot.fly();
std::cout << "--------------" << std::endl;
Sparrow sparrow;
std::cout << "Sparrow says : " << std::endl;
sparrow.chirp();
sparrow.fly();
std::cout << "--------------" << std::endl;
ToyBird toy_bird;
std::cout << "ToyBird says : " << std::endl;
toy_bird.chirp();
toy_bird.fly();
std::cout << "--------------" << std::endl;
return 0;
// output:
Parrot says :
I can chrip
I can fly slowly
--------------
Sparrow says :
I can chrip
I can fly fast
--------------
ToyBird says :
I can not chrip
I can not fly
--------------
四、总结
回过头来看到底什么是策略模式?
书本定义:
策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
简单来说,就是面向接口封装+解耦。
对于面向接口(算法):原先的写法是面向类的,因为每一个种类的鸟都要重写虚函数,在业务逻辑复杂的情况下很容易造成代码的冗余。
我们认识到,每一个接口都对应同一个类型的算法(或是叫、或是飞翔),但是它们实现的方式不一样,因此我们可以用单独的类把它们封装起来,再通过继承实现不同的算法逻辑,从而做到解耦。
因此,原先类的内部就会拥有每个算法族对应的基类指针,换句话说,就是将单纯的继承变成**"继承+组合"的混合模式**。
五、补充
事实上,策略模式我们早已接触过:
std::vector<int> vi;
std::vector<double> vd;
std::sort(vi.begin(), vi.end(), std::greater<int>());
std::sort(vd.begin(), vd.end(), std::less<double>());
其中,std::greater
和std::less
就是C++标准库为我们封装好的比较策略。
以上是关于策略模式(strategy-pattern)的主要内容,如果未能解决你的问题,请参考以下文章