JS设计模式之策略模式:灵活、可扩展的编程利器

news2024/12/23 5:14:13

image.png

一. 前言

在 JavaScript 前端开发中,随着代码规模的增长和项目的复杂性,我们常常需要处理各种不同的条件和情况,而这可能导致代码变得冗长、难以维护。这时,我们就需要一种强大而灵活的编程模式来应对这些复杂的逻辑,策略模式就是其中之一。

策略模式是一种经典的设计模式,它通过将特定操作封装在独立的策略函数中,使得我们能够轻松地切换和组合不同的算法和策略。在 JavaScript 中,策略模式可以帮助我们处理各种条件判断、算法选择和逻辑分支,提高代码的可读性、可维护性和可扩展性。

在本篇文章中,我们将详细学习 JavaScript 策略模式的工作原理和实现步骤,从基本概念开始,帮助我们理解什么是策略模式以及它的核心思想。

通过掌握策略模式,相信大家都将能够编写更加灵活、可扩展的代码,提高你的开发效率和代码质量。

二. 什么是策略模式

JavaScript 策略模式(Strategy Pattern)是一种行为型设计模式,它可以帮助我们更好地组织和处理复杂的业务逻辑。通过将不同的算法或逻辑封装成独立的策略对象,使得这些策略对象可以在运行时根据需要进行切换或替换,从而实现灵活性和可扩展性。

1. 基础概念

  • 策略对象Strategy Object):策略对象封装了一个独立的算法或逻辑,通过接口或方法提供对外的调用接口。

  • 环境对象Context Object):环境对象拥有策略对象,并根据具体的需求选择合适的策略对象进行调用。环境对象提供了一致的接口,使得策略对象可以被灵活切换。

2. 核心思想

  • 封装变化:策略模式的核心思想是封装不同的算法或逻辑,使得它们可以独立变化,而不影响其他部分的代码。这种封装可以提高代码的可维护性和可复用性。

  • 解耦调用:通过环境对象对策略对象进行调用,将调用与具体的策略对象解耦,使得调用者不需要知道策略对象的具体实现细节,只需要通过统一的接口进行调用。

3. 作用

  • 简化复杂逻辑:策略模式可以将复杂的业务逻辑分解为多个简单的策略对象,每个对象专注于解决一个小问题,从而降低程序的复杂性。

  • 灵活扩展:由于策略对象可以独立变化,因此可以很方便地新增、替换或重构策略对象,而不会影响到其他部分的代码。这种灵活性可以帮助我们应对需求变化或新功能的加入。

  • 提高代码可复用性:将不同的算法或逻辑封装成策略对象后,可以在多个地方复用这些策略对象,避免了重复编写相似的代码。

通过理解 JavaScript 策略模式的基础概念、核心思想和作用,我们可以更好地应用这一设计模式来组织和处理复杂的业务逻辑,提高代码的可维护性和可扩展性。

4. UML 类图

以下是一个简化的策略模式的 UML 类图示例:

image.png

在上述类图中,有三个关键角色:

  1. Context:上下文对象,它包含一个私有属性strategy,用于保存当前的策略对象。Context类中还包含了两个公共方法,setStrategy()方法用于设置策略对象,executeStrategy()方法用于执行策略算法。

  2. Strategy:策略接口或者基类,它定义了一个抽象的算法方法algorithm(),具体的策略类需要实现这个方法来提供具体的算法实现。

  3. ConcreteStrategyAConcreteStrategyB:具体的策略类,它们继承或实现Strategy接口,分别实现了algorithm()方法来提供具体的算法实现。

在策略模式中,客户端代码通过Context对象与具体的策略对象进行交互,可以通过setStrategy()方法来设置所需的策略对象,然后通过executeStrategy()方法来执行策略算法。

三. 实现方式

JavaScript 中实现策略模式的一般步骤如下:

1. 定义策略函数

确定你需要实现的不同策略,并将每个策略封装在一个函数中。每个策略函数应该有相同的参数列表,并返回相同类型的结果。策略函数的数量取决于你的需求,可以是两个、三个或更多。

// Step 1: 定义策略函数
const strategyA = function(arg) {
  return arg * 2;
};

const strategyB = function(arg) {
  return arg * arg;
};

2. 创建使用策略的上下文对象

这个对象可以是一个类的实例或一个简单的对象,它将作为策略模式的使用者。该对象应该有一个方法或属性来选择使用哪个策略函数。

// Step 2: 创建上下文对象
const context = {
  strategy: null, // 用于存储所选的策略函数
  setStrategy: function(strategy) {
    this.strategy = strategy;
  },
  executeStrategy: function(arg) {
    return this.strategy(arg);
  }
};

3. 实现选择策略的方法

在上下文对象中,定义一个方法或属性来选择特定的策略函数。这个方法或属性可以基于某些条件、用户输入或其他因素来决定选择哪个策略。

// Step 3: 实现选择策略的方法
context.setStrategy(strategyA); // 选择策略A

4. 调用所选的策略函数

在上下文对象的方法中,调用所选的策略函数,并传递必要的参数。接收策略函数的返回值,并用于需要的地方。

// Step 4: 调用所选的策略函数
const resultA = context.executeStrategy(3);
console.log(resultA); // 输出: 6

context.setStrategy(strategyB); // 选择策略B

const resultB = context.executeStrategy(3);
console.log(resultB); // 输出: 9

在上述示例中,首先定义了两个策略函数 strategyAstrategyB,它们分别执行不同的操作。然后创建了一个上下文对象 context,其中 setStrategy 方法用于选择策略函数,executeStrategy 方法用于调用所选的策略函数。通过调用 setStrategy 方法来选择不同的策略,在 executeStrategy 方法中调用所选的策略函数,并传递参数进行计算。

执行上述的代码,输出结果如下图所示:

image.png

总结而言,实现策略模式的步骤涉及定义策略函数、创建上下文对象、实现选择策略的方法以及调用所选策略函数。通过这些步骤,可以实现灵活的策略切换和重用,使代码更加模块化和可扩展。

四. 应用场景

假设我们有一个购物车对象,里面包含了多个商品项,每个商品项有商品的名称和价格。我们可以定义一个计价策略对象,根据不同的计价规则进行计算。

在购物车计价的场景中,我们可以使用 JavaScript 策略模式来实现不同的计价策略。下面将以一个简单的示例来详细分析策略模式的应用。

首先,我们定义一个购物车对象和商品项的数据结构:

class ShoppingCart {
  constructor() {
    this.items = [];
  }

  addItem(name, price) {
    this.items.push({ name, price });
  }

  calculateTotalPrice(strategy) {
    return strategy.calculate(this.items);
  }
}

const shoppingCart = new ShoppingCart();
shoppingCart.addItem("item1", 10);
shoppingCart.addItem("item2", 20);
shoppingCart.addItem("item3", 30);

在上述代码中,我们定义了一个 ShoppingCart 类,它包含一个 items 数组用于存储商品项。addItem 方法用于向购物车中添加商品项。calculateTotalPrice 方法用于计算总价,它接受一个策略对象作为参数。

接下来,我们定义计价策略对象和相应的计价规则:

class RegularPriceStrategy {
  calculate(items) {
    return items.reduce((total, item) => total + item.price, 0);
  }
}

class DiscountPriceStrategy {
  constructor(discount) {
    this.discount = discount;
  }

  calculate(items) {
    const totalPrice = items.reduce((total, item) => total + item.price, 0);
    return totalPrice * (1 - this.discount);
  }
}

const regularStrategy = new RegularPriceStrategy();
const discountStrategy = new DiscountPriceStrategy(0.1);

在上述代码中,我们定义了两个计价策略对象:RegularPriceStrategyDiscountPriceStrategyRegularPriceStrategy 表示普通计价规则,即不打折的计价方式,通过对商品项的价格求和来计算总价。DiscountPriceStrategy 表示折扣计价规则,通过商品项价格求和后乘以折扣来计算总价。

最后,我们可以使用策略模式来计算购物车的总价:

const totalPrice1 = shoppingCart.calculateTotalPrice(regularStrategy);
console.log(totalPrice1); // 输出: 60

const totalPrice2 = shoppingCart.calculateTotalPrice(discountStrategy);
console.log(totalPrice2); // 输出: 54

在上述代码中,我们分别使用 calculateTotalPrice 方法来计算购物车的总价,分别传入不同的计价策略对象。通过策略对象的 calculate 方法来计算总价。

运行上述的代码,最终输出的计算结果如下图所示:

image.png

通过以上的实现,我们可以灵活地切换购物车计价的规则,只需要定义新的计价策略对象即可。这符合开闭原则,使得代码更易维护和扩展。

综上所述,购物车计价场景是策略模式的一个典型应用。我们定义了不同的计价策略对象,每个策略对象封装了不同的计价规则。通过切换不同的策略对象,实现了不同的计价方式。这样可以使得代码结构更清晰、易于维护,并且可以方便地扩展其他的计价规则。

五. 优缺点

通过以上对策略模式的了解和分析,我们可以总结一下 JavaScript 策略模式的优缺点:

优点

  1. 灵活性高:策略模式允许动态切换算法或策略,使得程序可以根据不同的需求使用不同的策略,提高了代码的灵活性。

  2. 可扩展性好:添加新的策略只需要实现一个新的策略函数即可,不需要修改已有的代码,可以很方便地对代码进行扩展。

缺点

  1. 增加了代码复杂度:在使用策略模式时,需要编写多个策略函数,可能会增加代码的复杂度和文件体积。对于简单的场景,引入策略模式可能会显得过于复杂。

  2. 需要额外管理策略对象:策略模式需要额外的对象来管理不同的策略函数,这可能增加一些额外的开销。

策略模式能够提供灵活、可扩展、可维护和可测试的代码结构,使得代码具有更好的可复用性和可变性。但是在一些简单的场景中,可能会因为引入策略模式而增加代码的复杂性,需要根据具体情况进行权衡和选择。

六. 总结

在 JavaScript 中,策略模式可以帮助我们处理不同算法、策略之间的切换和组合,为我们提供了一种强大的编程利器。

通过本篇文章的学习,我们了解了 JavaScript 策略模式的基本实现步骤。总结如下:

  • 首先,我们需要定义不同的策略函数,每个策略函数实现一个独立的算法或策略。

  • 然后,创建一个上下文对象,这个对象作为策略模式的使用者,负责选择并执行特定的策略函数。在上下文对象中,我们还可以实现选择策略的方法,根据不同的条件或需求来动态选择策略函数。

  • 最后,在上下文对象的方法中调用所选的策略函数,并处理返回的结果。

上面我们也分析了策略模式的优缺点,优点在于它的灵活性和可扩展性。通过将不同的策略封装在独立的函数中,根据不同的需求来灵活选择和切换策略,而无需修改原有的代码。然而它也有一些缺点,比如增加了代码复杂度和额外的策略管理对象。在一些简单的场景中,引入策略模式可能会显得过度设计,增加不必要的开销。

因此,在使用策略模式时,我们需要权衡利弊,选择合适的模式来应对具体的需求和问题。

总结而言,JavaScript 策略模式是一种强大的编程工具,它是可以应用于多种场景的通用解决方案,无论是处理业务逻辑、算法优化还是用户交互,都能发挥着重要的作用。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

人工智能的未来:从知识廉价时代到AI主导国家模式

随着人工智能(AI)技术的飞速发展,知识的获取和使用正变得更加普及与廉价。这不仅引发了技术领域的深刻变革,也将对全球社会经济模式产生广泛影响。特别是在《时代》杂志对风险投资巨头维诺德科斯拉(Vinod Khosla&#…

【AUTOSAR 基础软件】PduR模块详解(通信路由)

文章包含了AUTOSAR基础软件(BSW)中PduR模块相关的内容详解。本文从AUTOSAR规范解析,ISOLAR-AB配置以及模块相关代码分析三个维度来帮读者清晰的认识和了解PduR这一基础软件模块。文中涉及的ISOLAR-AB配置以及模块相关代码都是依托于ETAS提供的…

[Python学习日记-42] Python 中的生成器

[Python学习日记-42] Python 中的生成器 简介 表达式生成器 函数生成器 用生成器实现并发编程 简介 Python 中的生成器(Generator)是一种特殊的迭代器,它又被成为惰性运算,它可以在迭代过程中动态生成值,而不需要事…

HTML CSS 基础

HTML & CSS 基础 HTML一、HTML简介1、网页1.1 什么是网页1.2 什么是HTML1.3 网页的形成1.4总结 2、web标准2.1 为什么需要web标准2.2 Web 标准的构成 二、HTML 标签1、HTML 语法规范1.1基本语法概述1.2 标签关系 2、 HTML 基本结构标签2.1 第一个 HTML 网页2.2 基本结构标签…

PHP input 多文件上传功能实现-网页不为人知的数据库缺陷——未来之窗行业应用跨平台架构

一、多文件上传html部分 1.1错误示例 <input type"file" class"input fl" name"file" style"width:200px;display:inline;border:0px;" multiple />1.2 正确示例 <input type"file" class"input fl" …

Vxe UI vue vxe-table 实现表格单元格选中功能

Vxe UI vue vxe-table 实现表格单元格选中功能 在表格中实现鼠标点击任意单元格&#xff0c;选取的功能&#xff0c;通过 mouse-config 配置就可以开启单选功能&#xff0c;多选单元格选取功能需安装插件支持。 代码 参数说明 mouse-config 鼠标配置项&#xff1a; selected&…

Linux之shell详解(Linux Shell Detailed Explanation)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

【韩顺平Java笔记】第8章:面向对象编程(中级部分)【285-296】

文章目录 285. 为什么需要继承286. 继承原理图287. 继承快速入门288. 289. 290. 291. 292. 继承使用细节1,2,3,4,5288.1 继承给编程带来的便利288.2 继承的深入讨论/细节问题 293. 继承本质详解294. 继承课堂练习1295. 继承课堂练习2296. 继承课堂练习3 285. 为什么需要继承 28…

【软件部署安装】OpenOffice转换PDF字体乱码

现象与原因分析 执行fc-list查看系统字体 经分析发现&#xff0c;linux默认不带中文字体&#xff0c;因此打开我们本地的windows系统的TTF、TTC字体安装到centos机器上。 安装字体 将Windows的路径&#xff1a; C:\Windows\Fonts 的中文字体&#xff0c;如扩展名为 TTC 与TT…

App模拟心跳长连接的实现方法demo

摘要 背景&#xff1a;心跳通常是指客户端或服务器定期发送一个小型的、空的消息以保持连接的活动状态。它用于检测连接是否仍然有效&#xff0c;并防止连接由于长时间没有活动而被关闭。 技术原理&#xff1a;App定时发消息给服务器&#xff0c;服务器回消息表示连接依旧有效…

手机竖屏 Premiere Pro 电影转场特效视频模板Pr工程文件

10个不同的类别和115个过渡。过渡很容易使用。随附视频教程。 下载地址&#xff1a;Pr模板网 下载链接&#xff1a;https://prmuban.com/40597.html

动态规划算法专题(五):子序列问题

目录 1、最长递增子序列 1.1 算法原理 1.2 算法代码 2、摆动序列 2.1 算法原理 2.2 算法代码 3、最长递增子序列的个数 3.1 算法原理 3.2 算法代码 4、最长数对链 4.1 算法原理 4.2 算法代码 5、最长定差子序列 5.1 算法原理 5.2 算法代码 6、最长的斐波那契子序…

NASA:气象追踪分子光谱(ATMOS)二级产品,包含在垂直高度(公里)网格上的微量气体

目录 简介 摘要 引用 网址推荐 0代码在线构建地图应用 机器学习 ATMOS L2 Trace Gases on Altitude Grid, Tab Delimited Format V3 (ATMOSL2AT) at GES DISC 简介 这是版本3的气象追踪分子光谱&#xff08;ATMOS&#xff09;二级产品&#xff0c;包含在垂直高度&#…

多线程股吧(东方财富)用户信息爬取

多线程东方财富&#xff08;股吧&#xff09;用户信息爬取 在上一篇博客股吧信息爬取的基础上加入了多线程&#xff0c;使得速度提升了十几倍&#xff0c;爬取内容如下&#xff1a; 最终爬取结果如下&#xff1a; 完整代码如下&#xff08;准备好环境&#xff0c;比如pytho…

安宝特案例 | Fundació Puigvert 医院应用AR技术开创尿石症治疗新纪元

案例介绍 在医疗科技不断进步的今天&#xff0c;Fundaci Puigvert 医院迈出了重要一步&#xff0c;成功应用AR技术进行了全球首例同时使用两台内窥镜的ECIRS手术&#xff08;内镜肾内联合手术&#xff09;&#xff0c;由Esteban Emiliani M.D. PhD F.E.B.U 博士主刀。这标志着…

yub‘s Algorithmic Adventures_Day7

环形链表 link&#xff1a;https://leetcode.cn/problems/linked-list-cycle-ii/description/ 思路分析 我只能说双指针yyds【刻板hh】 我们分两种情况来分析 起码在第二圈才会相遇 fast比slow多走环的整数倍 fast 走的步数是 slow 步数的 2 倍&#xff0c;即 f2s&#xff…

5.资源《Arduino UNO R3 proteus 使用CD4511驱动数码管工程文件(含驱动代码)》说明。

资源链接&#xff1a; Arduino UNO R3 proteus 使用CD4511驱动数码管工程文件&#xff08;含驱动代码&#xff09; 1.文件明细&#xff1a; 2.文件内容说明 包含&#xff1a;proteus工程&#xff0c;内含设计图和工程代码。 3.内容展示 4.简述 工程功能可以看这个视频 数码…

微信小程序流量主

开发小程序也已经有一段时间了,也是为了添加流量主来开发小程序,根据小程序的定位,来获取用户想要的资源,通过广告的形式来增加用户的点击量进行收益,收益虽然微不足道,但是也是很有成就感的

活动邀请 | SonarQube×创实信息即将亮相2024 GOPS全球运维大会-上海站,分享代码质量与安全提升策略

2024年10月18日-19日&#xff08;周五-周六&#xff09;&#xff0c;第二十四届 GOPS 全球运维大会上海站将在上海中庚聚龙酒店举办。 大会为期2天&#xff0c;侧重大模型、DevOps、SRE、AIOps、BizDevOps、云原生及安全等热门技术领域。特设了如大模型 运维/研发测试、银行/…

宠物咖啡馆服务平台:SpringBoot技术深度解析

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理基于Spring Boot的宠物咖啡馆平台的设计与…