当前位置: 首页 > 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;第二行输入一个…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...

Pydantic + Function Calling的结合

1、Pydantic Pydantic 是一个 Python 库&#xff0c;用于数据验证和设置管理&#xff0c;通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发&#xff08;如 FastAPI&#xff09;、配置管理和数据解析&#xff0c;核心功能包括&#xff1a; 数据验证&#xff1a;通过…...