设计模式1:策略模式(C++实现)

最近在读“Head First 设计模式”,书中的例子是Java写的,自己用C++实现一遍加深理解。

1、背景环境

某公司做了一套模拟鸭子游戏,游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫。此系统的内部设计使用了标准的OO技术,设计了一个鸭子基类,并让各种鸭子继承此基类。

class Duck
{
public:
        void swim() {cout << "All ducks float, even decoys!" << endl;}
};
class MallardDuck: public Duck
{......};

后来由于创新性需求,需要为鸭子增加一种技能:飞行,该怎么实现呢?

有几种方法可以实现:

1、在基类中添加fly()方法

由于不是所有鸭子都会飞,所以这种方法行不通

2、考虑到不是所有的鸭子都会飞,可在Duck基类中将fly()方法设置为虚函数,则会飞的继承fly(),不会飞的重写fly()

如果不小心忘了重写fly()方法,则会带来错误的结果,所以该方法貌似也行不通

3、在Duck基类中将fly()方法设置为纯虚函数,此时只有基类的接口会被继承,缺省的实现不会被继承

感觉该方法可行

2、策略模式

到目前为止,并没有使用任何设计模式,但问题已经解决了。实际上用不用设计模式,取决于实际需求,也取决于开发者

2.1 适用场景

《Design Patterns》中,关于策略模式的适用场景,如下所示:

1) many related classes differ only in their behavior

2) you need different variants of an algorithm

3) an algorithm uses data that clients shouldn't know about

4) a class defines many behaviors, and these appear as multiple conditional statements in its operations

显然,鸭子的各个派生类属于“related class”。关键就在于“飞行”这个行为,如果只是将飞行行为简单划分为飞和不会飞,则不用设计模式完全可以。但假如对鸭子的需求不只是能飞和不能飞,还有飞行方式的多样性。则随着派生类的增多飞行方式也会增加至几十种;或者视“飞行方式”为一种算法,以后还会不断改进;再或者将“飞行方式”作为封装算法供第三方使用。那么此时设计模式的价值就体现出来了——易复用,易扩展,易维护。

2.2 设计原则

I、隔离变化

找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。Duck基类中变化之处是飞行行为,故把fly行为从Duck类中分离出来。

II、针对接口编程,而不是针对实现编程。

接口对应于C++就是抽象基类,故将fly行为封装到FlyBehavior抽象基类中,并在其不同子类中实现不同的fly方法

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

class FlyWithWings: public FlyBehavior
{
public:
        virtual void fly()
        {
            cout << "I'm flying!" << endl;
        }
};

class FlyNoWay : public FlyBehavior
{
public:
        virtual void fly()
        {
	    cout << "I can't fly" << endl;
        }
};

III、委托>继承 

公有继承即是is-a;而委托是Delegation即composition by reference(与has-a关系不同),我有接口,但将实现委托给别人做。如你所见,使用委托建立系统具有很大的弹性,不仅可将算法簇封装成类,更可以“在运行时动态地改变行为”。接口不变,但实现可以由不同的实现类(FlyWithWings、FlyNoWay)完成。

在Duck类中,声明FlyBehavior类指针,即:Duck现在拥有接口FlyBehavior类指针,但将实现委托给FlyBehavior的不同实现类完成。

2.3 策略模式定义

Defines a family of algorithms,  encapsulates each one,  and makes them interchangeable.  Strategy lets the algorithm vary independently from clients that use it.


Context指向Strategy(由指针实现);Context通过Strategy接口,调用一系列算法;ConcreteStrategy实现了一系列具体的算法。

3、代码分析

Duck.h:

#ifndef DUCK_H_
#define DUCK_H_
#include "FlyBehavior.h"
#include "QuackBehavior.h"
#include <iostream>
using namespace std;

class Duck
{
public:
	Duck(FlyBehavior* ptr_fB);
	virtual ~Duck() {}
	void swim()
	{
		cout << "All ducks float, even decoys!" << endl;
	}
	void performFly();
	void setFlyBehavior(FlyBehavior* ptr_fB);
private:
	//为行为接口类型声明两个指针变量,所有鸭子子类都继承它们
	FlyBehavior* flyBehavior;
};

class MallardDuck: public Duck
{
public:
	MallardDuck(FlyBehavior* ptr_fB);
};

#endif

FlyBehavior.h:

FiyWithWings和FlyNoWay都是飞行行为,可以用统一的接口FlyBehavior表示;再写2个实现类FlyWithWings和FlyNoWay,毕竟这是2个不同的飞行行为

#ifndef FLYBEHAVIOR_H_
#define FLYBEHAVIOR_H_
#include <iostream>
using namespace std;

// C++中接口用纯虚基类实现
class FlyBehavior
{
public:
	virtual void fly() = 0;
};

class FlyWithWings: public FlyBehavior
{
public:
	virtual void fly()
	{
		cout << "I'm flying!" << endl;
	}
};

class FlyNoWay : public FlyBehavior
{
public:
	virtual void fly()
	{
		cout << "I can't fly" << endl;
	}
};

#endif

Duck.cpp:

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

Duck::Duck(FlyBehavior* ptr_fB): flyBehavior(ptr_fB) {}

void Duck::performFly()
{
	// 鸭子对象不亲自处理飞行行为,而是委托给flyBehavior引用的对象
	flyBehavior->fly();
}

//动态设定行为
void Duck::setFlyBehavior(FlyBehavior* ptr_fB)
{
	flyBehavior = ptr_fB;
}

MallardDuck::MallardDuck(FlyBehavior* ptr_fB): Duck(ptr_fB) {}

MiniDuckSimulator.cpp:

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

int main()
{
	FlyBehavior* ptrFlyWithWings = new FlyWithWings;
	FlyBehavior* ptrFlyNoWay = new FlyNoWay;

	Duck* mallard = new MallardDuck(ptrFlyNoWay);
 // 这会调用MallardDuck继承来的performFly方法,进而委托给该对象的FlyBehavior对象处理(也就是说,调用继承来的flyBehavior引用对象的fly())
	mallard->performFly(); 

	cout << "\nDynamic change the flying mode: " << endl;
	mallard->setFlyBehavior(ptrFlyWithWings);
	mallard->performFly();
       
        //基类中的析构函数必须是虚函数,delete才能调用指针所指对象的对应析构函数
	delete mallard;
	delete ptrFlyNoWay;
	delete ptrFlyWithWings;
	system("pause");
}

4、参考资料

Head First 设计模式

Effective C++

https://www.cnblogs.com/xinxue/p/5271184.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值