1.策略模式

模拟鸭子应用

需求

  • 游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫。

一开始,是通过使用继承的方式,用派生类实现各种鸭子。

class Duck {
public:
    void quack();
    void swim();
    void display();
};

class MallardDuck : Duck {
public:
    void display() {
        // 外观是绿头
    }
};

class RedheadDuck {
    void display() {
        // 外观是红头
    }
};

以上缺点:

  • 代码在多个子类中重复;
  • 运行时的行为不容易改变;
  • 我们不能让鸭子跳舞;
  • 很难知道所有鸭子的全部行为;
  • 鸭子不能同是又飞又叫;
  • 改变会牵一发动全身,造成其他鸭子不想要的改变。

由于需求总在变化,现在我们需要让鸭子能飞起来。不同的鸭子,叫声、显示、飞行方式都可能不太一样。

面对这种情况,我们需要的是将不变的部分放在基类中,而将经常变化的部分分离出来,作为独立的部分进行封装。为此,我们做了以下工作:

  • 利用抽象类(接口)代表每个行为,比如说FlyBehavior和QuackBehavior,而行为的每个实现都将实现其中的一个抽象类。
  • 鸭子的子类将使用抽象类(FlyBehavior与QuackBehavior)所表示的行为,所以实际的“实现”不会被绑死在鸭子的子类中。
  • 此次使用的是组合方式,而不是继承的方式进行实现。

于是代码变成了如下:


// 定义飞行行为抽象类(接口),
// 将鸭子类中经常变化的部分进行分离与封装。
class FlyBehavior {
public:
    virtual void fly() = 0;
    
    // 该类为基类,是需要被继承的。
    // 因此,析构函数需要被写成虚函数形式,
    // 这样子资源才得以正确释放。
    virtual ~FlyBehavior() {};
};

// 使用飞行行为抽象类(接口),
// 实现用翅膀飞行的具体类,
// 为下面继承鸭子基类的派生类复用和灵活设定使用类而使用。
class FlyWithWings : public FlyBehavior {
public:
    virtual void fly() {
        cout << "Fly with wings." << endl;
    }
};

// 使用飞行行为抽象类(接口),
// 实现没有飞行的具体类,
// 为下面继承鸭子基类的派生类复用和灵活设定使用类而使用。
class FlyNoWay : public FlyBehavior {
public:
    virtual void fly() {
        cout << "FlyNoWay: 啥时都不干。" << endl;
    }
};

// 定义鸭叫行为抽象类(接口),
// 将鸭子类中经常变化的部分进行分离与封装。
class QuackBehavior {
public:
    virtual void quack() = 0;
    
    // 该类为基类,是需要被继承的。
    // 因此,析构函数需要被写成虚函数形式,
    // 这样子资源才得以正确释放。
    virtual ~QuackBehavior() {};
};

// 使用鸭叫行为抽象类(接口),
// 实现鸭的呱呱叫具体类,
// 为下面继承鸭子基类的派生类复用和灵活设定使用类而使用。
class Quack : public QuackBehavior {
public:
    virtual void quack() {
        cout << "Quack quack..." << endl;
    }
};

// 使用鸭叫行为抽象类(接口),
// 实现鸭的唧唧叫具体类,
// 为下面继承鸭子基类的派生类复用和灵活设定使用类而使用。
class Squeak : public QuackBehavior {
public:
    virtual void quack() {
        cout << "Squeak squeak..." << endl;
    }
};

// 使用鸭叫行为抽象类(接口),
// 实现鸭的哑叫(不叫)具体类,
// 为下面继承鸭子基类的派生类复用和灵活设定使用类而使用。
class MuteQuack : public QuackBehavior {
public:
    virtual void quack() {
        cout << "mute quack..." << endl;
    }
};


// 鸭子作为具体基类,为后面各种鸭子的派生类做基础。
// 鸭子之所以作为基类而不是抽象类(接口),
// 是因为鸭子本身有具体的部分。
// 将鸭子的叫、飞行等方式通过组合的形式,而不是继承的方式实现,
// 可以获得在运行中动态改变,也实现了代码的最大化复用。
// 也不会出现引起继承所造成的牵一发动全身的缺点。
// 将不变的部分封装在基类即可。
class Duck {
public:
    Duck() {
        quackBehavior = nullptr;
        flyBehavior = nullptr;
    }

    // 该类为基类,是需要被继承的。
    // 因此,析构函数需要被写成虚函数形式,
    // 这样子资源才得以正确释放。
    virtual ~Duck() {};

    // 将鸭叫行为委托给行为类实现,
    // 实现类中经常变化的部分分离,得以封装。
    // 也实现了将变化的代码最大化的复用与灵活性。
    void performQuack() {
        quackBehavior->quack();
    };

    // 将飞行行为委托给行为类实现,
    // 实现类中经常变化的部分分离,得以封装。
    // 也实现了将变化的代码最大化的复用与灵活性。
    void performFly() {
        flyBehavior->fly();
    };

    virtual void display() {
        cout << "Duck display" << endl;
    }

    // 获得该类的鸭叫行为类
    const QuackBehavior const* getQuackBehavior() {
        return quackBehavior;
    }

    // 获得该类的飞行行为类
    const FlyBehavior const* getFlyBehavior() {
        return flyBehavior;
    }

    // 设置该类的鸭叫行为类
    void setQuackBehavior(QuackBehavior* quack) {
        delete quackBehavior;
        quackBehavior = quack;
    }

    // 设置该类的飞行行为类
    void setFlyBehavior(FlyBehavior* fly) {
        delete flyBehavior;
        flyBehavior = fly;
    }

// 类成员使用保护方式,将实现派生类可以访问该类成员,
// 实现该类在具体运行过程中可以动态改变行为。
protected:
    QuackBehavior* quackBehavior;
    FlyBehavior* flyBehavior;
};

class MallardDuck : public Duck {
public:
    // 初始化派生类的行为类
    MallardDuck() {
        quackBehavior = new Quack;
        flyBehavior = new FlyWithWings;
    }

    // 释放派生类所拥有的资源
    virtual ~MallardDuck() {
        delete quackBehavior;
        delete flyBehavior;
    }

    // 实现派生类的显示
    virtual void display() {
        cout << "MallardDuck display..." << endl;
    }
};

class RedHeadDuck : public Duck {
public:
    // 初始化派生类的行为类
    RedHeadDuck() {
        quackBehavior = new Squeak;
        flyBehavior = new FlyNoWay;
    }

    // 释放派生类所拥有的资源
    virtual ~RedHeadDuck() {
        delete quackBehavior;
        delete flyBehavior;
    }

    // 实现派生类的显示
    virtual void display() {
        cout << "RedHeadDuck display..." << endl;
    }
};

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1 室友公司新来了一个男实习生,她来带。从此之后,每天晚上都要开一次吐槽大会。 “同一个问题问了我四遍,他是不是傻...
    wsc米亚阅读 2,444评论 0 0
  • 猛然间抬头,发现窗外已是灰蒙蒙的,细雨分分飘落。深秋时分,仿佛还是昨年那个青涩的自己,却不曾想,自己也早已不是从前...
    圆的方阅读 1,371评论 0 0
  • Spring及SpringMVC扫描包隔离及配置文件优化 applicationContext.xml dispa...
    胖达_4b7e阅读 4,780评论 0 0
  • 之前画的,忘记留步骤图了。
    温白M阅读 3,106评论 2 3
  • 案例一: 班里最让人操心的凡凡同学了。他是一年级下学期转来的,性格比较内向,但经常有学生向我告状,说凡凡老打人。打...
    冯姗姗阅读 4,235评论 0 0