策略模式

策略模式定义了一系列算法和行为(也就是策略),他们可以在运行时相互替换。通常把这些算法封装为一个个独立的类,形成一个“算法族”,这一族算法有相同的接口。

这里的算法和行为的含义都是指某件事的做法,也就是策略 Strategy 的含义,而不局限于计算机科学中的具体算法。本文中会混用算法、行为和策略。含义是一样的。

例如新的税法颁布,计算个人所得税的新旧两种算法算一个算法族;试想如果将计算个人所得税的方法硬编码在用户类中,当新的税法颁布的时候,就要重写用户类的相关代码, 而如果使用策略模式,只需要遵循接口实现一个新的所的税算法类就行了,用户类只要确保成员变量设置为新的类的对象就行了。系统从而更有弹性和可维护性

应用案例

这个例子来源于 《Head First 设计模式》:某公司有个鸭子仿真器这样一套系统,可以模拟各种鸭子。原先有 Mollard Duck(绿头鸭),是一种野鸭,可以飞,可以呱呱叫; 现在新加入一种模型鸭 (ModelDuck),因为是模型,不能飞也不能叫; 面向对象的思想,很自然的我们将鸭子Duck 作为基类,MollardDuck 和 ModelDuck 继承Duck类,然后分别实现或者重写 fly() 和 quack() 方法。考虑到鸭子子类越来越多,大白鸭,小黄鸭,火箭鸭,丑小鸭... 这些鸭子的fly() 方法和quack()方法各异,每实现一个子类就要写一遍这些方法,而这些方法有些鸭种是相同的,如果反复重写这些方法就无法复用代码了; 而且对于丑小鸭来说,小的时候不会飞长大了是会飞的,丑小鸭变身——白天鹅。

如何提高复用性和弹性呢?答案是策略模式。将 flyBehavior 和 quackBehavior作为类的成员,将 flyBehavior和quackBehavior也封装为基类,使用多态达到多种行为(策略、算法)的封装和复用。

实现的关键

将算法、策略封装为类,语言特性即为多态技术。

Talk is cheap,let's See The Code

让我们直接看代码吧。https://github.com/sunchaothu/DesignPatternsCpp_Practice.git

1.定义鸭子基类

Duck.h

  1. #ifndef STRATEGY_PATTERN_DUCK_H
  2. #define STRATEGY_PATTERN_DUCK_H
  3. #include "QuackBehavior.h"
  4. #include "FlyBehavior.h"
  5. #include <memory>
  6. class Duck {
  7. public:
  8. virtual ~Duck();
  9. void performFly();
  10. void performQuack();
  11. virtual void display()=0;
  12. void swim();
  13. void setFlyBehavior(FlyBehavior* flyBehavior);
  14. void setquackBehavior(QuackBehavior* quackBehavior) ;
  15. protected:
  16. std::shared_ptr<FlyBehavior> flyBehavior_ptr;
  17. std::shared_ptr<QuackBehavior> quackBehavior_ptr;
  18. };
  19. #endif //STRATEGY_PATTERN_DUCK_H

注意到,这里用flyBehavior 和 quackBehavior 作为鸭子类的成员,实际上是提供了接口,后面我们会注意到,C++的抽象类指针可以

作为接口。这里用C++11 标准库的智能指针share_ptr管理内存,可以免去delete烦恼。

和鸭子基类类的实现 Duck.cpp

  1. #include <iostream>
  2. #include "Duck.h"
  3. Duck::~Duck() {
  4. }
  5. void Duck::swim() {
  6. std::cout<<"As a duck, swim is a default skill!"<<std::endl;
  7. }
  8. void Duck::performFly() {
  9. flyBehavior_ptr->fly();
  10. }
  11. void Duck::performQuack() {
  12. quackBehavior_ptr->quack();
  13. }
  14. void Duck::setFlyBehavior(FlyBehavior* flyBehavior) {
  15. flyBehavior_ptr.reset(flyBehavior);
  16. }
  17. void Duck::setquackBehavior(QuackBehavior* quackBehavior) {
  18. quackBehavior_ptr.reset(quackBehavior);
  19. }

2.几种鸭子派生类

MallardDuck.h

  1. #ifndef STRATEGY_PATTERN_MALLARDDUCK_H
  2. #define STRATEGY_PATTERN_MALLARDDUCK_H
  3. #include "Duck.h"
  4. class MallardDuck:public Duck{
  5. public:
  6. MallardDuck();
  7. ~MallardDuck();
  8. void display();
  9. };
  10. #endif //STRATEGY_PATTERN_MALLARDDUCK_H

MallardDuck.cpp

  1. #include "MallardDuck.h"
  2. #include "FlyBehavior.h"
  3. #include "QuackBehavior.h"
  4. #include <iostream>
  5. #include <memory>
  6. MallardDuck::MallardDuck() {
  7. FlyWithWings *f_ptr = new FlyWithWings();
  8. QuackNormal *q_ptr = new QuackNormal();
  9. setquackBehavior(q_ptr);
  10. setFlyBehavior(f_ptr);
  11. }
  12. MallardDuck::~MallardDuck(){
  13. }
  14. void MallardDuck::display() {
  15. std::cout<<"I am a MallardDuck."<<std::endl;
  16. }

ModelDuck.h

  1. #ifndef STRATEGY_PATTERN_MODELDUCK_H
  2. #define STRATEGY_PATTERN_MODELDUCK_H
  3. #include "Duck.h"
  4. class ModelDuck:public Duck{
  5. public:
  6. ModelDuck();
  7. ~ModelDuck();
  8. void display();
  9. };
  10. #endif //STRATEGY_PATTERN_MODELDUCK_H

MoedlDuck.cpp

  1. #include "ModelDuck.h"
  2. #include "QuackBehavior.h"
  3. #include "FlyBehavior.h"
  4. #include <iostream>
  5. #include <memory>
  6. ModelDuck::ModelDuck() {
  7. FlyNoWay *f_ptr = new FlyNoWay();
  8. QuackNoWay *q_ptr = new QuackNoWay();
  9. setquackBehavior(q_ptr);
  10. setFlyBehavior(f_ptr);
  11. }
  12. ModelDuck::~ModelDuck() {}
  13. void ModelDuck::display() {
  14. std::cout<<"I am a model. "<<std::endl;
  15. }

3.策略类

FlyBehavior.h

  1. #ifndef STRATEGY_PATTERN_FLYBEHAVIOR_H
  2. #define STRATEGY_PATTERN_FLYBEHAVIOR_H
  3. class FlyBehavior {
  4. public:
  5. virtual void fly()=0;
  6. };
  7. class FlyNoWay:public FlyBehavior{
  8. public:
  9. virtual void fly();
  10. };
  11. class FlyWithWings:public FlyBehavior{
  12. public:
  13. virtual void fly();
  14. };
  15. #endif //STRATEGY_PATTERN_FLYBEHAVIOR_H

FlyBehavior.cpp

  1. #include "FlyBehavior.h"
  2. #include <iostream>
  3. void FlyWithWings::fly() {
  4. std::cout<<"I'm flying with wings"<<std::endl;
  5. }
  6. void FlyNoWay::fly(){
  7. std::cout<<"I can't fly"<<std::endl;
  8. }

QuackBehavior.h

  1. #ifndef STRATEGY_PATTERN_QUACKBEHAVIOR_H
  2. #define STRATEGY_PATTERN_QUACKBEHAVIOR_H
  3. class QuackBehavior{
  4. public:
  5. virtual void quack()=0;
  6. };
  7. class QuackNoWay:public QuackBehavior {
  8. public:
  9. virtual void quack();
  10. };
  11. class QuackNormal:public QuackBehavior {
  12. public:
  13. virtual void quack();
  14. };
  15. #endif //STRATEGY_PATTERN_QUACKBEHAVIOR_H

QuackBehavior.cpp

  1. #include "QuackBehavior.h"
  2. #include <iostream>
  3. void QuackNoWay::quack() {
  4. std::cout<<"<<Silence>>"<<std::endl;
  5. }
  6. void QuackNormal::quack(){
  7. std::cout<<"quack!" <<std::endl;
  8. }

4.测试文件

main.cpp

  1. #include <iostream>
  2. #include "Duck.h"
  3. #include "ModelDuck.h"
  4. #include "MallardDuck.h"
  5. int main() {
  6. Duck* duck;
  7. duck = new MallardDuck();
  8. duck->display();
  9. duck->performQuack();
  10. duck->performFly();
  11. delete duck;
  12. duck = new ModelDuck();
  13. duck->display();
  14. duck->performQuack();
  15. duck->performFly();
  16. delete duck;
  17. return 0;
  18. }

5.构建文件 CMakeLists.txt

  1. cmake_minimum_required(VERSION 3.0)
  2. project(Strategy_Pattern)
  3. set(CMAKE_CXX_STANDARD 11)
  4. add_library(Duck Duck.cpp MallardDuck.cpp ModelDuck.cpp)
  5. add_library(Behavior FlyBehavior.cpp QuackBehavior.cpp)
  6. target_link_libraries(Duck Behavior)
  7. add_executable(Strategy_Pattern main.cpp)
  8. target_link_libraries(Strategy_Pattern Duck)

6.运行结果

  1. I am a MallardDuck.
  2. quack!
  3. I'm flying with wings
  4. I am a model.
  5. <<Silence>>
  6. I can't fly

设计思想

  1. 用组合代替继承,注意到 组合的含义是 Has-A 而继承的含义是 Is-A。使用组合的话,将算法类作为用户类的成员,所以在运行时可以通过更改成员进行算法的替换。
  2. 面向接口编程,而非面向实现编程。按照我的理解,为了系统的弹性和可维护性,将程序中容易变化的部分和不变的部分分离出来,接口Interface 定义了一种规范和约束,C++ 抽象基类(接口类)负责定义一组统一的不变的方法,子类必须实现这些方法,但是对于实现的细节没有约束。用鸭子的叫声这个行为为例子,定义了 QuackBehavior基类作为接口,要求子类必须实现void quack() 方法; 子类的实现可以是 呱呱叫, 咕咕叫,甚至是汪汪叫,也可以沉默(不叫)。

源码地址:https://github.com/sunchaothu/DesignPatternsCpp_Practice.git

参考

[1] Head First Design Patterns

设计模式C++实现(1)——策略(Strategy)模式的更多相关文章

  1. 设计模式C++描述----15.策略(Strategy)模式

    一. 举例说明 以前做了一个程序,程序的功能是评价几种加密算法时间,程序的使用操作不怎么变,变的是选用各种算法. 结构如下: Algorithm:抽象类,提供算法的公共接口. RSA_Algorith ...

  2. 《Head First 设计模式》ch.1 策略(Strategy)模式

    策略模式 定义了算法族,分别封装起来,让它们可以互相替换,让算法的变化独立于使用算法的客户. 模式名词的意义 威力强大,交流的不止是模式名称,而是一整套模式背后所象征的质量.特性.约束 用更少的词汇做 ...

  3. 策略(strategy)模式

    Head First一书中对于策略(strategy)模式的正式定义是:策略模式定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户. 为了介绍这个算法,书中讲了 ...

  4. Java 实现策略(Strategy)模式

    策略模式:行为型模式 将同一行为,不同的处理算法分别封装起来.让它们之间能够互相替换 1. 定义一个超类型接口,及 行为方法 2. 定义不同的实现类,实现该行为的 不同的算法 /** * 策略模式:针 ...

  5. 《图解设计模式》读书笔记4-2 STRATEGY模式

    目录 示例程序 角色 想法 Strategy模式即策略模式,在编程中,策略指的就是算法.利用此模式可以整体替换算法,即使用不同方式解决同一个问题.比如设计一个围棋程序,通过切换算法可以方便地切换AI的 ...

  6. C++设计模式实现--策略(Strategy)模式

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/L_Andy/article/details/30489331 一. 举例说明 曾经做了一个程序,程序 ...

  7. Head First 设计模式 - 01. 策略 (Strategy) 模式

    当涉及到"维护"时,为了"复用"目的而使用继承,结局并不完美 P4 对父类代码进行修改时,影响层面可能会很大 思考题 利用继承来提供 Duck 的行为,这会导致 ...

  8. Java设计模式透析之 —— 策略(Strategy)

    今天你的leader兴致冲冲地找到你,希望你能够帮他一个小忙.他如今急着要去开会.要帮什么忙呢?你非常好奇. 他对你说.当前你们项目的数据库中有一张用户信息表.里面存放了非常用户的数据.如今须要完毕一 ...

  9. Java设计模式(18)策略模式(Strategy模式)

    Strategy是属于设计模式中 对象行为型模式,主要是定义一系列的算法,把这些算法一个个封装成单独的类. Stratrgy应用比较广泛,比如,公司经营业务变化图,可能有两种实现方式,一个是线条曲线, ...

随机推荐

  1. translate动画实例

    <!doctype html> <html lang="en"> <head> <meta name="viewport&quo ...

  2. DPDK安装依赖项合集 环境合集

    前言 在dpdk编译过程中,由于一些依赖项的限制,dpdk在纯净的系统上安装需要花一些功夫.本文总结了编译dpdk所需的依赖项,并归纳了安装合集,在安装过程上可以省下大量的搜索时间. 使用系统 ubu ...

  3. nRF5 SDK for Mesh( 七 ) BLE MESH 的 架构(rchitecture)

    The mesh architecture   The mesh stack consists of a number of subsystems that are interfaced throug ...

  4. MongoDB简易

    一   安装 1.下载 $ brew install mongodb 2.启动 $ mongod --config /usr/local/etc/mongod.conf 3.连接 $ mongo 二 ...

  5. oracle定时器在项目中的应用

    业务需求: 现在业务人员提出了一个需求: 在项目中的工作流,都要有一个流程编号,此编号有一定的规则: 前四五位是流程的字母缩写,中间是8位的日期,后面五位是流水码,要求流水码每天从00001开始.即: ...

  6. generate failed: Cannot resolve classpath entry: mysql-connector-java-5.1.38.jar

    详细错误及处理方法如下: [ERROR] Failed to execute goal org.mybatis.generator:mybatis-generator-maven-plugin:1.3 ...

  7. 为什么浏览器控制台返回不是undefined,而是一串数字

    setTimeout( (function(){console.log("ok")} )(), 16) 在浏览器控制台输入这段代码的时候返回的不是"undefined&q ...

  8. 请给出如下格式的date命令 例:11-02-26.再给出实现按周输出 比如:周六输出为6,请分别给出命令。

    请给出如下格式的date命令 例:19-01-18.再给出实现按周输出 比如:周六输出为6,请分别给出命令. 解答: 方法1: [root@zhaokang ~]# date2019年 01月 17日 ...

  9. Linux --- Ubuntu16.04.5 LTS 虚拟机安装后的软件安装基础操作总结

    1. 配置安装源 因为默认是使用Ubuntu官方服务器,国内电脑使用外国服务器较慢,所以需使用国内的服务器(以下清华大学服务器为例). 方法一: (此过程很慢,实在不动就取消吧,加载一部分也够用,以后 ...

  10. 升级Xcode10报错问题修复

    Xcode10 问题1 报文件重复 File--> Workspace Settings --> Build System 修改为Legacy Build System (默认是New B ...