当前位置: 首页 > news >正文

设计模式 -- 策略模式(传统面向对象与JavaScript 的对比实现)

设计模式 – 策略模式(传统面向对象与JavaScript 的对比实现)

文章目录

  • 设计模式 -- 策略模式(传统面向对象与JavaScript 的对比实现)
    • 使用策略模式计算年终奖
      • 初级实现
        • 缺点
      • 使用组合函数重构代码
        • 缺点
      • 使用策略模式重构代码
        • 传统的面向对象的策略模式实现方法
        • 最终实现 -- JavaScript 版本的策略模式
        • 多态
        • 终极实现 -- JavaScript 版本的精简版实现

使用策略模式计算年终奖

规则:根据员工的工资基数和年底绩效情况计算年终奖


初级实现

		const calculateBonus = function (performanceLevel, salary) {if (performanceLevel === 'S') {return salary * 4;}if (performanceLevel === 'A') {return salary * 3;}if (performanceLevel === 'B') {return salary * 2;}};calculateBonus('B', 20000); // 输出:40000 calculateBonus('S', 6000); // 输出:24000

缺点

  1. 多重 if else
  2. 违反开发-封闭原则,可维护性差
  3. 复用性差

使用组合函数重构代码

使用组合函数来重构代码,把各种算法封装到一个个的小函数里面,这些小函数有着良好的命名,可以一目了然地知道它对应着哪种算法,它们也可以被复用在程序的其他地方。

		const performanceS = function (salary) {return salary * 4;};const performanceA = function (salary) {return salary * 3;};const performanceB = function (salary) {return salary * 2;};const calculateBonus = function (performanceLevel, salary) {if (performanceLevel === 'S') {return performanceS(salary);}if (performanceLevel === 'A') {return performanceA(salary);}if (performanceLevel === 'B') {return performanceB(salary);}};calculateBonus('A', 10000); // 输出:30000

缺点

程序得到了一定的改善,但这种改善非常有限,我们依然没有解决最重要的问题:

  1. calculateBonus 函数有可能越来越庞大
  2. 而且在系统变化的时候缺乏弹性

使用策略模式重构代码

策略模式指的是定义一系列的算法,把它们一个个封装起来。将不变的部分和变化的部分隔开是每个设计模式的主题,策略模式也不例外,策略模式的目的就是将算法的使用与算法的实现分离开来。

一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。 第二个部分是环境类 Context,Context 接受客户的请求,随后把请求委托给某一个策略类。要做到这点,说明 Context 中要维持对某个策略对象的引用。

更详细一点,就是: 定义一系列的算法,把它们各自封装成策略类,算法被封装在策略类内部的方法里。在客户对 Context 发起请求的时候,Context 总是把请求委托给这些策略对象中间的某一个进行计算。


传统的面向对象的策略模式实现方法

 const performanceS = function () { };performanceS.prototype.calculate = function (salary) {return salary * 4;};const performanceA = function () { };performanceA.prototype.calculate = function (salary) {return salary * 3;};const performanceB = function () { };performanceB.prototype.calculate = function (salary) {return salary * 2;};const Bonus = function () {this.salary = null; // 原始工资this.strategy = null;// 绩效等级对应的策略对象};Bonus.prototype.setSalary = function (salary) {this.salary = salary; // 设置员工的原始工资};Bonus.prototype.setStrategy = function (strategy) {this.strategy = strategy; // 设置员工绩效等级对应的策略对象};Bonus.prototype.getBonus = function () { // 取得奖金数额return this.strategy.calculate(this.salary); // 把计算奖金的操作委托给对应的策略对象};const bonus = new Bonus();bonus.setSalary(10000);bonus.setStrategy(new performanceS()); // 设置策略对象console.log(bonus.getBonus()); // 输出:40000 bonus.setStrategy(new performanceA()); // 设置策略对象console.log(bonus.getBonus()); // 输出:30000

可以看到通过策略模式重构之后,代码变得更加清晰,各个类的职责更加鲜明。


最终实现 – JavaScript 版本的策略模式

        const strategies = {"S": function (salary) {return salary * 4;},"A": function (salary) {return salary * 3;},"B": function (salary) {return salary * 2;}};const calculateBonus = function (level, salary) {return strategies[level](salary);};console.log(calculateBonus('S', 20000));console.log(calculateBonus('A', 10000));

在 JavaScript 语言中,函数也是对象,所以更简单和直接的做法是把 strategy 直接定义为函数。
同样,Context 也没有必要必须用 Bonus 类来表示,我们依然用 calculateBonus 函数充当Context 来接受用户的请求。经过改造,代码的结构变得更加简洁。


多态

通过使用策略模式重构代码,我们消除了原程序中大片的条件分支语句。所有跟计算奖金有 关的逻辑不再放在 Context 中,而是分布在各个策略对象中。Context 并没有计算奖金的能力,而 是把这个职责委托给了某个策略对象。每个策略对象负责的算法已被各自封装在对象内部。当我 们对这些策略对象发出“计算奖金”的请求时,它们会返回各自不同的计算结果,这正是对象多态性的体现,也是“它们可以相互替换”的目的。替换 Context 中当前保存的策略对象,便能执行不同的算法来得到我们想要的结果。


终极实现 – JavaScript 版本的精简版实现

使用 map 来做映射(配置文件),若以后新增规则,则直接在map中增加,保证了可维护性

		const map = {'S': 4,'A': 3,'B': 2}const calculateBonus = function (level, salary) {return map[level] * salary;};console.log(calculateBonus('S', 20000));console.log(calculateBonus('A', 10000));

参考文献:
JavaScript 设计模式与开发实践 (by 曾探)

相关文章:

设计模式 -- 策略模式(传统面向对象与JavaScript 的对比实现)

设计模式 – 策略模式(传统面向对象与JavaScript 的对比实现) 文章目录 设计模式 -- 策略模式(传统面向对象与JavaScript 的对比实现)使用策略模式计算年终奖初级实现缺点 使用组合函数重构代码缺点 使用策略模式重构代码传统的面…...

非常详细的 Ceph 介绍、原理、架构

1. Ceph架构简介及使用场景介绍 1.1 Ceph简介 Ceph是一个统一的分布式存储系统,设计初衷是提供较好的性能、可靠性和可扩展性。 Ceph项目最早起源于Sage就读博士期间的工作(最早的成果于2004年发表),并随后贡献给开源社区。在经过…...

js 的正则表达式(二)

1.正则表达式分类: 正则表达式分为普通字符和元字符。 普通字符: 仅能够描述它们本身,这些字符称作普通字符,例如所有的字母和数字。也就是说普通字符只能够匹配字符串中与它们相同的字符。 元字符: 是一些具有特殊含…...

星际争霸之小霸王之小蜜蜂(四)--事件监听-让小蜜蜂动起来

目录 前言 一、监听按键并作出判断 二、持续移动 三、左右移动 总结: 前言 今天开始正式操控我们的小蜜蜂了,之前学java的时候是有一个函数监听鼠标和键盘的操作,我们通过传过来不同的值进行判断,现在来看看python是否一样的实现…...

Visual Studio 2022 你必须知道的实用调试技巧

目录 1、什么是bug? 2.调试是什么?有多重要? 2.1我们是如何写代码的? 2.2又是如何排查出现的问题的呢? ​编辑 2.3 调试是什么? 2.4调试的基本步骤 2.5Debug和Release的介绍 3.Windows环境调试介绍…...

Webgl 存储限定符attribute、gl.getAttribLocation、gl.vertexAttrib3f及其同族函数和矢量版本的介绍

目录 attribute变量规范 获取attribute变量的存储位置 gl.getAttribLocation()函数的规范: 向attribute变量赋值 gl.vertexAttrib3f()的规范。 gl.vertexAttrib3f()的同族函数 示例代码…...

postgresql跨库创建视图

需求: A库a表中的字段拆分1个到B库b表,所以b表中只保留唯一标识字段(可以理解为id)和另一个被拆分的字段 需要用到的拓展:CREATE EXTENSION dblink 使用dblink创建连接: SELECT dblink_connect(other_db, hostaddr【IP…...

FPGA时钟

几年前FPGA时钟只需要连接一个单端输入的晶振,非常容易。现在不同了,差分时钟输入,差分信号又分为LVDS和LVPECL,时钟芯片输出后还要经过直流或交流耦合才能接入FPGA,有点晕了,今天仔细研究一下。 FPGA输入…...

FifthOne:计算机视觉提示和技巧

一、说明 欢迎来到我们每周的FiftyOne提示和技巧博客,我们回顾了最近在Slack,GitHub,Stack Overflow和Reddit上弹出的问题和答案。FiftyOne是一个开源机器学习工具集,使数据科学团队能够通过帮助他们策划高质量数据集、评估模型、…...

Oracle19c-补丁升级报错合集(一)

前言: 本文主要介绍Oracle19c补丁升级遇到的问题,涉及安装补丁prepatch步骤,apply应用报错以及datapatch -verbose数据字典更新报错 问题一: 在执行补丁rootcrs.sh -prepatch操作时,发生执行检查命令cluutil -chkshare报错 CLSRSC-180: An …...

嵌入式:ARM Day6

作业:完成cortex-A7核UART总线实验 目的:1.输入a,显示b,将输入的字符的ASCII码下一位字符输出 2.原样输出输入的字符串 源码: uart4.h #ifndef __UART4_H__ #define __UART4_H__#include "stm32mp1xx_rcc.h" #incl…...

ClickHouse安装步骤

文章目录 ClickHouse安装步骤背景安装启动用户相关修改密码登录验证新增用户config配置文件 基本操作服务管理 ClickHouse安装步骤 背景 经过研究ClickHouse是列式数据库,下面是在Centos7.9版本单机版的安装的演示 安装 首先安装yum-utils工具包 sudo yum inst…...

Android CCodec (二十) CCodec Native服务实现分析

1、C2解码服务registerAsService注册流程 google实现CCodec的vendor默认解码服务代码路径是在frameworks/av/media/codec2/hidl/services/vendor.cpp中,而其注册的是HIDL服务,本文就对HIDL服务注册做简要分析。首先看下vendor.cpp中的代码注册流程。 int main(int /* argc *…...

Shell编程学习之while循环语句和for循环语句的应用

Shell编程中的while循环:可以类比C语言中的while循环进行学习,详见本人原创博客链接: https://blog.csdn.net/qq_41878292/article/details/132081302?spm1001.2014.3001.5502伪代码表示: while 表达式 do循环体 done用法类比S…...

【校招VIP】CSS校招考点之选择器优先级

考点介绍: 选择器是CSS的基础,也是校招中的高频考点,特别是复合选择器的执行优先级,同时也是实战中样式不生效的跟踪依据。 因为选择器的种类较多,很难直接记忆,可以考虑选择一个相对值,比如id类…...

Netty+springboot开发即时通讯系统笔记(四)终

实时性 1.线程池多线程,把消息同步给其他端和对方用户,其中数据持久化往往是最浪费时间的操作,可以使用mq异步存储,因为其他业务不需要拿着整条数据,只需要这条数据的id进行操作。 2。消息校验前置,放在t…...

java -jar 启动服务后,关闭命令窗口后服务停止

java -jar 启动服务后,关闭命令窗口后服务停止 问题:当我们用java -jar命令启动服务后,只有一直保持Xshell的窗口开启且正常连接服务器时才能访问服务,当关闭命令窗口时,服务会停止运行 解决:使用nohup命…...

Android PowerManager的使用

唤醒锁定 如果需要使 CPU 保持运行状态&#xff0c;以便在设备进入休眠模式之前完成某项工作&#xff0c;可以使用“唤醒锁定” 。 添加权限 <uses-permission android:name"android.permission.WAKE_LOCK" />设置唤醒锁定 PowerManager powerManager (Po…...

安防监控/视频集中存储/云存储平台EasyCVR v3.3增加首页告警类型

安防监控/视频集中存储/云存储EasyCVR视频汇聚平台&#xff0c;可支持海量视频的轻量化接入与汇聚管理。平台能提供视频存储磁盘阵列、视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、平台级联、H.265自动转码等…...

7-6 统计字符出现次数

分数 20 全屏浏览题目 切换布局 作者 C课程组 单位 浙江大学 本题要求编写程序&#xff0c;统计并输出某给定字符在给定字符串中出现的次数。 输入格式&#xff1a; 输入第一行给出一个以回车结束的字符串&#xff08;少于80个字符&#xff09;&#xff1b;第二行输入一个…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

scikit-learn机器学习

# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...