策略模式(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() = 0;
  virtual void chirp() = 0;

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() {
  std::shared_ptr<Bird> bird;
  bird = std::make_shared<Parrot>();
  std::cout << "Parrot says : " << std::endl;
  bird->chirp();
  bird->fly();
  std::cout << "--------------" << std::endl;

  bird = std::make_shared<Sparrow>();
  std::cout << "Sparrow says : " << std::endl;
  bird->chirp();
  bird->fly();
  std::cout << "--------------" << std::endl;

  bird = std::make_shared<ToyBird>();
  std::cout << "ToyBird says : " << std::endl;
  bird->chirp();
  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++标准库为我们封装好的比较策略

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白龙码~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值