一:概念

  1. Strategy模式,对一系列的算法加以封装,为所有算法定义一个抽象的算法接口,并通过继承该抽象算法接口对所有的算法加以封装和实现,具体的算法选择交由客户端决定(策略)。
  1. Strategy模式主要用于平滑的处理算法的切换

二:动机

  1. 在软件构建过程中,某些对象可能用到的算法多种多样,经常改变,如果将这些算法都编码到对象中,将会使得对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
  1. 如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?

三:代码解析(税种计算)

(一)结构化代码

1.原代码

  1. enum TaxBase {
  2. CN_Tax,
  3. US_Tax,
  4. DE_Tax,
  5. };
  6.  
  7. class SalesOrder{
  8. TaxBase tax;
  9. public:
  10. double CalculateTax(){
  11. //...
  12.  
  13. if (tax == CN_Tax){  //或者switch开关语句
  14. //CN***********
  15. }
  16. else if (tax == US_Tax){
  17. //US***********
  18. }
  19. else if (tax == DE_Tax){
  20. //DE***********
  21. }//....
  22. }
  23.  
  24. };
  1. 不要静态的去看一个软件结构的设计,而是要动态的去看,这是设计模式的一个重要观点。

2.需求变化,需要支持法国税法

  1. enum TaxBase {
  2. CN_Tax,
  3. US_Tax,
  4. DE_Tax,
  5. FR_Tax //更改
  6. };
  7.  
  8. class SalesOrder{
  9. TaxBase tax;
  10. public:
  11. double CalculateTax(){
  12. //...
  13.  
  14. if (tax == CN_Tax){
  15. //CN***********
  16. }
  17. else if (tax == US_Tax){
  18. //US***********
  19. }
  20. else if (tax == DE_Tax){
  21. //DE***********
  22. }
  23. else if (tax == FR_Tax){ //更改
  24. //...
  25. }
  26. //....
  27. }
  28.  
  29. };

我们可以发现上面的修改违反了一个原则:开放封闭原则(重点)

  1. 对扩展开发,对更改封闭。类模块尽可能用扩展的方式来支持未来的变化,而不是找到源代码,用修改源代码的方式来面对未来的变化
  1. 上面代码两处红色部分就违反了开放封闭原则,带来了一个很大的复用性负担,软件要重新更改,重新编译,重新测试,重新部署,需要的代价十分大。
  1. 所以尽可能使用扩展方式来解决问题,如何使用扩展方式来解决,就要用到Strategy模式

(二)面向对象Strategy模式代码

1.原代码

  1. class TaxStrategy{  //算法测试基类
  2. public:
  3. virtual double Calculate(const Context& context)=;  //纯虚方法
  4. virtual ~TaxStrategy(){}  //虚析构函数
  5. };
  6.  
  7. class CNTax : public TaxStrategy{
  8. public:
  9. virtual double Calculate(const Context& context){
  10. //***********
  11. }
  12. };
  13.  
  14. class USTax : public TaxStrategy{
  15. public:
  16. virtual double Calculate(const Context& context){
  17. //***********
  18. }
  19. };
  20.  
  21. class DETax : public TaxStrategy{
  22. public:
  23. virtual double Calculate(const Context& context){
  24. //***********
  25. }
  26. };
  27. class SalesOrder{
  28. private:
  29. TaxStrategy* strategy;  //多态性
  30. public:
  31. SalesOrder(StrategyFactory* strategyFactory){
  32. this->strategy = strategyFactory->NewStrategy();  //工厂模式来创建指针,堆对象,
  33. }
  34. ~SalesOrder(){
  35. delete this->strategy;
  36. }
  37.  
  38. public double CalculateTax(){
  39. //...
  40. Context context();  //上下文参数
  41.  
  42. double val =
  43. strategy->Calculate(context); //多态调用,具体依赖于工厂创建的对象
  44. //...
  45. }
  46.  
  47. };
  1. 我们将原来结构化设计中的一个个算法,变为TaxStrategy类的一个子类
  1. 相对于上面结构化思想相比较:功能是一样的。但是要比较好处,在设计领域要比较好处需要放在时间轴中去看

2.修改代码,现在需要支持法国税务算法,进行扩展(新的文件)

  1. //扩展
  2. //*********************************
  3. class FRTax : public TaxStrategy{
  4. public:
  5. virtual double Calculate(const Context& context){
  6. //.........
  7. }
  8. };
  1. 其他地方不需要修改,当然工厂中需要去修改使得能够产生法国税务对象
  1. 多态调用会自然找到我们扩展的税务算法运算
  1. 在整体代码中,尤其是SalesOrder主调用类中,代码不需要改变,得到了复用性
  1. 注意:我们在新的文件中写入我们扩展的算法,单独编译,例如dll,动态加入程序中,遵循了开闭原则
  1. 而第一种方法中在方法内部去修改添加源代码,打破了开放封闭原则,违背了复用性

复用性:

  1. 注意:我们在面向对象中(尤其是设计模式领域中)谈到的复用性是指编译单位,二进制层次上的复用性
  1. 例如我们第二种方法,在编译部署后,不需要修改原来的文件,我们只需要将新添加的扩展单独编译,动态被调用即可。原来二进制文件的复用性很好。
    但是对于第一种方法,我们对原方法进行了修改,我们的整个程序都要重新编译,部署,原来的二进制文件不再使用,而是需要我们新的编译后的二进制文件,所以复用性极差

例如下面的修改:

  1. if (tax == CN_Tax){
  2. //CN***********
  3. }
  4. else if (tax == US_Tax){
  5. //US***********
  6. }
  7. else if (tax == DE_Tax){
  8. //DE***********
  9. }
  10. else if (tax == FR_Tax){ //更改
  11. //...
  12. }
  1. 而且在下面补充一段代码是很有问题的,虽然我们保证上面代码不变,但是在实际开发中,向后面添加代码,往往会打破这个代码前面的一些代码,会向前面引入一些bug。而且我们对这种不叫做复用,真正的复用是编译层面的

四:模式定义

  1. 定义一系列算法,把他们一个个封装起来,并且使他们可以互相替换(变化<各个算法>)。该模式使得算法可独立于使用它的客户程序(稳定<SalesOrder类>)而变化(扩展,子类化

五:类图(结构)

  1. Strategy基类中一般放置的方法不多
  1. 除了极个别,像单例。其他的都可以像上面一样去找出它的稳定部分和变化部分

六:要点总结(优点)

(一)Strategy及其子类为组件提供了一系列的可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。

  1. class SalesOrder{
  2. private:
  3. TaxStrategy* strategy;
  4.  
  5. public:
  6. SalesOrder(StrategyFactory* strategyFactory){
  7. this->strategy = strategyFactory->NewStrategy();  //在运行时传入一个多态对象
  8. }
  9. ~SalesOrder(){
  10. delete this->strategy;
  11. }
  12.  
  13. public double CalculateTax(){
  14. //...
  15. Context context();
  16.  
  17. double val =
  18. strategy->Calculate(context); //在运行时就支持多态调用,灵活变化
  19. //...
  20. }
  21.  
  22. };

(二)Strategy模式提供了用条件判断语句以外的另一种选择,消除条判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。

  1. 一般来说代码出现if..else...或者switch...case...,这就是我们需要Strategy模式的特征。(当然我们说的是变化的,若是像男女性别判断,就是绝对不变的,就不需要使用到Strategy模式,但是更多场景都是可变的)
  2. 因为if..else..是结构化思维中的分而治之。而且需要一直判断,支持不使用的算法也是一个性能负担。而且由很大一段代码都被装载在代码段中,这是不需要的。
  3. 我们使用Strategy模式,是在运行时加载,运行中需要哪个就可以即时调用。具有稳定性。
  4. 在运行时代码在代码段中,放在内存中,最好的是加载在高级缓存中,最快,若是代码段过长,就只能放在主存,甚至虚拟内存中(硬盘),所以我们使用if...else..时,会有很多代码被加载到高级缓存,内存中,这会占用空间,其他代码就会被挤入其他地方,执行不会太快,Strategy模式会顺便解决部分这个问题

(三)如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销

七:缺点

(一)客户端必须知道所有的策略类,并自行决定使用哪一个类。解决:我们可以使用工厂模式

(二)策略模式造成了很多的策略类。解决:通过把依赖于环境的状态保存到客户端,而将策略类设计成共享的,这样策略类实例可以被不同客户端使用(单例模式)

八:案例实现

  1. //策略基类
  2. class Strategy
  3. {
  4. public:
  5. virtual void SymEncrypt() = ;
  6. virtual ~Strategy(){};
  7. };
  1. //各个算法
  2. class Des:public Strategy
  3. {
  4. public:
  5. virtual void SymEncrypt()
  6. {
  7. cout << "Des Encrypt" << endl;
  8. }
  9. };
  10.  
  11. class AES :public Strategy
  12. {
  13. public:
  14. virtual void SymEncrypt()
  15. {
  16. cout << "AES Encrypt" << endl;
  17. }
  18. };
  1. //上下文管理器
  2. class Context
  3. {
  4. private:
  5. Strategy* s;
  6. public:
  7. Context(Strategy* strategy)
  8. {
  9. s = strategy; //这里应该由工厂实现
  10. }
  11.  
  12. void Operator()
  13. {
  14. s->SymEncrypt();
  15. }
  16. };
  1. void main()
  2. {
  3. Strategy* strategy = NULL;
  4. Context* ctx = NULL;
  5.  
  6. strategy = new AES;
  7. ctx = new Context(strategy);
  8. ctx->Operator();
  9.  
  10. delete strategy;
  11. delete ctx;
  12.  
  13. system("pause");
  14. return;
  15. }

设计模式---组件协作模式之策略模式(Strategy)的更多相关文章

  1. 23种设计模式 - 组件协作(TemplateMethod - Observer/Event - Strategy)

    其他设计模式 23种设计模式(C++) 每一种都有对应理解的相关代码示例 → Git原码 ⌨ 组件协作 现代软件专业分工之后的第一个结果是"框架与应用程序的划分","组件 ...

  2. 行为型设计模式之模板方法(TEMPLATE METHOD)模式 ,策略(Strategy )模式

    1 模板方法(TEMPLATE METHOD)模式: 模板方法模式把我们不知道具体实现的步聚封装成抽象方法,提供一些按正确顺序调用它们的具体方法(这些具体方法统称为模板方法),这样构成一个抽象基类.子 ...

  3. C#设计模式学习笔记:(19)策略模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8057654.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第七个模式--策 ...

  4. Java设计模式(十二) 策略模式

    原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...

  5. 设计模式之第8章-策略模式(Java实现)

    设计模式之第8章-策略模式(Java实现) “年前大酬宾了啊,现在理发冲500送300,冲1000送500了.鱼哥赶紧充钱啊,理发这事基本一个月一回,挺实惠的啊.不过话说那个理发店的老板好傻啊,冲10 ...

  6. 【java设计模式】(7)---策略模式(案例解析)

    策略模式 一.概念 1.理解策略模式 策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口 和 具体行为的实现.策略模式最大的特点是行为的变化,行为之间可以相互替换. 每个if判断 ...

  7. 大型Java进阶专题(七) 设计模式之委派模式与策略模式

    前言 ​ 今天开始我们专题的第七课了.本章节将介绍:你写的代码中是否觉得很臃肿,程序中有大量的if...else,想优化代码,精简程序逻辑,提升代码的可读性,这章节将介绍如何通过委派模式.策略模式让你 ...

  8. Android设计模式之命令模式、策略模式、模板方法模式

    命令模式是其它很多行为型模式的基础模式.策略模式是命令模式的一个特例,而策略模式又和模板方法模式都是算法替换的实现,只不过替换的方式不同.下面来谈谈这三个模式. 命令模式 将一个请求封装为一个对象,从 ...

  9. Python 中的设计模式详解之:策略模式

    虽然设计模式与语言无关,但这并不意味着每一个模式都能在每一门语言中使用.<设计模式:可复用面向对象软件的基础>一书中有 23 个模式,其中有 16 个在动态语言中“不见了,或者简化了”. ...

  10. 我学的是设计模式的视频教程——命令模式vs策略模式,唠嗑

    课程视频 命令模式vs策略模式 唠嗑 课程笔记 课程笔记 课程代码 课程代码 新课程火热报名中 课程介绍 版权声明:本文博主原创文章,博客,未经同意不得转载.

随机推荐

  1. HDU 2012 素数判定

    http://acm.hdu.edu.cn/showproblem.php?pid=2012 Problem Description 对于表达式n^2+n+41,当n在(x,y)范围内取整数值时(包括 ...

  2. CAS的应用场景

    国外应用(需FQ尝试): 来自CAS官网推荐的Demo http://casserver.herokuapp.com/cas/login https://casserver.herokuapp.com ...

  3. git忽略本地文件

    一.忽略文件的常用方法 1.git通常在.gitignore文件进行配置,来忽略本地文件.但是这仅对于重来没有提交过的文件有效. 2.使用git update-index --skip-worktre ...

  4. Android控件第5类——ViewAnimator

    1.ViewAnimator,继承自FrameLayout ViewAnimator是一个基类,它继承自FrameLayout.它的子类有ViewSwitcher和ViewFlipper:ViewSw ...

  5. Angular 添加路由

    var app=angular.module('kaifanla',['ng','ngRoute']);app.config(function($routeProvider){ //添加路由 $rou ...

  6. 如何为TreeView定义三层模板并实现数据绑定

    一直以来都想对TreeView定义多层模板,并实现数据绑定做一个总结,今天在这里做一个概述,我们常用的两层的TreeView绑定的话,我们首先修改TreeView的模板,这里我们使用的是级联的数据模板 ...

  7. mysql登录密码相关

    设置root登录密码 方法一:用root 进入mysql后 mysql>set password =password('你的密码'); mysql>flush privileges; 方法 ...

  8. protocol buffer开发指南(官方)

    欢迎来到protocol buffer的开发者指南文档,一种语言无关.平台无关.扩展性好的用于通信协议.数据存储的结构化数据序列化方法. 本文档是面向计划将protocol buffer使用的到自己的 ...

  9. fix

    rounds the elements of A toward zero, resulting in an array of integers. For complex A, the imaginar ...

  10. hdu 5919 Sequence II (可持久化线段树)

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=5919 大致题意: 给你一个长度为n的序列,q个询问,每次询问是给你两个数x,y,经过与上一次的答案进行运算 ...