1.概述

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

2.实例

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

2.1丑陋的设计

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

  1. //基类Duck
  2. class Duck{
  3. public:
  4. //呱呱叫
  5. virtual void quack(){
  6. cout<<"呱呱叫"<<endl;
  7. }
  8. //游泳
  9. virtual void swim(){
  10. cout<<"开始游泳"<<endl;
  11. }
  12. //飞行
  13. virtual void fly(){
  14. cout<<"开始飞行"<<endl;
  15. }
  16. //外观展示(纯虚函数,由子类实现)
  17. virtual void display()=0;
  18. };
  19. //绿头野鸭
  20. class MallardDuck{
  21. public:
  22. virtual void display(){
  23. //外观是绿头
  24. }
  25. };
  26. //红头鸭
  27. class RedheadDuck{
  28. public:
  29. virtual void display(){
  30. //外观是红头
  31. }
  32. };

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

  1. class RubberDuck:public Duck{
  2. public:
  3. virtual void fly(){
  4. cout<<"我不能飞行"<<endl;
  5. }
  6. virtual void quack(){
  7. cout<<"吱吱叫"<<endl;
  8. }
  9. };

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

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

2.2合适的设计

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

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

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

  1. //飞行接口,所有飞行类都实现它,所有新的飞行类都必须实现fly()方法
  2. class FlyBehavior{
  3. public:
  4. virtual void fly()=0;
  5. };
  6. //翅膀飞行类
  7. class FlyWithWings:public FlyBehavior{
  8. public:
  9. virtual void fly(){
  10. //实现鸭子飞行
  11. }
  12. };
  13. //不飞行类
  14. class FlyNoWay:public FlyBehavior{
  15. public:
  16. virtual void fly(){
  17. //什么都不做,鸭子不飞行
  18. }
  19. };
  20. //呱呱叫行为接口,一个接口只包含一个需要实现的quack()方法
  21. class QuackBehavior{
  22. public:
  23. virtual void quack()=0;
  24. };
  25. //真的呱呱叫
  26. class Quack:public QuackBehavior{
  27. public:
  28. virtual void quack(){//实现鸭子呱呱叫}
  29. };
  30. //吱吱叫
  31. class Squeak:public QuackBehavior{
  32. public:
  33. virtual void quack(){//实现鸭子吱吱叫}
  34. };
  35. //不叫
  36. class MuteQuack:public QuackBehavior{
  37. public:
  38. virtual void quack(){//什么都不做,不会叫}
  39. };

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

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

  1. //橡胶鸭类
  2. class RubberDuck {
  3. private:
  4. FlyBehavior* flyBehavior;
  5. QuackBehavior* quackBehavior;
  6. public:
  7. RubberDuck() {
  8. flyBehavior = new FlyNoWay();
  9. quackBehavior = new Squeak();
  10. }
  11. //飞行行为
  12. void performFly() {
  13. flyBehavior->fly();
  14. }
  15. //改变飞行行为
  16. void setFlyBehavior(FlyBehavior* flyB) {
  17. flyBehavior = flyB;
  18. }
  19. //呱呱叫行为
  20. void performQuack() {
  21. quackBehavior->quack();
  22. }
  23. //改变叫声
  24. void setPerformQuack(QuackBehavior* quackB) {
  25. quackBehavior = quackB;
  26. }
  27. //其他行为方法及析构函数
  28. ...
  29. };

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

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

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

3.小结

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

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

(3)本文提到的OO原则有:

(3.1)封装变化。找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起;

(3.2)针对接口编程,不针对实现编程;

(3.3)多用组合,少用继承。

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

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


参考文献

[1]标准C++中有没有接口和纯抽象类的概念?

[2]Freeman E.,Freeman E.,Sierra K.,et al.设计模式[M].第一版O’Reilly Taiwan公司译.北京:中国电力出版社,2015:1-32

设计模式 (一)——策略模式(Strategy,行为型)的更多相关文章

  1. 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)

    原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...

  2. JAVA设计模式之策略模式 - Strategy

    在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改.这种类型的设计模式属于行为型模式. 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 ...

  3. 反馈法学习设计模式(一)——策略模式Strategy Pattern

    简介(Introduction) 之前学习Java8实战时,遇到一个很好的策略模式示例.便想着借着这个示例结合反馈式的方法来,学习策略设计模式,也以便后面反复琢磨学习. 首先我们通过练习,逐步写出符合 ...

  4. 设计模式 笔记 策略模式 Strategy

    //---------------------------15/04/28---------------------------- //Strategy 策略模式----对象行为型模式 /* 1:意图 ...

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

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

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

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

  7. 行为型设计模式之策略模式(Strategy)

    结构 意图 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换.本模式使得算法可独立于使用它的客户而变化. 适用性 许多相关的类仅仅是行为有异.“策略”提供了一种用多个行为中的一个行为来配 ...

  8. 大熊君说说JS与设计模式之------策略模式Strategy

    一,总体概要 1,笔者浅谈 策略模式,又叫算法簇模式,就是定义了不同的算法,并且之间可以互相替换,此模式让算法的变化独立于使用算法的客户. 策略模式和工厂模式有一定的类似,策略模式相对简单容易理解,并 ...

  9. [设计模式] 21 策略模式 Strategy

    在GOF的<设计模式:可复用面向对象软件的基础>一书中对策略模式是这样说的:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.该模式使得算法可独立于使用它的客户而变化. 策略模 ...

  10. 设计模式之策略模式Strategy

    /** * 策略设计模式 * 策略模式:定义一系列的算法族,使他们之间可以相互转换,动态的改变其行为. * 问题:设计一个鸭子模拟游戏. * 现在有一群鸭子: * ①这些鸭可以有飞的行为(分为快和慢) ...

随机推荐

  1. anaconda+pycharm的安装和应用

    至于anaconda的安装与pycharm的安装在此不做多说,主要说下遇到的问题. 问题描述: 安装anaconda后,pip下载的第三方库调用不到. 原因分析: anaconda自带的python3 ...

  2. java多线程相关代码

    1.创建线程的三种方式 使用Thread package com.wpbxx.test; //1.自定义一个类,继承java.lang包下的Thread类 class MyThread extends ...

  3. K-SVD字典学习及其实现(Python)

    算法思想 算法求解思路为交替迭代的进行稀疏编码和字典更新两个步骤. K-SVD在构建字典步骤中,K-SVD不仅仅将原子依次更新,对于原子对应的稀疏矩阵中行向量也依次进行了修正. 不像MOP,K-SVD ...

  4. Spark概述及集群部署

    Spark概述 什么是Spark (官网:http://spark.apache.org) Spark是一种快速.通用.可扩展的大数据分析引擎,2009年诞生于加州大学伯克利分校AMPLab,2010 ...

  5. 论文笔记:分形网络(FractalNet: Ultra-Deep Neural Networks without Residuals)

    FractalNet: Ultra-Deep Neural Networks without Residuals ICLR 2017 Gustav Larsson, Michael Maire, Gr ...

  6. Nginx是如何配置为 Web 服务器的【转载】

    详解 Nginx是如何配置为 Web 服务器的 林涛 发表于:2016-11-29 23:23 分类:WebServer 标签:Nginx,web,web服务器 521次 抽象来说,将 Nginx 配 ...

  7. CF 1095C Powers Of Two(二进制拆分)

    A positive integer xx is called a power of two if it can be represented as x=2y, where y is a non-ne ...

  8. Visual Studio AI配置环境

    参考链接:http://www.cnblogs.com/ms-uap/p/9123033.html 背景: 1.能联网的电脑:Win7 64 SP1 2.鼠标.键盘.显示器好使 3.已安装VS2010 ...

  9. python下graphviz安装

    参考链接:https://blog.csdn.net/u013250416/article/details/72790754 1.安装Graphviz 在graphviz的官网(网址:http://w ...

  10. Daily Scrum6 11.10

    今日任务: 徐钧鸿:codingcook的sql相关内容,并在进行复查张艺:继续用户管理部分代码黄可嵩:学习搜索的知识,继续进行搜索的移植和响应徐方宇:动态控件和页面间信息传递以及页面响应事件机制试验 ...