javascript 常见设计模式

news2024/12/23 14:56:17

什么是设计模式?

在软件开发中,设计模式是解决特定问题的经验总结和可复用的解决方案。设计模式可以提高代码的复用性、可维护性和可读性,是提高开发效率的重要手段。

单例模式

1.概念

单例模式 (Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点。也就是说,第二次使用同一个类创建新对象的时候,应该得到与第一次创建的对象完全相同的对象。

2.代码实现

class Singleton {
  static instance;
  constructor() {
    if (!Singleton.instance) {
      Singleton.instance = this;
    }
    return Singleton.instance;
  }
}

const instance1 = new Singleton();
const instance2 = new Singleton();

console.log(instance1 === instance2); // true
3.应用场景
  • 浏览器中的 window 和 document 全局变量,这两个对象都是单例,任何时候访问他们都是一样的对象,window 表示包含 DOM 文档的窗口,document 是窗口中载入的 DOM 文档,分别提供了各自相关的方法。
  • element-ui 中以服务方式调用的Loading 是单例的。
  • 项目中的全局状态管理模式 Vuex 维护的全局状态,vue-router`维护的路由实,在单页应用的单页面中都属于单例的应用。
4.优缺点

优点

  • 内存中只有一个实例,减少内存开销,尤其是频繁创建和销毁实例时(如管理学院首页页面缓存)。
  • 避免资源的多重占用(如写文件操作)。

缺点

  • 扩展不友好:没有接口,不能继承。
  • 与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心实例化方式。

二、发布订阅模式

1.概念

JavaScript 发布订阅模式(Publish/Subscribe Pattern)是一种常用的设计模式。在发布订阅模式中,事件的发生者(发布者)不需要直接调用事件的处理者(订阅者),而是通过一个「发布-订阅中心」来管理事件的发生和处理。具体来说,发布者将事件发布到「发布-订阅中心」中,订阅者可以向「发布-订阅中心」注册事件处理函数,当事件发生时,「发布-订阅中心」会将事件通知给所有注册了该事件处理函数的订阅者,订阅者就可以处理该事件。
发布订阅模式的核心思想是解耦事件的发生和事件的处理,使得事件发生者和事件处理者之间不直接依赖,从而提高程序的灵活性和可维护性。

2.代码实现


/**
 * @description: 发布订阅
 * @return {*}
 */
// events: {
//   [key]: [{fn: Function, isOnce: Boolean},],
//   [key2]: [{fn: Function, isOnce: Boolean}, ],
// }
class EventBus {
  events = {}
  constructor() {
    this.events = {}
  }

  // 注册事件
  on(key, fn, isOnce = false) {
    if (!key) return
    const currentEvents = this.events[key]
    if (!currentEvents) {
      this.events[key] = []
    }
    this.events[key].push({ fn, isOnce })
  }
  // once
  once(key, fn) {
    this.on(key, fn, true)
  }
  // 触发事件
  emit(key, ...args) {
    if (!key) return
    const currentEvents = this.events[key]
    if (!currentEvents || !currentEvents.length) {
      return
    }
    this.events[key] = currentEvents.filter((item) => {
      item.fn(...args)
      return !item.isOnce
    })
  }
  // 取消订阅
  off(key, fn = undefined) {
    if (!this.events[key]) return
    if (!fn) {
      this.events[key] = []
    } else {
      this.events[key] = this.events[key].filter((_) => _.fn !== fn)
    }
  }
}
const e = new EventBus()

function fn1(a, b) { console.log('fn1', a, b) }
function fn2(a, b) { console.log('fn2', a, b) }
function fn3(a, b) { console.log('fn3', a, b) }

e.on('key1', fn1)
e.on('key1', fn2)
e.once('key1', fn3)
e.emit('key1', 10, 20) // 触发 fn1 fn2 fn3
e.off('key1', fn1)
e.emit('key1', 100, 200) // 触发 fn2
3.应用场景
4.优缺点

优点

  • 发布-订阅模式的有点非常明显,一为时间上的解耦,二为对象之间的解耦。
  • 应用广泛,既可以用在异步编程中,也可以帮助我们更松耦合的代码编写。

缺点

  • 创建订阅者本身要消耗一定的时间和内存,而且当订阅一个消息后,也许此消息最后都未发生,但这个订阅者会始终存在于内存中。
  • 发布-订阅模式虽然可以弱化对象之间的联系,但如果过度使用的话,对象与对象之间必要的联系也将被深埋在背后,会导致程序难以跟踪维护和理解。

二、观察者模式

1.概念

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。

2.代码实现
let observerId = 1;
let observedId = 10;
//观察者类
class Observer {
  constructor() {
    this.id = observerId++;
  }
  //观测到变化后的处理
  update(ob) {
    console.log("观察者" + this.id + `-检测到被观察者${ob.id}变化`);
  }
}

//被观察者列
class Observed {
  constructor() {
    this.observers = [];
    this.id = observedId++;
  }
  //添加观察者
  addObserver(observer) {
    this.observers.push(observer);
  }
  //删除观察者
  removeObserver(observer) {
    this.observers = this.observers.filter(o => {
      return o.id != observer.id;
    });
  }
  //通知所有的观察者
  notify() {
    this.observers.forEach(observer => {
      observer.update(this);
    });
  }
}
let mObserved = new Observed();
let mObserver1 = new Observer();
let mObserver2 = new Observer();

mObserved.addObserver(mObserver1);
mObserved.addObserver(mObserver2);

mObserved.notify();
3.应用场景

微信公众号,如果一个用户订阅了某个公众号,那么便会收到公众号发来的消息,那么,公众号就是『被观察者』,而用户就是『观察者』

观察者模式 VS 发布订阅模式

在这里插入图片描述

对象关系
  • 观察者模式中,被观察者和观察者之间的关系是一对多的关系,即一个被观察者可以有多个观察者,但是每个观察者只关注一个被观察者。被观察者维护一个观察者列表,当被观察者发生变化时,通知所有观察者进行相应的处理。
  • 发布订阅模式中,发布者和订阅者之间的关系是多对多的关系,即一个发布者可以有多个订阅者,每个订阅者可以关注多个发布者。发布者和订阅者之间通过「发布-订阅中心」进行通信,当发布者发生变化时,通知所有订阅者进行相应的处理。
解耦
  • 在观察者模式中,被观察者和观察者之间的通信是直接的,即被观察者会直接调用观察者的方法进行通信。这种直接的通信方式可能会导致被观察者与观察者之间的耦合度较高。
  • 在发布订阅模式中,发布者和订阅者之间的通信是通过「发布-订阅中心」进行的,即发布者不直接与订阅者通信,而是通过「发布-订阅中心」进行通信。这种间接的通信方式可以降低发布者与订阅者之间的耦合度。

三、策略模式

1.概念

策略模式(Strategy Pattern)指的是定义一系列的算法,把它们一个个封装起来,并且使它们可以互相替换。封装的策略算法一般是独立的,策略模式根据输入来调整采用哪个算法。
⽬的就是将策略的实现和使用分离。

Context :封装上下文,根据需要调用需要的策略,屏蔽外界对策略的直接调用,只对外提供一个接口,根据需要调用对应的策略;
Strategy :策略,含有具体的算法,其方法的外观相同,因此可以互相代替;
StrategyMap :所有策略的合集,供封装上下文调用;

2.应用(表单校验)

表单验证是一个常见的应用场景,而策略模式可以很好地应用于表单验证的实现。通过策略模式,可以将不同的验证规则封装成策略对象,根据具体的情况选择相应的验证策略进行验证。这样可以实现更加灵活和可扩展的表单验证功能。

// 定义表单验证策略对象
const strategies = {
  isNotEmpty(value, errorMsg) {
    if (value === '') {
      return errorMsg;
    }
  },
  isEmail(value, errorMsg) {
    const emailReg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*.\w+([-.]\w+)*$/;
    if (!emailReg.test(value)) {
      return errorMsg;
    }
  },
  minLength(value, length, errorMsg) {
    if (value.length < length) {
      return errorMsg;
    }
  },
};// 表单验证类
class Validator {
  constructor() {
    this.rules = [];
  }
  addRule(value, rule, errorMsg) {
    this.rules.push(() => strategies[rule](value, errorMsg));
  }
  validate() {
    for (let rule of this.rules) {
      const errorMsg = rule();
      if (errorMsg) {
        return errorMsg;
      }
    }
  }
}// 使用策略模式进行表单验证
const validator = new Validator();
validator.addRule('example@qq.com', 'isNotEmpty', '邮箱不能为空');
validator.addRule('example@qq.com', 'isEmail', '邮箱地址无效');
const error = validator.validate();
if (error) {
  console.log(error);
} else {
  console.log('表单验证通过');
}

4.优缺点

优点

  • 算法切换自由:可以在运行时根据需要切换算法。
  • 避免多重条件判断:消除了复杂的条件语句。
  • 扩展性好:新增算法只需新增一个策略类,无需修改现有代码。

缺点

  • 策略类数量增多:每增加一个算法,就需要增加一个策略类。
  • 所有策略类都需要暴露:策略类需要对外公开,以便可以被选择和使用。

四代理模式

1.概念

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代码举例
1、图片懒加载
//图片加载
let imageEle = (function(){
    let node = document.createElement('img');
    document.body.appendChild(node);
    return {
        setSrc:function(src){
            node.src = src;
        }
    }
})();
//代理对象
let proxyImage = (function(){
    let img = new Image();
    img.onload = function(){
        imageEle.setSrc(this.src);
    };
    return {
        setSrc:function(src){
            img.src = src;
            imageEle.setSrc('loading.gif');
        }
    }
})();
proxyImage.setSrc('example.png');
2.缓存代理

缓存代理可以作为一些开销大的运算结果提供暂时的存储,下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果

//计算乘积
let mult = function(){
    let result = 1;
    for(let i = 0,len = arguments.length;i < len;i++){
        result*= arguments[i];
    }
    return result;
}

//缓存代理
let proxy = (function(){
    let cache = {};
    reutrn function(){
        let args = Array.prototype.join.call(arguments,',');
        if(args in cache){
            return cache[args];
        }
        return cache[args] = mult.apply(this,arguments);
    }
})();
3.优缺点

优点

  • 职责分离:代理模式将访问控制与业务逻辑分离。
  • 扩展性:可以灵活地添加额外的功能或控制。
  • 智能化:可以智能地处理访问请求,如延迟加载、缓存等。
    缺点
  • 性能开销:增加了代理层可能会影响请求的处理速度。
  • 实现复杂性:某些类型的代理模式实现起来可能较为复杂。

注:更多设计模式待后续补充…

参考链接

  • https://juejin.cn/post/6844904032826294286?searchId=202407012147303848AD829D3DBC1E62B7
  • https://juejin.cn/post/7372514352425926668
  • https://juejin.cn/post/6844904138707337229?searchId=20240701174747B0D1A8A77127F94493AD#heading-19
  • https://www.runoob.com/design-pattern/design-pattern-tutorial.html

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

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

相关文章

c++ 智能指针实战分析

一.智能指针的设计思路 智能指针是类模板&#xff0c;再栈上创建智能指针对象。把普通指针交给智能指针对象。智能指针对象过期时&#xff0c;调用析构函数释放普通指针的内存。 智能指针的类型 auto_ptr是C98的标准&#xff0c;c17已经弃用。unique_ptr、shared_ptr和weak_…

动手学深度学习(Pytorch版)代码实践 -计算机视觉-41目标检测数据集

41目标检测数据集 import os import pandas as pd import torch import torchvision import matplotlib.pylab as plt from d2l import torch as d2l# 数据集下载链接 # http://d2l-data.s3-accelerate.amazonaws.com/banana-detection.zip# 读取数据集 #save def read_data_b…

Mustango——音乐领域知识生成模型探索

Mustango&#xff1a;利用领域知识的音乐生成模型 论文地址&#xff1a;https://arxiv.org/pdf/2311.08355.pdf 源码地址&#xff1a;https://github.com/amaai-lab/mustango 论文题为**“**利用音乐领域知识开发文本到音乐模型’Mustango’”。它利用音乐领域的知识从文本指…

计算机毕业设计Python深度学习美食推荐系统 美食可视化 美食数据分析大屏 美食爬虫 美团爬虫 机器学习 大数据毕业设计 Django Vue.js

Python美食推荐系统开题报告 一、项目背景与意义 随着互联网和移动技术的飞速发展&#xff0c;人们的生活方式发生了巨大变化&#xff0c;尤其是餐饮行业。在线美食平台如雨后春笋般涌现&#xff0c;为用户提供了丰富的美食选择。然而&#xff0c;如何在海量的餐饮信息中快速…

python 笔试面试八股(自用版~)

1 解释型和编译型语言的区别 解释是翻译一句执行一句&#xff0c;更灵活&#xff0c;eg&#xff1a;python; 解释成机器能理解的指令&#xff0c;而不是二进制码 编译是整个源程序编译成机器可以直接执行的二进制可运行的程序&#xff0c;再运行这个程序 比如c 2 简述下 Pyth…

2.2章节python的变量和常量

在Python中&#xff0c;变量和常量有一些基本的概念和用法&#xff0c;但需要注意的是&#xff0c;Python本身并没有内置的“常量”类型。然而&#xff0c;程序员通常会遵循一种约定&#xff0c;即使用全部大写的变量名来表示常量。 一、变量 在Python中&#xff0c;变量是一…

对不起,AI大模型不是风口

“我们正处在全新起点&#xff0c;这是一个以大模型为核心的人工智能新时代&#xff0c;大模型改变了人工智能&#xff0c;大模型即将改变世界。”——5月26日&#xff0c;百度创始人、董事长兼CEO李彦宏先生在2023中关村论坛发表了《大模型改变世界》演讲。 李彦宏指出&#…

4PCS点云配准算法实现

4PCS点云配准算法的C实现如下&#xff1a; #include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/common/common.h> #include <pcl/common/distances.h> #include <pcl/common/transforms.h> #in…

PostgreSQL介绍与安装

一、PostgreSQL数据库介绍 1、什么是数据库&#xff1f; 数据库&#xff08;Database&#xff09;是按照数据结构来组织、存储和管理数据的仓库。每个数据库都有一个或多个不同的 API 用于创建&#xff0c;访问&#xff0c;管理&#xff0c;搜索和复制所保存的数据。 我们也…

JAVA医院绩效考核管理系统源码:系统优势、系统目的、系统原则 (自主研发 功能完善 可直接上项目)

JAVA医院绩效考核管理系统源码&#xff1a;系统优势、系统目的、系统原则 &#xff08;自主研发 功能完善 可直接上项目&#xff09; 医院绩效考核系统优势 1.实现科室负责人单独考核 对科室负责人可以进行单独考核、奖金发放。 2. 科室奖金支持发放到个人 支持奖金二次分配&…

同一个excel表格,为什么在有的电脑上会显示#NAME?

一、哪些情况会产生#NAME?的报错 1.公式名称拼写错误 比如求和函数SUM&#xff0c;如果写成SUN就会提示#NAME&#xff1f;报错。 2.公式中的文本值未添加双引号 如下图&#xff1a; VLOOKUP(丙,A:B,2,0) 公式的计算结果会返回错误值#NAME?&#xff0c;这是因为公式中文本…

GraphPad Prism生物医学数据分析软件下载安装 GraphPad Prism轻松绘制各种图表

Prism软件作为一款功能强大的生物医学数据分析与可视化工具&#xff0c;其绘图功能尤为突出。该软件不仅支持绘制基础的图表类型&#xff0c;如直观明了的柱状图、展示数据分布的散点图&#xff0c;以及描绘变化趋势的曲线图&#xff0c;更能应对复杂的数据呈现需求&#xff0c…

7-1作业

1.实验目的&#xff1a;完成字符收发 led.h #ifndef __GPIO_H__ #define __GPIO_H__#include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_uart.h"//RCC,GPIO,UART初始化 void init();//字符数据发送 void set_tt…

【2024 插件开发大赛】为 ONLYOFFICE 开发插件,赢取万元奖金!

我们发布了 2024 插件开发大赛&#xff1a;为 ONLYOFFICE 开发适合中国用户的插件&#xff0c;赢取税前5500 – 10000元的结项奖金与证书&#xff01;阅读本文了解详情。 关于 ONLYOFFICE ONLYOFFICE 是一个国际开源项目&#xff0c;由领先的 IT 公司 Ascensio System SIA 开发…

论坛万能粘贴手(可将任意文件转为文本)

该软件可将任意文件转为文本。 还原为原文件的方法&#xff1a;将得到的文本粘贴到记事本&#xff0c;另存为UUE格式&#xff0c;再用压缩软件如winrar解压即可得到原文件。建议用于小软件。 下载地址&#xff1a;https://download.csdn.net/download/wgxds/89505015 使用演示…

算法基础入门 - 2.栈、队列、链表

文章目录 算法基础入门第二章 栈、队列、链表2.1 队列2.2 栈2.3 纸牌游戏2.4 链表如何建立链表?1.我们需要一个头指针(head)指向链表的初始。链表还没建立时头指针head为空2.建立第一个结点3.设置刚创建的这个结点的数据域(左半)和指针域(右半)4.设置头指针,头指针可方便…

构建高效的数字风控系统:应对现代网络威胁的策略与实践

文章目录 构建高效的数字风控系统&#xff1a;应对现代网络威胁的策略与实践1. 数字风控基本概念1.1 数字风控&#xff08;数字化风控&#xff09;1.2 数字风控的原理1.3 常见应用场景 2. 数字风控的必要性3. 构建高效的数字风控系统3.1 顶层设计与规划3.2 数据基础建设3.3 风险…

代码随想录第38天|动态规划

1049. 最后一块石头的重量 II 参考 备注: 当物体容量也等同于价值时, 01背包问题的含义则是利用好最大的背包容量sum/2, 使得结果尽可能的接近或者小于 sum/2 等价: 尽可能的平分成相同的两堆, 其差则为结果, 比如 (abc)-d, (ac)-(bd) , 最终的结果是一堆减去另外一堆的和, 问…

Nik Collection by DxO:摄影师的创意利器与调色宝典

在数码摄影的世界里&#xff0c;后期处理是摄影师们展现创意、调整细节、提升作品质量的重要步骤。而Nik Collection by DxO作为一款由DxO公司开发的强大照片编辑插件套件&#xff0c;为摄影师们提供了一套全面的、功能丰富的工具集&#xff0c;让他们的创意得以充分发挥。 Ni…

数据结构 - C/C++ - 栈

目录 结构特性 结构实现 结构容器 结构设计 顺序栈 链式栈 结构特性 栈(stack)是线性表的一种形式&#xff0c;限定仅在表的一端进行插入或者删除的操作。 栈顶 - 表中允许插入、删除的一端称为栈顶(top)&#xff0c;栈顶位置是可以发生变化的。 插入 - 进栈、入栈、压栈…