策略模式心得趣谈

《HeadFirst设计模式》一书非常有趣,图文并茂,诙谐生动,学习时难免喜形于色。相比之下GOF的作品我更愿意作为工具书,最终权威的解读。

 该书的描述为Java语言,但编程的思想是通用的。

开篇讲策略模式。一时心痒难耐,决定举一反三,推出一个C++的示例。

模式是一个套路,里面的基本思想才是精华。毕竟咱们程序员都是讲实战的,理论结合实际才是硬道理。

策略模式用到了三个朴素的基本编程原则,这个不是由谁谁说了就算的,而是由无数先辈们们经过无数次的惨痛教训后总结出来的。当然遵不遵循,则是个人意愿了。

三个原则如下。

① 经常变动的代码应该提出来。

② 多用组合,少用继承。

③ 面向接口编程。

接下来,我会通过示例来讲述一下这三个原则如何在实战中应用。     

在编程的世界里,唯一不变的东西就是需求永远是变的。作为程序员,这点觉悟应该还是要有的。示例中需求也是层层递进。

1、 基本需求及实现

现在你是老板,要雇人干活。干活就要管人家吃饭,统一标准订餐“汉堡包+鸡翅+可乐”。当然为防止有不相关的人来蹭饭,大家吃饭前都先介绍一下自己。

类图是这样的。


代码见FirstStep.cpp。(代码在下面的附录中)

运行结果为:

我是干活的人类
吃汉堡包+鸡翅+可乐

OK,一切正常,工人们也没人抱怨。

2、 需求变更及实现

由于人工智能的飞速进步,你决定雇佣机器人来为你工作。

现在类图变成这样。

策略模式心得趣谈_第1张图片

代码见SecondStep_1.cpp。(代码在下面的附录中)

运行结果为:

我是干活的人类
吃汉堡包+鸡翅+可乐
 
我是干活的机器人
吃汉堡包+鸡翅+可乐

这样的结果,冷汗了吧。看过《人工智能》的都应该记得那个情节,吃了人类食物的机器人要赶紧去修理,晚了可都全完了。

这个时候,你幡然悔悟,机器人不吃饭,而是应该是吃电(充电)。

怎么改呢?覆盖Robot的Eat方法。听起来不错,不过要是再雇佣个吃电的雇员(比如钢铁侠),吃电这个方法不是要实现两次(代码不能复用,改起来也要改两个地方)。

我们的基础三原则应该能提供些帮助。经常变动的代码,Eat方法明显是,提出来。针对接口编程,我们把Eat方法提取为一个接口,调用者不再关心他的具体实现。

类图如下。

    策略模式心得趣谈_第2张图片

代码见SecondStep_2.cpp。(代码在下面的附录中)

运行结果为:

我是干活的人类
吃汉堡包+鸡翅+可乐
 
我是干活的机器人
进行充电

终于正常了^_^。

3、 更多需求变更

你的业务越做越大,来帮你干活的人也多了。大力水手来了,汽车人来了,树人也来了。这些人你也要管饭,大力水手吃菠菜,汽车人吃汽油,树人嘛光合作用就行了。而且,天天吃快餐谁也受不了,工人们要求可以随时换餐。

步骤2里设计的框架很灵活,我们在原来基础上改动不大就可以实现。

Worker类里增加SetEatPtr,可以随时换餐。

类图就变成如下的样子。


代码参见ThirdStep.cpp。(代码在下面的附录中)

运行结果:

我是干活的人类
吃汉堡包+鸡翅+可乐
 
我是干活的机器人
进行充电
 
我是干活的汽车人
加汽油
 
我是干活的树人
进行光合作用
 
我是干活的人类
吃菠菜
 
我是干活的人类
吃大米

好了,现在大家不用一直吃快餐了,有人吃菠菜,有人吃大米,汽车人树人也有自己的进食方式。这套模式便于扩展,比如火星人来给你干活了,直接再实现个火星人吃饭的算法即可。


策略模式归总

现在大家明白了吧,策略模式,就是把经常改变的算法提取出来,形成一个算法族。算法族下的算法可以相互替换。

但是最重要的东西,我认为还是开头提到的三原则,根据这些原则就可以研究出自己的模式。^_^


代码附录

FirstStep.cpp

#pragma once

// StrategyPattern.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class Worker
{
public:
    Worker()
        : m_strIntroduce("我是干活的")
    {}
    virtual ~Worker() {}

    //自我介绍
    virtual void Introduce()
    {
        cout<<m_strIntroduce.c_str()<<endl;
    }

    //吃饭
    virtual void Eat()
    {
        cout<<"吃汉堡包+鸡翅+可乐"<<endl;
    }

protected:
    string m_strIntroduce;
};

class Human : public Worker
{
public:
    Human() : Worker()
    {
        m_strIntroduce += "人类";
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Worker& worker_a = Human();

    worker_a.Introduce();
    worker_a.Eat();

    cout<<endl;

    return 0;
}

SecondStep_1.cpp

// StrategyPattern.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class Worker
{
public:
    Worker()
        : m_strIntroduce("我是干活的")
    {}
    virtual ~Worker() {}

    //自我介绍
    virtual void Introduce()
    {
        cout<<m_strIntroduce.c_str()<<endl;
    }

    //吃饭
    virtual void Eat()
    {
        cout<<"吃汉堡包+鸡翅+可乐"<<endl;
    }

protected:
    string m_strIntroduce;
};

class Human : public Worker
{
public:
    Human() : Worker()
    {
        m_strIntroduce += "人类";
    }
};

class Robot : public Worker
{
public:
    Robot() : Worker()
    {
        m_strIntroduce += "机器人";
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Worker& worker_a = Human();

    worker_a.Introduce();
    worker_a.Eat();
    cout<<endl;

    Worker& worker_b = Robot();

    worker_b.Introduce();
    worker_b.Eat();
    cout<<endl;

    return 0;
}

SecondStep_2.cpp

// StrategyPattern.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class EatInterface
{
public:
    virtual ~EatInterface() {}

    //提供吃的接口
    virtual void Eat() = 0;
};

class EatFastFood : public EatInterface
{
public:
    virtual void Eat()
    {
        cout<<"吃汉堡包+鸡翅+可乐"<<endl;
    }
};

class EatBattery : public EatInterface
{
public:
    virtual void Eat()
    {
        cout<<"进行充电"<<endl;
    }
};

class Worker
{
public:
    Worker()
        : m_strIntroduce("我是干活的")
        , pEatPtr(NULL)
    {}

    virtual ~Worker()
    {
        if (pEatPtr != NULL)
        {
            delete pEatPtr;
            pEatPtr = NULL;
        }
    }

    //自我介绍
    virtual void Introduce()
    {
        cout<<m_strIntroduce.c_str()<<endl;
    }

    //吃饭
    virtual void SimulateEat()
    {
        pEatPtr->Eat();
    }

protected:
    string m_strIntroduce;
    EatInterface* pEatPtr;
};

class Human : public Worker
{
public:
    Human() : Worker()
    {
        m_strIntroduce += "人类";
        pEatPtr = new EatFastFood;
    }
};

class Robot : public Worker
{
public:
    Robot() : Worker()
    {
        m_strIntroduce += "机器人";

        pEatPtr = new EatBattery;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Worker& worker_a = Human();

    worker_a.Introduce();
    worker_a.SimulateEat();
    cout<<endl;

    Worker& worker_b = Robot();

    worker_b.Introduce();
    worker_b.SimulateEat();
    cout<<endl;

    return 0;
}

ThirdStep.cpp

// StrategyPattern.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class EatInterface
{
public:
    virtual ~EatInterface() {}

    //提供吃的接口
    virtual void Eat() = 0;
};

class EatFastFood : public EatInterface
{
public:
    virtual void Eat()
    {
        cout<<"吃汉堡包+鸡翅+可乐"<<endl;
    }
};

class EatRice : public EatInterface
{
public:
    virtual void Eat()
    {
        cout<<"吃大米"<<endl;
    }
};

class EatSpinach : public EatInterface
{
public:
    virtual void Eat()
    {
        cout<<"吃菠菜"<<endl;
    }
};

class EatBattery : public EatInterface
{
public:
    virtual void Eat()
    {
        cout<<"进行充电"<<endl;
    }
};

class EatOil : public EatInterface
{
public:
    virtual void Eat()
    {
        cout<<"加汽油"<<endl;
    }
};

class EatLight : public EatInterface
{
public:
    virtual void Eat()
    {
        cout<<"进行光合作用"<<endl;
    }
};

class Worker
{
public:
    Worker()
        : m_strIntroduce("我是干活的")
        , m_pEatPtr(NULL)
    {}

    virtual ~Worker()
    {
        ClearEatPtr();
    }

    void SetEatPtr(EatInterface* pEatPtr)
    {
        ClearEatPtr();
        m_pEatPtr = pEatPtr;
    }

    //自我介绍
    virtual void Introduce()
    {
        cout<<m_strIntroduce.c_str()<<endl;
    }

    //吃饭
    virtual void SimulateEat()
    {
        if (m_pEatPtr != NULL)
        {
            m_pEatPtr->Eat();
        }
    }

private:
    void ClearEatPtr()
    {
        if (m_pEatPtr != NULL)
        {
            delete m_pEatPtr;
            m_pEatPtr = NULL;
        }
    }

protected:
    string m_strIntroduce;
    EatInterface* m_pEatPtr;
};

class Human : public Worker
{
public:
    Human() : Worker()
    {
        m_strIntroduce += "人类";
        SetEatPtr(new EatFastFood);
    }
};

class Robot : public Worker
{
public:
    Robot() : Worker()
    {
        m_strIntroduce += "机器人";
        SetEatPtr(new EatBattery);
    }
};

class CarMan : public Worker
{
public:
    CarMan() : Worker()
    {
        m_strIntroduce += "汽车人";
        SetEatPtr(new EatOil);
    }
};

class TreeMan : public Worker
{
public:
    TreeMan() : Worker()
    {
        m_strIntroduce += "树人";
        SetEatPtr(new EatLight);
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Worker& worker_a = Human();
    worker_a.Introduce();
    worker_a.SimulateEat();
    cout<<endl;

    Worker& worker_b = Robot();
    worker_b.Introduce();
    worker_b.SimulateEat();
    cout<<endl;

    Worker& worker_c = CarMan();
    worker_c.Introduce();
    worker_c.SimulateEat();
    cout<<endl;

    Worker& worker_d = TreeMan();
    worker_d.Introduce();
    worker_d.SimulateEat();
    cout<<endl;

    Worker& worker_e = Human();
    worker_e.SetEatPtr(new EatSpinach);
    worker_e.Introduce();
    worker_e.SimulateEat();
    cout<<endl;

    Worker& worker_f = Human();
    worker_f.SetEatPtr(new EatRice);
    worker_f.Introduce();
    worker_f.SimulateEat();
    cout<<endl;

    return 0;
}


你可能感兴趣的:(C++,C++,策略模式)