首页 > 基础资料 博客日记

设计模式:1. 策略模式

2026-06-13 22:00:02基础资料围观2

文章设计模式:1. 策略模式分享给大家,欢迎收藏极客资料网,专注分享技术知识

笔者目前正在阅读《Head First设计模式》,首先对该书发表我的个人观点,非常有趣生动的一本书,在阅读这本书之前,我一直对设计模式抱有畏惧的心理,阅读该书之后,才发现设计模式可以这么通俗易懂,推荐各位去阅读原书。为加深知识印象,对书中内容进行梳理总结,书中的案例均由Java实现,而笔者本人目前主要使用C++,因此该文章通过C++来描述案例。由于本人水平有限,表达会有欠佳处,若要深入理解设计模式,还是推荐读者能够阅读原书。

先设想一个场景,你所在的公司做出的一款模拟鸭子的游戏,游戏中的鸭子可以一边戏水一边呱呱叫,并且系统使用了标准的面向对象技术。设计一个超类Duck

class Duck
{
public:
  virtual void quack()
  {
    std::cout << "嘎嘎叫~" << std::endl;
  }
  virtual void swim()
  {
    std::cout << "游泳" << std::endl;
  }
  virtual void display() = 0; // 鸭子外观
};

并让各种鸭子都继承此超类。

class MallardDuck: public Duck // 绿头鸭
{
public:
  void display() override
  {
    std::cout << "绿头"  << std::endl;
  }
};
/* ----------------------- */
class RedheadDark: public Duck // 红头鸭
{
public:
  void display() override
  {
    std::cout << "红头"  << std::endl;
  }
};

为了提升产品竞争力,老板给你提出了新的需求,要鸭子能够飞起来。
你拍着胸部应下,简单,我只要给超类Duck类中增加一个fly()函数就可以了。

class Duck
{
public:
  virtual void quack()
  {
    std::cout << "嘎嘎叫~" << std::endl;
  }
  virtual void swim()
  {
    std::cout << "游泳" << std::endl;
  }
  virtual void fly()
  {
    std::cout << "飞行" << std::endl;
  }
  virtual void display() = 0; // 鸭子外观
};

可是糟糕的事情发生了,第二天老板就找到了你,“那些天空中飞来飞去的橡皮鸭 是怎么回事?”。
看样子简单地在超类中进行函数的具体实现是不合适的,不同的子类对于同一个接口可能有着不同的表现形式。如橡皮鸭不会飞,而且橡皮鸭不会嘎嘎叫,而是吱吱叫。
那如果重写超类中的函数呢?

class RubberDuck: public Duck //橡皮鸭
{
public:
  void quack() override
  {
    std::cout << "吱吱叫" << std::endl;
  }
  void fly() override
  {
    // 什么都不做
  }
  // .....
};

这样虽然能解决掉Bug,但是会有如下的缺点,系统难以维护。

  1. 代码会在多个子类中重复,如增加了一个诱饵鸭,它也不会飞,那么诱饵鸭也要重写fly()函数。而且如果后期对飞行动作做了优化,那么这些重复的代码也要挨个进行修改。
  2. 我们很难梳理清楚,鸭子的行为都有哪些。“吱吱叫”、“嘎嘎叫”,这些行为都在子类中维护,要一个个打开子类的文件才能查看得到。
  3. 没有办法动态的改变子类的行为,比如游戏中,给橡皮鸭背上了火箭背包,橡皮鸭应该可以飞起来了。
  4. 因为继承了具体的类,改变会牵一发动全身,造成其他鸭子不想要的改变。

接下来就要引出本次需要介绍的设计模式:策略模式
在程序设计时,我们需要尽可能遵循一个原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
在这个例子中,变化的部分就是鸭子的行为。我们把行为抽离出来,组成一组新类来表示每个行为。
再补充一个设计原则:针对接口编程,而不是针对实现编程。
我们可以定义一个接口,来表示鸭子的飞行行为。

class FlyBehavior
{
public:
  virtual void fly() = 0;
};

每一个具体的行为,都是对这个接口的实现。

class FlyWithWings: public FlyBehavior
{
public:
  void fly() override
  {
    std::cout << "使用翅膀飞行~" << std::endl;
  }
};
class FlyNoWay: public FlyBehavior
{
public:
  void fly() override
  {
    std::cout << "不会飞行~" << std::endl;
  }
};

这时候,我们再对Duck类进行如下修改。

class Duck
{
// 其他行为暂时省略,这里关注飞行行为。
public:
  Duck(FlyBehavior* fb):flyBehavior(fb){}
  void performFly()
  {
    flyBehavior->fly();
  }
  void setFlyBehavior(FlyBehavior* fb)
  {
    flyBehavior = fb;
  }
private:
  FlyBehavior* flyBehavior;
};

橡皮鸭RubberDuck这么写。

class RubberDuck: public Duck
{
public:
  RubberDuck(FlyBehavior* fb):Duck(fb) {}
};

业务流程这么写。

int main()
{
  FlyBehavior* flyNoWay = new FlyNoWay(); // 创建行为对象
  Duck* rubberDuck = new RubberDuck(flyNoWay); // 创建鸭子对象,并绑定行为
  rubberDuck->performFly(); // 由行为对象代理执行动作,不会飞行
  FlyBehavior* flyByRocket = new FlyRocketPowered(); //创建一个“火箭背包飞行”的行为对象
  rubberDuck->setFlyBehavior(flyByRocket); // 现在橡皮鸭可以通过火箭背包飞行了
  rubberDuck->performFly(); // 起飞
}

可以看到,使用策略模式,不仅实现了动态更换行为。而且,如果要扩展新的行为需求,只需要新建一个类继承FlyBehavior接口,不需要更改原有代码。且鸭子共有哪些行为,也可以通过哪些类继承了Behavior接口快速找到,方便系统的维护。
最后,给出策略模式的定义:
定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。


文章来源:https://www.cnblogs.com/chuwuershiyi/p/20508898
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云