常见设计模式

概念

设计模式是怎么解决问题的一种方案

常见的设计模式

单例模式

概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
应用:项目封装个websocket用于大屏,redux,vuex都应用了单例模式的思想;现在很多第三方库都是单例模式,多次引用只会使用同一个对象,如jquerylodashmoment
实现:先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象

// 定义一个类
function Singleton(name) {this.name = name;this.instance = null;
}
// 原型扩展类的一个方法getName()
Singleton.prototype.getName = function() {console.log(this.name)
};
// 获取类的实例
Singleton.getInstance = function(name) {if(!this.instance) {this.instance = new Singleton(name);}return this.instance
};// 获取对象1
const a = Singleton.getInstance('a');
// 获取对象2
const b = Singleton.getInstance('b');
// 进行比较
console.log(a === b);//true
工厂模式

概念:不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中,那么这个函数就可以被视为一个工厂。
工厂模式根据抽象程度的不同可以分为:

  • 简单工厂模式(Simple Factory):用一个工厂对象创建同一类对象类的实例.

    function Factory(career) {function User(career, work) {this.career = career this.work = work}let workswitch(career) {case 'coder':work =  ['写代码', '修Bug'] return new User(career, work)breakcase 'boss':work = ['喝茶', '开会', '审批文件']return new User(career, work)break}
    }
    let coder = new Factory('coder')
    console.log(coder)
    let boss = new Factory('boss')
    console.log(boss)
    
  • 工厂方法模式(Factory Method):工厂方法模式跟简单工厂模式差不多,但是把具体的产品放到了工厂函数的prototype中.

    // 工厂方法
    function Factory(career){if(this instanceof Factory){var a = new this[career]();return a;}else{return new Factory(career);}
    }
    // 工厂方法函数的原型中设置所有对象的构造函数
    Factory.prototype={'coder': function(){this.careerName = '程序员'this.work = ['写代码', '修Bug'] },'hr': function(){this.careerName = 'HR'this.work = ['招聘', '员工信息管理']}
    }
    let coder = new Factory('coder')
    console.log(coder)
    let hr = new Factory('hr')
    console.log(hr)
    
  • 抽象工厂模式(Abstract Factory):简单工厂模式和工厂方法模式都是直接生成实例,但是抽象工厂模式不同,抽象工厂模式并不直接生成实例, 而是用于对产品类簇的创建。

    通俗点来讲就是:简单工厂和工厂方法模式的工作是生产产品,那么抽象工厂模式的工作就是生产工厂的

    let CareerAbstractFactory = function(subType, superType) {// 判断抽象工厂中是否有该抽象类if (typeof CareerAbstractFactory[superType] === 'function') {// 缓存类function F() {}// 继承父类属性和方法F.prototype = new CareerAbstractFactory[superType]()// 将子类的constructor指向父类subType.constructor = subType;// 子类原型继承父类subType.prototype = new F()} else {throw new Error('抽象类不存在')}
    }
    //由于JavaScript中并没有抽象类的概念,只能模拟,可以分成四部分:
    //用于创建抽象类的函数
    //抽象类
    //具体类
    //实例化具体类
    //上面代码中CareerAbstractFactory就是一个抽象工厂方法,该方法在参数中传递子类和父类,在方法体内部实现了子类对父类的继承
    

工厂模式适用场景如下:

  • 如果你不想让某个子系统与较大的那个对象之间形成强耦合,而是想运行时从许多子系统中进行挑选的话,那么工厂模式是一个理想的选择
  • 将new操作简单封装,遇到new的时候就应该考虑是否用工厂模式;
  • 需要依赖具体环境创建不同实例,这些实例都有相同的行为,这时候我们可以使用工厂模式,简化实现的过程,同时也可以减少每种对象所需的代码量,有利于消除对象间的耦合,提供更大的灵活性
策略模式

概念:定义一系列的算法,把它们一个个封装起来,目的就是将算法的使用与算法的实现分离开来。
一个基于策略模式的程序至少由两部分组成:
策略类(可变),策略类封装了具体的算法,并负责具体的计算过程
环境类(不变),接受客户的请求,随后将请求委托给某一个策略类

var obj = {"A": function(salary) {return salary * 4;},"B" : function(salary) {return salary * 3;},"C" : function(salary) {return salary * 2;} 
};
var calculateBouns =function(level,salary) {return obj[level](salary);
};
console.log(calculateBouns('A',10000)); // 40000

策略模式的优点有如下:

  • 策略模式利用组合,委托等技术和思想,有效的避免很多if条件语句
  • 策略模式提供了开放-封闭原则,使代码更容易理解和扩展
  • 策略模式中的代码可以复用
代理模式

概念:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

ES6中,存在proxy构建函数能够让我们轻松使用代理模式:

const proxy = new Proxy(target, handler);

而按照功能来划分,javascript代理模式常用的有:

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

    //缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果
    var proxyMult = (function () {var cache = {};return function () {var args = Array.prototype.join.call(arguments, ",");if (args in cache) {return cache[args];}return (cache[args] = mult.apply(this, arguments));};
    })();proxyMult(1, 2, 3, 4); // 输出:24
    proxyMult(1, 2, 3, 4); // 输出:24
    
  • 虚拟代理:虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建。

    常见的就是图片预加载功能:

    // 图片本地对象,负责往页面中创建一个img标签,并且提供一个对外的setSrc接口
    let myImage = (function(){let imgNode = document.createElement( 'img' );document.body.appendChild( imgNode );return {//setSrc接口,外界调用这个接口,便可以给该img标签设置src属性setSrc: function( src ){imgNode.src = src;}}
    })();
    // 代理对象,负责图片预加载功能
    let proxyImage = (function(){// 创建一个Image对象,用于加载需要设置的图片let img = new Image;img.onload = function(){// 监听到图片加载完成后,给被代理的图片本地对象设置src为加载完成后的图片myImage.setSrc( this.src );}return {setSrc: function( src ){// 设置图片时,在图片未被真正加载好时,以这张图作为loading,提示用户图片正在加载myImage.setSrc( 'https://img.zcool.cn/community/01deed576019060000018c1bd2352d.gif' );img.src = src;}}
    })();proxyImage.setSrc( 'https://xxx.jpg' );
    

    应用场景:

    现在的很多前端框架或者状态管理框架都使用代理模式,用与监听变量的变化。

    使用代理模式代理对象的访问的方式,一般又被称为拦截器,比如我们在项目中经常使用 Axios 的实例来进行 HTTP 的请求,使用拦截器 interceptor 可以提前对 请求前的数据 服务器返回的数据进行一些预处理。

    以及上述应用到的缓存代理和虚拟代理。

中介者模式

​ 通过一个中介者对象,其他所有的相关对象都通过该中介者对象来通信,当其中的一个对象发生改变时,只需要通知中介者对象即可。

​ 通过中介者模式可以解除对象与对象之间的紧耦合关系

装饰者模式

​ 在原有方法维持不变,在原有方法上再挂载其他方法来满足现有需求。

观察者模式

​ 观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。
​ 观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯。
在这里插入图片描述

// 被观察者模式
class Subject {constructor() {this.observerList = [];}addObserver(observer) {this.observerList.push(observer);}removeObserver(observer) {const index = this.observerList.findIndex(o => o.name === observer.name);this.observerList.splice(index, 1);}notifyObservers(message) {const observers = this.observeList;observers.forEach(observer => observer.notified(message));}
}//观察者
class Observer {constructor(name, subject) {this.name = name;if (subject) {subject.addObserver(this);}}notified(message) {console.log(this.name, 'got message', message);}
}
//使用
const subject = new Subject();
const observerA = new Observer('observerA', subject);
const observerB = new Observer('observerB');
subject.addObserver(observerB);
subject.notifyObservers('Hello from subject');
subject.removeObserver(observerA);
subject.notifyObservers('Hello again');
发布订阅模式

​ 发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。
在这里插入图片描述

// 发布订阅中心
class PubSub {constructor() {this.messages = {};this.listeners = {};}// 添加发布者publish(type, content) {const existContent = this.messages[type];if (!existContent) {this.messages[type] = [];}this.messages[type].push(content);}// 添加订阅者subscribe(type, cb) {const existListener = this.listeners[type];if (!existListener) {this.listeners[type] = [];}this.listeners[type].push(cb);}// 通知notify(type) {const messages = this.messages[type];const subscribers = this.listeners[type] || [];subscribers.forEach((cb, index) => cb(messages[index]));}
}//发布者代码
class Publisher {constructor(name, context) {this.name = name;this.context = context;}publish(type, content) {this.context.publish(type, content);}
}//订阅者代码
class Subscriber {constructor(name, context) {this.name = name;this.context = context;}subscribe(type, cb) {this.context.subscribe(type, cb);}
}//使用代码
const TYPE_A = 'music';
const TYPE_B = 'movie';
const TYPE_C = 'novel';const pubsub = new PubSub();const publisherA = new Publisher('publisherA', pubsub);
publisherA.publish(TYPE_A, 'we are young');
publisherA.publish(TYPE_B, 'the silicon valley');
const publisherB = new Publisher('publisherB', pubsub);
publisherB.publish(TYPE_A, 'stronger');
const publisherC = new Publisher('publisherC', pubsub);
publisherC.publish(TYPE_C, 'a brief history of time');const subscriberA = new Subscriber('subscriberA', pubsub);
subscriberA.subscribe(TYPE_A, res => {console.log('subscriberA received', res)
});
const subscriberB = new Subscriber('subscriberB', pubsub);
subscriberB.subscribe(TYPE_C, res => {console.log('subscriberB received', res)
});
const subscriberC = new Subscriber('subscriberC', pubsub);
subscriberC.subscribe(TYPE_B, res => {console.log('subscriberC received', res)
});pubsub.notify(TYPE_A);
pubsub.notify(TYPE_B);
pubsub.notify(TYPE_C);//发布者和订阅者需要通过发布订阅中心进行关联,发布者的发布动作和订阅者的订阅动作相互独立,无需关注对方,消息派发由发布订阅中心负责。
发布订阅、观察者模式区别
  • 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
  • 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
  • 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通过MATLAB自动产生Hamming编译码的verilog实现,包含testbench

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 1. 原理 1.1 编码规则 1.2 错误检测和纠正 2. 实现过程 2.1 编码过程 2.2 解码过程 3. 应用领域 3.1 数字通信 3.2 存储系统 3.3 ECC内存 3.4 数据传输 5.算法完整程序工程 1.算法…

robotframework+selenium 进行webui页面自动化测试

robotframework其实就是一个自动化的框架,想要进行什么样的自动化测试,就需要在这框架上添加相应的库文件,而用于webui页面自动化测试的就是selenium库. 关于robotframework框架的搭建我这里就不说了,今天就给大家根据一个登录的实…

wsl2安装mysql环境

安装完mysql后通过如下命令启动mysql service mysql start 会显示如下错误: mysql: unrecognized service 实际上上面显示的错误是由于mysql没有启动成功造成的 我们要想办法成功启动mysql才可以 1.通过如下操作就可以跳过密码直接进入mysql环境 2.如果想找到my…

边写代码边学习之LSTM

1. 什么是LSTM 长短期记忆网络 LSTM(long short-term memory)是 RNN 的一种变体,其核心概念在于细胞状态以及“门”结构。细胞状态相当于信息传输的路径,让信息能在序列连中传递下去。你可以将其看作网络的“记忆”。理论上讲&a…

LeetCode_03Java_1572. 矩阵对角线元素的和

给你一个正方形矩阵 mat,请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 输入:mat [[1,2,3],[4,5,6],[7,8,9]] 输出:25 解释:对角线的和为:1 5 9 3 7 2…

kube-prometheus 使用 blackbox-exporter 进行icmp 监控

安装kube-prometheus 后默认在monitoring namespace中有创建 blackbox-exporter deployment。但默认没有icmp的module配置,无法执行ping探测。因为即使有icmp module,默认配置也是无法执行ping探测的(这篇文章要解决的就是这个问题&#xff0…

mybatis-plus逻辑删除的坑

一旦在逻辑字段上加了TableLogic逻辑删除的配置,并且使用mybatis-plus自带的方法时(如果自己用xml写SQL不会出现下面的情况) 查询、修改时会自动排除逻辑删除的数据 当使用mybatis-plus自带的查询方法时,就不用每次查询的时候跟…

Elasticsearch同时使用should和must

问题及解决方法 must和should组合查询,should失效。使用must嵌套查询,将should组成的bool查询包含在其中一个must查询中。 SearchRequest request new SearchRequest(); request.indices("function_log");SearchSourceBuilder sourceBuilde…

数字化时代,选择商业智能BI解决80%数据问题

数据是需要有人来照料、培养的,如果企业没有完善的数据治理方案,就很难保障数据的质量,进而导致数据无法利用,让这些辛苦积累的数据失去了价值。 数据治理目标 数据从业务活动中产生,也会深刻影响到业务本身。 对于…

GrapeCity Documents for Excel, .NET Crack

GrapeCity Documents for Excel, .NET 增加了对双面打印的支持。 GcExcel.NET支持PrintOutOptions类中的Duplex枚举,以启用/禁用页面上的双面打印。 枚举中有四个选项,用户可以相应地使用它们来打印工作簿: 双面打印。Default表示打印机的默认…

Docker高级篇_DockerFile

目录 DockerFile简介构建DockerFile构建过程解析Docker执行Dockerfile的大致流程 DockerFile常用保留字指令FROMMAINTAINERRUNEXPOSEWORKDIRUSERENVVOLUMEADDCOPYCMDENTRYPOINT案例使用虚悬镜像 Docker微服务 DockerFile简介 Dockerfile是用来构建Docker镜像的文本文件&#x…

基于STM32微控制器的物联网(IoT)节点设计与实现

基于STM32微控制器的物联网(IoT)节点的设计和实现。我们讨论物联网节点的基本概念和功能,并详细介绍了STM32微控制器的特点和优势。然后,我们将探讨如何使用STM32开发环境和相关的硬件模块来设计和实现一个完整的物联网节点。最后,我们将提供一个示例代码,展示如何在STM3…

【CheatSheet】Python、R、Julia数据科学编程极简入门

《Python、R、Julia数据科学编程极简入门》PDF版,是我和小伙伴一起整理的备忘清单,帮助大家10分钟快速入门数据科学编程。 另外,最近 TIOBE 公布了 2023 年 8 月的编程语言排行榜。 Julia 在本月榜单中实现历史性突破,成功跻身 …

(一)创建型设计模式:3、建造者模式(Builder Pattern)

目录 1、建造者模式含义 2、建造者模式的讲解 3、使用C实现建造者模式的实例 4、建造者模式的优缺点 5、建造者模式VS工厂模式 1、建造者模式含义 The intent of the Builder design pattern is to separate the construction of a complex object from its representatio…

opencv基础45-图像金字塔01-高斯金字塔cv2.pyrDown()

什么是图像金字塔? 图像金字塔(Image> Pyramid)是一种用于多尺度图像处理和分析的技术,它通过构建一系列不同分辨率的图像,从而使得图像可以在不同尺度下进行处理和分析。图像金字塔在计算机视觉、图像处理和计算机…

Vue实现详细界面里面有一个列表

目录 Vue实现详细界面里面有一个列表 理一下思路: 效果如下: 1、 主页面正常写 2、详细界面(重点) 3、详细界面里面的列表(重点) 要点: Vue实现详细界面里面有一个列表 理一下思路: 1、首先需要这条数据的主键id&#xff…

Android 13 Hotseat定制化修改——001 hotseat布局方向

目录 一.背景 二.hotseat布局方向 一.背景 由于需求是需要自定义修改Hotseat,所以此篇文章是记录如何自定义修改hotseat的,应该可以覆盖大部分场景,修改点有修改hotseat布局方向,hotseat图标数量,hotseat图标大小,hotseat布局位置,hotseat图标禁止形成文件夹,hotseat图…

纯前端 -- html转pdf插件总结

一、html2canvasjsPDF(文字会被截断): 将HTML元素呈现给添加到PDF中的画布对象,不能仅使用jsPDF,需要html2canvas或rasterizeHTML html2canvasjsPDF的具体使用链接 二、html2pdf(内容显示不全文字会被截断…

前端接口修改工具 Requestly具体操作

更新于2023年8月12日18:17:56,插件版本可能会变,界面可能会有所变化 插件下载地址:https://chrome.google.com/webstore/detail/requestly-open-source-htt/mdnleldcmiljblolnjhpnblkcekpdkpa 注意,必须用谷歌浏览器,…

VSCode中如何修改代码字体

通过「File」→「Preferences」→「Settings」→「Text Editor」→「Font」→「Font Family」中,修改对应的字体即可。因为比较喜欢 JetBrains Mono,所以设置的字体是这个。 其中Jetbrains Mono字体需要自己在Jetbrains官网下载,然后中文字体…