1.策略模式

2019-03-24  本文已影响0人  你今天作业做了吗

模拟鸭子应用

需求

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

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

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

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

以上缺点:

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

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

于是代码变成了如下:


// 定义飞行行为抽象类(接口),
// 将鸭子类中经常变化的部分进行分离与封装。
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;
    }
};

上一篇 下一篇

猜你喜欢

热点阅读