设计模式之策略模式详解

概述

先看下面的图片,我们去旅游选择出行模式有很多种,可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。

作为一个程序猿,开发需要选择一款开发工具,当然可以进行代码开发的工具有很多,可以选择Idea进行开发,也可以使用eclipse进行开发,也可以使用其他的一些开发工具。


在软件开发中,我们也常常会遇到类似的情况,实现某一个功能有多条途径,每一条途径对应一种算法,此时我们可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。

策略模式定义:

​ 该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户(算法的具体实现不同?)。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开,将算法的定义放在专门的策略类中,每一个策略类封装了一种实现算法,使用算法的环境类针对抽象策略类进行编程,符合“依赖倒转原则”。在出现新的算法时,只需要增加一个新的实现了抽象策略类的具体策略类即可。

结构

在策略模式中,我们可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里,每一个封装算法的类我们都可以称之为一种策略(Strategy),为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做规则的定义,而每种算法则对应于一个具体策略类。

策略模式的主要角色如下:

  • 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口,所有具体的策略类都要实现这个接口。环境(上下文)类Context 使用这个接口调用具体的策略类。
  • 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
  • 环境(Context)类:用于配置一个具体的算法策略对象 ,维持一个策略接口类型的引用( Reference ),并且可以定义一个让接口 Strategy 的具体对象访问的接口。在简单情况下,Context 类可以省略。

策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列具体策略类里面,作为抽象策略类的子类。在策略模式中,对环境类和抽象策略类的理解非常重要,环境类是需要使用算法的类。在一个系统中可以存在多个环境类,它们可能需要重用一些相同的算法。

在客户端代码中只需注入一个具体策略对象,可以将具体策略类类名存储在配置文件中,通过反射来动态创建具体策略对象,从而使得用户可以灵活地更换具体策略类,增加新的具体策略类也很方便。策略模式提供了一种可插入式(Pluggable)算法的实现方案。

UML类图的设计

不同的具体策略类实现了同一个抽象的策略算法接口,环境类通过聚合抽象类接口,通过多态的方式依赖具体的策略类;
用户直接依赖环境类,当具体的策略类发生变化或者有新的策略算法,用户端不需要进行修改;

案例实现

【例】促销活动

引出策略模式

一家百货公司在定年度的促销活动。针对不同的节日(春节、中秋节、圣诞节)推出不同的促销活动,由促销员将促销活动展示给客户。
该促销活动包括ABC三种不同的推销方式,最初的设计是将不同的推销策略以及用户的main方法全都封装在同一个类中;其优点是将整个程序设计为一个单独的类,比较容易写代码;缺点是没有做到责任分离,在可扩展性以及可维护性方面也存在问题。
所以最初的改进方案是将以这个单个类拆分为两个类,一个是客户端使用的main方法,一个是封装了三种推销策略的类;
很明显,这个设计将带有主方法的客户类 与具体的策略类 剥离,因此做到了责任分离。并并且因为该设计中所有的策略算法都被封装在一个类中,而这些方法都是为了完成相同的任务,即对针对节日进行产品促销, 所以该设计具有高内聚的性质。
表面上看来本设计应该说是令人满意的 其优点是当需要修改策略类中的某个方法时,不必对客户类 做任何修改。虽然不满足开闭原则,但是从可维护性上说,新的设计比原来的设计要好得多。
而且该设计仍然存在这样的问题:当一个新促销策略被添加到策略类中或当一个
算法被修改以后,整个策略类都需要重新编译,也就是说扩展性跟开闭原则都没有得到很好的满足;

策略模式改进

所以为了解决上述问题,需要进一步改进设计。进一步拆分策略类 ,将每个促销算法都单独封装在一个类中,也就是将一个类拆分成几个类,每个类都单独封装一个促销策略算法。
这样一来,修改一个算法只需重新编译算法所涉及的那个类,而不需要重新编译其他类。如
果想要添加一个新的算法 只需在子类的集合中再添加一个新的封装该算法的类即可。
类图如下:

代码如下:

定义百货公司所有促销活动的共同接口

//抽象策略类
public interface Strategy {void show();
}

定义具体策略角色(Concrete Strategy):每个节日具体的促销活动

//为春节准备的促销活动A
public class StrategyA implements Strategy {public void show() {System.out.println("买一送一");}
}//为中秋准备的促销活动B
public class StrategyB implements Strategy {public void show() {System.out.println("满200元减50元");}
}//为圣诞准备的促销活动C
public class StrategyC implements Strategy {public void show() {System.out.println("满1000元加一元换购任意200元以下商品");}
}

定义环境角色(Context):用于连接上下文,即把促销活动推销给客户,这里可以理解为销售员

public class SalesMan {                        //持有抽象策略角色的引用                              private Strategy strategy;                 public SalesMan(Strategy strategy) {       this.strategy = strategy;              }                                          //向客户展示促销活动                                public void salesManShow(){                strategy.show();                       }
}                                              

优缺点

1,优点:

  • 策略类之间可以自由切换

    由于策略类都实现同一个接口,所以使它们之间可以自由切换。

  • 易于扩展

    增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则“

  • 避免使用多重条件选择语句(if else),充分体现面向对象设计思想。

2,缺点:

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。也就是说客户程序必须知道不同策略接口的各个子类的行为,必须理解每个子类有哪些不同。
  • 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。
  • 无法同时在客户端使用多个策略类,也就是说,在使用策略模式时,客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。

使用场景

  • 一个系统需要动态地在几种算法中选择一种时,当有多个仅在行为上不同但是任务相关的类存在时,可将每个算法封装到策略类中。
  • 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
  • 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
  • 当一个算法使用的用户不应该知道的数据时 使用策略模式可以将算法实现细节隐藏起来 ,避免暴露与算法相关的复杂细节。注意 虽然可以将算法实现细节封装起来,但是客户程序必须知道各个策略子类的接口。

关于策略模式的讨论

使用策略模式主要有两个出发点:
(1 ) 将一组相关的算法封装为各个策略分支,从而将策略分支相关的代码隐藏起来。
(2) 希望可以提升程序的可扩展性。

下面我们就策略模式的可扩展性进行简单的讨论,实际上 策略模式的初衷是要减少与各个分支下的行为相关的条件语句。这已经通过将一个具有条件相关的多种行为的类拆分成一个策略超类与若干个策略子类得到了解决。也就是说,将原来的一个单独的但是包含多个条件语句的类改变为一个没有条件语句的策略层次类。
这里虽然看似条件语句消失了,但是在客户程序与 Context 类中是否也不存在与策略子类相关的条件语句了呢?答案当然不是。

实际上一般在策略模式的设计中 客户类根据不同的条件负责创建不同的策略子类的对象,然后再将该对象传递给 Context 环境类,Context 类的作用可以理解为:为被调用策略子类的一些方法提供一些参数,以及使用该由 Client 类传入的对象去调用 Strategy 类的某些方法。
这说明 在客户类 Client 中 存在许多与策略分支子类相关的条件语句,而在 Context 类中,没有这样的语句。
那么 是否可以将创建策略子类的对象的责任交给 Context 类,而客户类 Client 只为 Context 类提供一些代表客户请求的参数呢?

(1 ) 客户类负责创建策略子类的对象的情况
**客户类根据用户提供的不同的请求,负责创建不同的策略子类的对象 ,然后再将该对象传递
给 Context 类。**在这种情况下,客户类中通常包含与策略相关的条件语句,而在 Context 类中不必
使用任何与策略有关的条件语句,因此,修改或者添加一个策略子类都不必修改 Context 类。但
是,在添加一个新的策略子类的情况下,如果客户类需要使用该子类,往往需要在客户类中添
加一个新的条件语句,即客户类需要修改。

(2) Context 类负责创建策略子类的对象的情况

将创建策略子类的对象的责任交给 Context 类, 而客户类 Client 只为 Context 类提供一些代
表客户请求的参数 ;在此情况下,Context 类在创建策略子类的对象时,必然会使用与策略子
类有关的条件语句。此时,修改一个策略子类不需要修改客户类与 Context 类。而在添加一个
新的策略子类时,如果此时客户类暂时不使用该新的子类,则新子类的添加不会影响客户类
与 Context 类的源代码。但是,如果客户类要使用新的策略子类,则必须同时在客户类与 Con- text 类中添加新的条件分支,也就是说,需要同时修改客户类与 Context 类

在以上两种情况下,当只是需要修改策略子类的代码时,客户类与 Context 类都不需要进行修改。
综上所述 由客户类创建对象的设计可扩展性好一些。这样,可以做到在 Context 类中出现与策略子类相关的条件语句,从而可扩展性也得到了提高。

设计模式之策略模式详解相关推荐

  1. java策略模式_Java设计模式之策略模式详解

    本文实例为大家分享了Java策略模式,供大家参考,具体内容如下 1.策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(PolicyPattern). 定义如下: Def ...

  2. 从java多态到策略模式_设计模式中的多态——策略模式详解

    2. 策略模式详解 2.1 策略模式定义 策略模式定义了一系列算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户端而独立的变化. 可以使用多态进行类比来理解策略模 ...

  3. 设计模式之模板方法模式详解

    设计模式之模板方法模式详解 概述 在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的 ...

  4. 设计模式之门面模式详解

    设计模式之门面模式详解 文章目录 设计模式之门面模式详解 一.什么是门面模式 二.门面模式的应用场景 三.门面模式的角色组成 四.门面模式通用写法 五.门面模式在业务中的应用 六.门面模式优缺点 一. ...

  5. 设计模式——模版方法模式详解(论沉迷LOL对学生的危害)

    0. 前言 写在最前面,本人的设计模式类博文,建议先看博文前半部分的理论介绍,再看后半部分的实例分析,最后再返回来复习一遍理论介绍,这时候你就会发现我在重点处标红的用心,对于帮助你理解设计模式有奇效哦 ...

  6. 设计模式之桥接模式详解

    设计模式之桥接模式详解 文章目录 设计模式之桥接模式详解 一.什么是桥接模式 二.桥接模式的应用场景 三.桥接模式的角色组成 四.桥接模式通用写法示例 五.桥接模式优缺点 一.什么是桥接模式 桥接模式 ...

  7. java策略模式详解_Java经典设计模式之策略模式原理与用法详解

    本文实例讲述了Java经典设计模式之策略模式.分享给大家供大家参考,具体如下: 策略模式指:策略模式指将程序中可变部分抽象分离成一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式 ...

  8. 设计模式之工厂模式详解(附应用举例实现)

    文章目录 1 工厂模式介绍 2 工厂模式详解 2.1 简单工厂模式 2.1.1 简单工厂模式结构 2.1.2 简单工厂模式实现 2.1.3 简单工厂模式应用举例 2.2 工厂方法模式 2.2.1 工厂 ...

  9. 设计模式之桥接模式详解(附应用举例实现)

    文章目录 1 桥接模式介绍 2 桥接模式详解 2.1 桥接模式结构 2.2 桥接模式实现 2.3 桥接模式应用实例 1 桥接模式介绍 毛笔和蜡笔是两种很常见的文具,它们都归属于画笔.假设我们需要大.中 ...

最新文章

  1. 自定义堆栈(回文检测)
  2. Hello程序的转换处理过程及程序执行时的数据流动过程
  3. C++求数组子数组和的最大值并将该子数组和最大值打印出来
  4. 2074. 反转偶数长度组的节点
  5. python怎么做项目_听说你没有python项目可做,我教你个方法
  6. 吓坏了!智能锁半夜自己“离奇打开”
  7. c++------------之---【虚析构函数应用】
  8. Linux 12.10 ISO下载地址
  9. centos6.5 install cobbler
  10. C语言创建顺序表并插入元素 详细注释
  11. Winfrom 定时锁屏
  12. ant design pro中权限组件Authorized的个人学习
  13. MSE、RMSE、MAE、R方等指标整理
  14. Java java.sql.SQLSyntaxErrorException:Duplicate column name ‘xxx‘问题解决
  15. Dubbo3.0 整合 Nacos
  16. 给VB.NET开发者的46个忠告
  17. 飞跃计划 | 袋鼠云与睿呈时代达成战略合作,携手助推传统行业数字化转型
  18. python 3.7安装festtext、提示要更新_现代开发者必备:5个更流畅、更受欢迎的Python web框架...
  19. STM32F429第九篇之stm32f4xx_hal_rcc_ex
  20. 《爬虫爬 wallhaven.cc壁纸》

热门文章

  1. Windows Server 2012打开端口
  2. MTK 平台手势唤醒功能
  3. android手机进入fastboot,安卓手机怎么进去fastboot模式?安卓手机进入fastboot模式的方法_系统圣地...
  4. ZDT_X57步进闭环驱动说明书Rev1.3
  5. 你会为 AI 转型么?我在考虑。。。
  6. Android支付宝蚂蚁森林能量自动收取插件开发原理解析
  7. 周鸿祎称微软抄袭 360 安全模式后发文否认;​英特尔CEO基辛格回应市值被AMD超越:股价下跌是咎由自取|极客头条...
  8. 微信公众号文章怎么添加文件下载功能?如Word,Excel,PPT,PDF等
  9. 你生日那天的宇宙什么样子知道?我全部给你吧!
  10. opencv+c++实现的瘦脸算法