策略模式(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(叫),然后利用继承和虚函数重写,就有了飞得慢且会叫的鹦鹉、飞得快且会叫的麻雀以及不会飞且不会叫的玩具鸟。

二、剖析现存问题

这样的代码简单,且初期维护的成本也不高,但是随着业务逐渐地扩展,鸟的类型可能由一开始的几种,再到后来的几十上百种,并且鸟的行为也进行了扩展,那岂不是每一次新增都要写很多重复的内容?

简单来说,问题有:

  1. 不易维护。每次新增类都要重写虚函数,太麻烦。
  2. 代码冗余。比如这里,飞得快或慢是单独的一种行为,完全可以从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::greaterstd::less就是C++标准库为我们封装好的比较策略

以上是关于策略模式(strategy-pattern)的主要内容,如果未能解决你的问题,请参考以下文章

百鸟商城系统开发模式设计详解

我的电赛作品:BP算法鸟类图像识别系统

斑布生活家系统开发详解模式设计

从最小样本中识别鸟类

语音识别基于LMS算法消除嘈杂的鸟类语音信号中的噪声后识别其对应的鸟类物种(Matlab代码实现)

太阳能电站为何变成鸟类杀手?人工智能或可给出答案