《Head First 设计模式》读后感01:策略模式

        之前,也在看设计模式,但是为了赶进度,没有思考,最后一无所获。故从今天开始,养成写博客的习惯,将学习到的和自己的思考记录下来。选择《Head First 设计模式》的原因:相比于《设计模式:可复用面向对象软件的基础》,本书更加容易理解;相比于《大话设计模式》,本书让人有更多的思考过程。

       本书第一章设计模式入门中,提到设计原则:针对接口编程,而不是针对实现编程。书中举得例子是,鸭子(Duck)有飞(fly)和叫(quack-嘎嘎)行为,起初一想,这两个行为不是鸭子的共有行为吗?直接将这两个共有行为放在Duck基类,具体鸭子类继承Duck基类,这样就实现了代码的复用。即使,不是所有鸭子都会叫和飞(如橡皮鸭),也可以在派生类(如RubberDuck橡皮鸭)覆盖基类的的行为。但是这样会使得代码在很多子类中重复(因为有些鸭子会叫但是不会飞,会飞但不会叫),并且如果想添加一个新的行为,则可能牵一发而动全身(因为有些鸭子并没有这些行为)。对于例子中鸭子的飞和叫行为,这两个行为可能会在子类中改变,所以不应该作为共有行为。无论何时何地,当休要修改这两个行为时,必须要到具体子类鸭子中去修改此行为。如果只有5种鸭子,改起来还能接受,如果是50种鸭子,那要改好久。程序可能一直都在变化,如果每次都这么麻烦,那维护太困难了!

       本书,首先提到用接口的形式来改变。添加两个接口,如fly()和quack(),当子类鸭子有这两个行为时,就添加该行为,这时看起来感觉不错,但是如果fly()和quack()有多种情况呢,那么就需要修改fly()和quack()接口中的实现,这看起来似乎更麻烦了。

       解决方法:这里提到一个设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。正如鸭子的fly()和quack(),这两个行为是变化的,需要独立出来。“系统中的某部分改变不会影响其他部分”,也就是说当改变Duck类中的行为,不需要去子类中改变。如何将鸭子的行为fly()和quack()从类中取出呢。

      将鸭子的飞和叫行为,分开放在别的中,此类专门提供某行为接口的实现,这样鸭子类就不再需要知道行为的实现细节,如创建一个FlyBehavior虚基类(多态的好处),提供一个fly()接口,则在所以飞行为子类都要实现此行为,叫也类似。这样,可以让飞和叫的行为可以被其他对象复用,这些行为已经与鸭子无关了,添加新的行为既不会影响已有的行为类,也不会影响"使用"到飞行行为的鸭子类。

 代码实现1:

//飞行行为虚基类
class FlyBehavior 
{
public:
    FlyBehavior();
    virtual ~FlyBehavior();
public:
    virtual int Fly() = 0;        
}

//飞行具体类
class FlyWithWings : public FlyBehavior
{
public:
    FlyWithWings();
    ~FlyWihWings();
public:
    virtual int Fly();
}


//嘎嘎叫行为虚基类
class QuackBehavior
{
public:
    QuackBehavior();
    virtual ~QuackBehavior();
public:
    virtual int Quack() = 0;        
}

//叫具体类
class Squeak : public QuackBehavior
{
public:
   Squeak();
    ~Squeak();
public:
    virtual int Quack();
}

//鸭子类
class Duck
{
public:
    Duck();
    virtual ~Duck();
public:
    int Display();
    void PerformFly(){ m_pFlyBehavior->Fly(); }
    void PerformQuack(){ m_pQuackBehavior-> Quack(); }
    //其他行为
protected:
    FlyBehavior *m_pFlyBehavior;
    QuackBehavior *m_pQuackBehavior;
}


//具体鸭子类
class MallardDuck : public Duck
{
public: 
    MallardDuck();
    ~MallardDuck();
}

MallardDuck :: MallardDuck()
{
    m_pFlyBehavior = new FlyWithWings();
    m_pQuackBehavior =  new Squeak();
}

~MallardDuck :: MallardDuck()
{
}


//测试类
void main()
{
    Duck *mallard = new MallardDuck();
    mallard->PerformFly();
    mallard->PerformQuack();
}

代码1中,鸭子现在将飞行和叫的动作委托(delegate)别人处理,而不是定义在Duck类中。通过子类鸭子的构造函数确定了飞和叫行为的具体实例,但是之前又说过,不对具体实现编程,所以目前初始化行为类实例不够弹性。(思考:在构造函数里动态创建,要在析构函数里释放,如果这里有多个子类鸭子,则会多次创建实例,析构只能释放最后一次创建的实例,这个时候可以将所以创建的实例放到一个容器里,析构的时候一一析构即可)

如何动态设定行为呢?如果行为初始化在子类构造函数中进行,则行为是无法改变的(如从飞变成不飞)。

代码实现2:

      

//在Duck类中添加两个新行为,删除鸭子子类构造函数行为初始化代码
Class Duck
{
    void SetFlyBehavior(FlyBehavior *fb)
    {
        m_pFlyBehavior = fb;
    }

    void SetQuackBehavior(QuackBehavior *qb)
    {
        m_pQuackBehavior = qb;
    }
}

//测试代码
void main()
{
    FlyBehavior *fb = new FlyWithWings();
    Duck *mallard = new mallardDuck();
    mallard->SetFlyBehavior(fb);
    mallard->PerformFly();
}

这里将鸭子的行为类和鸭子类结合起来使用,是一种HAS-A的关系,而不是继承,即IS-A的关系。这种组合关系,有很大的弹性,即可以将行为(算法)封装成类,也可以动态改变行为。这里引入第三个设计原则:多用组合,少用继承。

策略模式的定义:定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

题外话:从决定写这个到写完,大概花了2个小时,以前肯定觉得这是在浪费时间,但是写完发现,自己收获很多,可能表述和代码有错误,希望有大佬能指点一二!

猜你喜欢

转载自blog.csdn.net/songsong2017/article/details/81568333