文章目录

  • 1.概述
  • 2.实例
    • 2.1 丑陋的设计
    • 2.2 合适的设计
  • 3.小结
  • 参考文献

1.概述

使用设计模式可以提高代码的可复用性、可扩充性和可维护性。策略模式(Strategy Pattern)属于行为型模式,其做法是将类所需的行为或者算法一个个封装成单独的类,并将其作为类的数据成员,使得类的行为可以在不改变类设计的情况下灵活变化。

2.实例

上面对策略模式的概述难免显得抽象,难于理解,下面给出实例,让我们在实际应用中感受策略模式的做法和作用。

2.1 丑陋的设计

比如现在我们要设计一款模拟鸭子的游戏:游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫。我们采用面向对象技术,先设计一个基类Duck,然后让各种鸭子继承该基类。代码如下:

//基类Duck
class Duck {public://呱呱叫virtual void quack(){    cout<<"呱呱叫"<<endl;}//游泳virtual void swim(){cout<<"开始游泳"<<endl;}//飞行virtual void fly(){cout<<"开始飞行"<<endl;}//外观展示(纯虚函数,由子类实现)virtual void display()=0;
};//绿头野鸭
class MallardDuck {public:virtual void display(){//外观是绿头}
};//红头鸭
class RedheadDuck {public:virtual void display(){//外观是红头}
};

上面的设计使用类继承的方式,复用了不同种类鸭子的公共行为,如游泳、呱呱叫和飞行,也就实现了代码复用。一切看似完美,但是现在我们需要在游戏中增加一个橡皮鸭。按照前面的思维,我们顺其自然的让新的类 RubberDuck 继承基类 Duck,但是橡皮鸭不能飞,叫的声音也和绿头野鸭和红头鸭不一样,于是我们需要在 RubberDuck 重写基类的 fly 和quack 方法。

class RubberDuck:public Duck{public:virtual void fly(){cout<<"我不能飞行"<<endl;}virtual void quack(){    cout<<"吱吱叫"<<endl;}
};

现在我们是否能暗自庆幸通过虚函数重写的方法解决了上面的问题,觉得上面的设计令人满意。但是问题来了,如果游戏每六个月更新一次,每次需要加入新的鸭子子类,我们就要被迫检查可能需要重写的 fly() 和quack()… 甚至需要更改基类 Duck 的设计,加入新的行为方法,这简直是无穷无尽的噩梦。

因为,并非所有的子类都具有飞行和呱呱叫的行为,所以继承并不是合适的解决方式。所以,我们需要让“某些”(而不是全部)鸭子类型可飞或可叫。

2.2 合适的设计

现在,我们需要坚持一个面向对象的设计原则:**封装变化。找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。**该原则把应用中会变化的部分抽取出来封装起来,以便以后可以轻易地改动或扩充,而不影响不需要变化的其他部分。

如何设计那组实现飞行和呱呱叫的行为的类呢?我们希望一切能有弹性,能够在游戏运行的过程中动态的改变鸭子的行为。也就是说,我们应该在鸭子类中包含设定行为的方法。有了这个设计目标,我们需要坚持第二个设计原则:**针对接口编程,而不是针对实现编程。**这里要说明一下,C++的接口指的是抽象类,含有纯虚函数的类[1]^{[1]}[1]。

在此,我们设计两个接口,FlyBehavior 和 QuackBehavior,还有他们对应的子类,负责实现具体的行为:

// 飞行接口,所有飞行类都实现它,所有新的飞行类都必须实现fly()方法
class FlyBehavior {
public:virtual void fly()=0;
};// 翅膀飞行类
class FlyWithWings:public FlyBehavior{
public:virtual void fly(){//实现鸭子飞行}
};// 不飞行类
class FlyNoWay:public FlyBehavior{
public:virtual void fly(){//什么都不做,鸭子不飞行}
};// 呱呱叫行为接口,一个接口只包含一个需要实现的 quack() 方法
class QuackBehavior{
public:virtual void quack()=0;
};// 真的呱呱叫
class Quack:public QuackBehavior{
public:virtual void quack(){//实现鸭子呱呱叫}
};// 吱吱叫
class Squeak:public QuackBehavior{
public:virtual void quack(){//实现鸭子吱吱叫}
};// 不叫
class MuteQuack:public QuackBehavior{
public:virtual void quack(){//什么都不做,不会叫}
};

这样的设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经与鸭子类无关了。此外,我们也可以新增一些行为,不会影响到既有的行为类,也不会影响“使用”到飞行行为的鸭子类。

下面整合鸭子的行为。将行为类对象作为鸭子类的数据成员,并提供动态改变行为的方法,具体实现如下:

//橡胶鸭类
class RubberDuck {private:FlyBehavior* flyBehavior;QuackBehavior* quackBehavior;
public:RubberDuck() {flyBehavior = new FlyNoWay();quackBehavior = new Squeak();}//飞行行为void performFly() {flyBehavior->fly();}//改变飞行行为void setFlyBehavior(FlyBehavior* flyB) {flyBehavior = flyB;}//呱呱叫行为void performQuack() {quackBehavior->quack();}//改变叫声void setPerformQuack(QuackBehavior* quackB) {quackBehavior = quackB;}//其他行为方法及析构函数...
};

上面就可以动态地改变了鸭子对象的飞行方式和呱呱叫行为,因为每一个鸭子都有一个 FlyBehavior 和一个 QuackBehavior,将飞行和呱呱叫委托给它们代为处理。

当你将两个类结合起来使用,如同上面的例子,这就是组合(composition)。这种做法和“继承”不同的地方在于,鸭子的行为不是继承来的,而是和适当的行为对象“组合”而来。这是一个很重要的技巧,其实是使用了我们的第三个设计原则:多用组合,少用继承。

如你所见,使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以“在运行时动态地改变行为“。组合可以用在许多设计模式中,有优点,但也有缺点。

3.小结

(1)使用设计模式可以提高代码的可复用性、可扩充性和可维护性;

(2)OO(Object Oriented,面向对象)的基础是:抽象、封装、继承与多态;

(3)本文提到的OO原则有:
(a)封装变化。找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起;
(b)针对接口编程,不针对实现编程;
(c)多用组合,少用继承。

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

(5)在面向对象设计时,没有找到适当的模式解决问题时,要想建立可维护的OO系统,要诀在于随时想到系统以后可能需要的变化以及应对变化的原则。


参考文献

[1] 标准C++中有没有接口和纯抽象类的概念?
[2] Freeman E.,Freeman E.,Sierra K.,et al.设计模式[M].第一版O’Reilly Taiwan公司译.北京:中国电力出版社,2015:1-32

行为型设计模式(5)—— 策略模式(Strategy Pattern)相关推荐

  1. 8.6 GOF设计模式四: 策略模式… Strategy Pattern

    策略模式- Strategy Pattern  在POS系统中,有时需要实行价格优惠, 该如何处理?  对普通客户或新客户报全价  对老客户统一折扣5%  对大客户统一折扣10%  注:课件 ...

  2. 二十四种设计模式:策略模式(Strategy Pattern)

    策略模式(Strategy Pattern) 介绍 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.本模式使得算法的变化可独立于使用它的客户. 示例 有一个Message实体类,对它的操 ...

  3. 锈才学设计模式之 —— 策略模式(Strategy Pattern)

    锈才学设计模式之  -- 策略模式 策略模式:把功能提供者单独封装成类,使它们可以互相替换使用,让功能提供者独立于使用者(调用者). 说明: 在面向对象编程中,我们尽量将功能(类)设计成复用,以符合O ...

  4. 设计模式实战-策略模式(Strategy Pattern)

    0 联系我 图片标题 1.Q群[Java开发技术交流]:jq.qq.com/?_wv=1027&a- 2.完整博客链接:www.shishusheng.com 3.知乎:www.zhihu.c ...

  5. 解读设计模式----策略模式(Strategy Pattern)

    一.模式概述      策略模式(Strategy Pattern)在外形上与状态模式很相似,但在意图上有些不同.其意图是使这些算法可以相互替换,并提供一种方法来选择最合适的算法.       在我应 ...

  6. 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)

    在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了"工厂模式"."策略模式" ...

  7. 关于设计模式——策略模式-Strategy Pattern

    文章目录 1 策略模式 1.1 模拟鸭子 1.2 设计原则 1.3 整合行为 1.4 模拟鸭子代码的代码 1.5 动态设定行为 1.6 重新查看整体 1.7 继承和组合 1.8 总结 1.9 优劣期间 ...

  8. 设计模式-策略模式(Strategy Pattern)

    Define a family of algorithms,encapsulate each one,and make them interchangeable(定义一组算法,将每个算法都封装起来,并 ...

  9. 设计模式——策略模式( Strategy Pattern )

    写代码超过千行,自定义类10个以上,往往写着写着就觉的乱,最终决定抽空补习下设计模式方面的知识.本来AS3出了本关于设计模式的书的,国人也翻译了.我同事也早早买了一本,但前些天我借来看了几页,翻译的太 ...

  10. 设计模式之策略模式(Strategy)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

最新文章

  1. mysql 数据表 时间自动_MySQL数据库时间设置自动添加时间和自动更新时间
  2. [轉]如果把HTML當成飾品....
  3. android权限控制泄露,Android应用的权限泄露分析
  4. 主成分分析 PCA算法
  5. 时间管理神器:滴答清单之我最喜欢的特征
  6. 2019年暑假慈溪集训(更新ing)
  7. 【资源分享】CS起源 V34.4044(经典版本)
  8. word文档图标变成白纸_挽救你的文件 修复变成乱码的Word文档
  9. 校验功能算eo还是ilf_如何区分ILF和EIF?
  10. Qt奇淫技巧-使用QSharedMemory方式实现数据跨界面传输
  11. pycharm是python2.还是3_Pycharm:Python2和3及其的Anaconda的正确设置
  12. linux raid 卡日志,Linux系统下RAID卡异常日志收集方法、系统及存储介质与流程
  13. VMware Workstation 与 Device/Credential Guard 不兼容。在禁用 Device/Credential Guard
  14. html头像生成器,2020头像生成器
  15. AlexNet 实现猫狗分类(keras and pytorch)
  16. 多线程之基于积分法与欧拉恒等式法的圆周率计算及OMP优化
  17. Cocos2dx版本介绍【至3.10版】
  18. SQL语法分析-基础篇
  19. win10网络邻居看到linux,win10网络邻居找不到其他电脑怎么办
  20. 适合自学单片机c语言教材,单片机编程入门看什么书 盘点单片机初学者适合看的书...

热门文章

  1. Java-JUC(六):创建线程的4种方式
  2. Maven插件:versions-maven-plugin
  3. systemtap gui
  4. [kuangbin带你飞]专题九 连通图
  5. linux终端输入lsblk无命令,lsblk
  6. matlab波形反白,基于MATLAB的海岸污染物浓度扩散实验分析
  7. 蓝桥杯 ADV-154 算法提高 质数的后代
  8. [Java] 蓝桥杯ADV-166 算法提高 聪明的美食家
  9. [Python] L1-003. 个位数统计-PAT团体程序设计天梯赛GPLT
  10. 【note】PAT甲级题目中的单词整理