设计模式-策略(Strategy)模式

  • 又被称为政策(方针)模式
  • 策略模式(Strategy Design Pattern):封装可以互换的行为,并使用委托来决定要使用哪一个
  • 策略模式是一种行为设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换
  • 用人话翻译后就是:运行时我给你这个类的方法传不同的 “key”,你这个方法就去执行不同的业务逻辑
  • 解释:
    • 现实世界例子:屠龙是一项危险的职业;有经验将会使它变得简单;经验丰富的屠龙者对不同类型的龙有不同的战斗策略
    • 直白点说,策略模式允许在运行时选择最匹配的算法
    • 在程序编程领域,策略模式(又叫政策模式)是一种启用在运行时选择算法的行为型软件设计模式
  • 仔细一想,这不就是 if else 干的事吗
  • 先直观的看下传统的多重 if else 代码:

  • 再来看策略模式类图:

  • 策略模式涉及到三个角色:
    • Strategy:策略接口或者策略抽象类,用来约束一系列的策略算法(Context 使用这个接口来调用具体的策略实现算法)
    • ConcreateStrategy:具体的策略类(实现策略接口或继承抽象策略类)
    • Context:上下文类,持有具体策略类的实例,并负责调用相关的算法
  • 先来看看最简单的策略模式 demo:
  • 1-策略接口(定义策略)

  • 2-具体的算法实现

  • 3-上下文的实现

  • 4-客户端使用(策略的使用)

  • 这种策略的使用方式其实很死板,真正使用的时候如果还这么写,和写一大推 if-else 没什么区别,所以我们一般会结合工厂类,在运行时动态确定使用哪种策略
  • 策略模式侧重如何选择策略、工厂模式侧重如何创建策略
  • 策略模式的功能就是把具体的算法实现从具体的业务处理中独立出来,把它们实现成单独的算法类,从而形成一系列算法,并让这些算法可以互相替换
  • 策略模式的重心不是如何来实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性
  • 实际上,每个策略算法具体实现的功能,就是原来在 if-else 结构中的具体实现,每个 if-else 语句都是一个平等的功能结构,可以说是兄弟关系
  • 策略模式呢,就是把各个平等的具体实现封装到单独的策略实现类了,然后通过上下文与具体的策略类进行交互
  • 策略模式 = 实现策略接口(或抽象类)的每个策略类 + 上下文的逻辑分派

  • 策略模式的本质:分离算法,选择实现
  • 所以说,策略模式只是在代码结构上的一个调整,即使用了策略模式,该写的逻辑一个也少不了,到逻辑分派的时候,只是变相的 if-else
  • 而它的优化点是抽象了出了接口,将业务逻辑封装成一个一个的实现类,任意地替换
  • 在复杂场景(业务逻辑较多)时比直接 if-else 更好维护和扩展些
  • 在策略模式中,我们可以自己定义谁来选择具体的策略算法,有两种:
    • 客户端:当使用上下文时,由客户端选择,像我们上边的 demo
    • 上下文:客户端不用选,由上下文来选具体的策略算法,可以在构造器中指定
  • 避免冗长的if/else:
    • 比如在出门旅游时:路线、交通工具的类型、天数、舱位等级、餐饮、住宿等等
    • 每个节点在执行时,都需要根据预算进行不同的操作,从而引起大量的判断
    • 增加一个策略,修改一个策略,都有可能牵一发而动全身
    • 需要对所有状态进行回测

    • 整个业务如图所示,所有的判断都耦合在业务流程内部,牵一发而动全身

    • 使用策略模式
    • 我们可以将某一条件(Type)下的逻辑,聚合封装到具体的策略类中

    • 使用策略类后如图所示,每个的情况被封装聚合到单个策略类中,相互隔离

    • 所以策略模式的作用主要体现在:
      • 1-解耦策略的定义、创建和使用
      • 控制代码的复杂度,让每个部分都不至于过于复杂、代码量过多
      • 2-让复杂框架满足开闭原则
      • 添加或者修改新策略的时候,最小化、集中化代码改动,减少引入 bug 的风险
    • 策略的创建
    • 通常会通过类型(type)来判断创建哪个策略来使用
    • 这里,有两种创建方式
      • if-else创建
      • 适用于有状态的策略类,每次创建一个新的策略类给业务方使用

      • 通过工厂模式里的Map进行创建
      • 适用于无状态的策略类创建,大家共用一个策略类即可

      • 本质上讲,是借助“查表法”,根据 type 查表替代根据 type 分支判断
  • Java中的例子:
    • 采用 Comparator 参数的 Collections.sort() 方法;根据 Comparator 接口的不同实现,对象会以不同的方式进行排序
  • 使用策略模式应当:
    • 许多相关的类只是行为不同;策略模式提供了一种为一种类配置多种行为的能力
    • 你需要一种算法的不同变体;比如,你可能定义反应不用时间空间权衡的算法;当这些算法的变体使用类的层次结构来实现时就可以使用策略模式
    • 一个算法使用的数据客户不应该对其知晓;使用策略模式来避免暴露复杂的,特定于算法的数据结构
    • 一个类定义了许多行为,这些行为在其操作中展现为多个条件语句;移动相关的条件分支到它们分别的策略类中来代替这些条件语句

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/248876.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【从零开始学习--设计模式--装饰者模式】

返回首页 前言 感谢各位同学的关注与支持,我会一直更新此专题,竭尽所能整理出更为详细的内容分享给大家,但碍于时间及精力有限,代码分享较少,后续会把所有代码示例整理到github,敬请期待。 此章节介绍装…

【Jmeter】Jmeter基础5-Jmeter元件介绍之线程(用户)

2.5.1、线程组 一个线程组即一个虚拟用户组,线程组中的每个线程即为1个虚拟用户,每个线程互相隔离,互不影响参数说明: 在取样器错误后要执行的动作 继续:忽略错误,继续执行启动下一进程循环: 终…

12G全国30米高程DEM原始数据

但可能大部分朋友更关注国内范围的30米高程DEM原始数据有多大,以及数据的具体覆盖情况。 我们在这里,再为大家分享全国30米高程DEM原始数据的基本情况。 全国30米高程DEM原始数据 全国30米高程DEM原始数据共分1159个文件块,每个文件块在经…

Go delve调试工具的简单应用

Delve是个啥 Delve is a debugger for the Go programming language. The goal of the project is to provide a simple, full featured debugging tool for Go. Delve should be easy to invoke and easy to use. Chances are if you’re using a debugger, things aren’t go…

机器学习练习题

例1: 解: 最大似然估计: P (男) 8 / 20 0.4 , P (女) 12 / 20 0.6 P(男) 8/200.4,P(女) 12/20 0.6 P(男&#xff0…

three.js模拟太阳系

地球的旋转轨迹目前设置为了圆形&#xff0c;效果&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"></div><div c…

服务器被攻击宕机的一些小建议

现在网络攻击屡有发生&#xff0c;任何网站服务器都面临这样的危险&#xff0c;服务器被攻击造成的崩溃宕机是损失是我们无法估量的。网络攻击我们无法预测&#xff0c;但做好防御措施是必须的&#xff0c;建议所有的网站都要做好防范措施&#xff0c;准备相应的防护预案&#…

RT-DETR 目标过线计数

使用 Ultralytics RT-DETR 进行目标计数 🚀 实际应用场景 物流水产养殖使用 Ultralytics RT-DETR 进行传送带包裹计数使用 Ultralytics RT-DETR 在海中进行鱼类计数请使用最新代码(2023年12月8日后),旧版本不支持! 示例 “目标计数示例” 目标计数 from ultralytics

高并发如何实现单用户信息查询接口

高并发如何实现单用户信息查询接口 故事情节 产品&#xff1a;小李&#xff0c;有个单用户信息查询的功能&#xff0c;需要你实现一下小李&#xff1a;这还不简单&#xff0c;两分钟我给你实现两分钟过去…小李&#xff1a;欧克了&#xff0c;部署上线了运维&#xff1a;哪个…

git checkout进行更改分支

git clone https://gitee.com/yaleguo1/minit-learning-demo.git下载代码。 cd minit-learning-demo/进入目录里边。 ls -l看一下当前分支的内容。 git checkout geek_chapter02更改分支到geek_chapter02。 ls -l看一下目录里边的内容。

Python 自动化之收发邮件(二)

发邮件之Windows进程监控 文章目录 发邮件之Windows进程监控前言一、基本内容二、基本结构三、库模块四、函数模块1.进程监控2.邮件发送 五、程序运行模块1.获取时间2.用户输入3.进程监控3.1进程启动发邮件3.2进程停止发邮件 总结 前言 上一篇简单写了一下如何进行邮件的收发操…

NXP应用随记(四):eMios阅读随记-整体功能概述

目录 1、eMios IP介绍 2、时钟结构 3、通道类型 4、功能介绍 5、中断与DMA 6、EMIOS -通道分配建议(针对S32K312) 1、eMios IP介绍 Emios是什么&#xff1f;eMIOS提供了独立的通道(UCs)&#xff0c;您可以配置这些通道来为不同的功能生成或测量时间事件。 每个eMIOS实例最…

智能插座是什么

智能插座 电工电气百科 文章目录 智能插座前言一、智能插座是什么二、智能插座的类别三、智能插座的原理总结 前言 智能插座的应用广泛&#xff0c;可以用于智能家居系统中的电器控制&#xff0c;也可以应用在办公室、商业场所和工业控制中&#xff0c;方便快捷地实现电器的远…

锁--07_2---- index merge(索引合并)引起的死锁

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 案例分析生产背景死锁日志表结构执行计划 EXPLAN为什么会用 index_merge&#xff08;索引合并&#xff09;为什么用了 index_merge就死锁了解决方案注&#xff1a;M…

算法训练营Day14

#Java #二叉树层次遍历 #反转二叉树 开源学习资料 二叉树的层次遍历&#xff1a;力扣题目链接 二叉树的层次遍历很好理解&#xff1a; 就是从根结点一层一层地往下遍历&#xff08;同一层&#xff0c;从左到右&#xff09;&#xff1a; 迭代的方式很好理解&#xff1a;就是…

Netty常见的设计模式

简介 设计模式在软件开发中起着至关重要的作用&#xff0c;它们是解决常见问题的经过验证的解决方案。而Netty作为一个优秀的网络应用程序框架&#xff0c;同样也采用了许多设计模式来提供高性能和可扩展性。在本文中&#xff0c;我们将探讨Netty中使用的一些关键设计模式&…

TS系列-keyof的妙用

案例1 1、如果&#xff0c;有一个接口&#xff0c;某个变量的类型&#xff0c;是这个接口的 key &#xff1f; keyof 后面可以跟 一个对象类型或者一个接口类型keyof 是把后面 对象或者接口 的 键 都提取出来&#xff0c;组成一个联合类型 interface IStudentAttr {name: stri…

【LeetCode刷题笔记(6-1)】【Python】【三数之和】【哈希表】【中等】

文章目录 三数之和题目描述示例示例1示例2示例3 提示解决方案1&#xff1a;【三层遍历查找】解决方案2&#xff1a;【哈希表】【两层遍历】 结束语 三数之和 三数之和 题目描述 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! …

nodejs微信小程序+python+PHP血液中心管理平台的设计与实现-计算机毕业设计推荐

在二十一世纪的今天&#xff0c;我国献血总量已经不容小觑&#xff0c;在全国人民的不懈努力下&#xff0c;贫血、缺血的病人已经有了足够的血液保障。与此同时&#xff0c;采血工作和血液入库、出库等工作也日愈繁重。为进一步提高采血工作和血液中心的工作效率&#xff0c;开…

【算法与数据结构】376、LeetCode摆动序列

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题难点在于要考虑到不同序列的情况&#xff0c;具体来说要考虑一下几种特殊情况&#xff1a; 1、上…