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

Javascript 设计模式

设计模式的五大设计原则(SOLID)

  • 单一职责:一个程序只需要做好一件事。如果功能过于复杂就拆分开,保证每个部分的独立

  • 开放封闭原则:对扩展开放,对修改封闭。增加需求时,扩展新代码,而不是修改源代码。这是软件设计的终极目标。

  • 里氏置换原则:子类能覆盖父类,父类能出现的地方子类也能出现。

  • 接口独立原则:保持接口的单一独立,避免出现“胖接口”。这点目前在TS中运用到。

  • 依赖导致原则:面向接口编程,依赖于抽象而不依赖于具体。使用方只专注接口而不用关注具体类的实现。俗称“鸭子类型”

工厂模式

工厂模式是用来创建对象的常见设计模式,在不暴露创建对象的具体逻辑,而是将逻辑进行封装,那么它就可以被称为工厂。工厂模式又叫做静态工厂模式,由一个工厂对象决定创建某一个类的实例。

class Plant {constructor(name) {this.name = name;}grow() {console.log("grow up");}
}class Apple extends Plant {constructor(name, flavour) {super(name)this.flavour = flavour;}
}class Orange extends Plant {constructor(name, flavour) {super(name)this.flavour = flavour;}
}class Factory {static create(type) {switch (type) {case 'apple':return new Apple('xiao ping','sweet')case 'orange':return new Orange('xiao ju','sour')default:throw new Error("Unknown plant type")}}
}let apple = Factory.create('apple');
console.log(apple.flavour)
let orange = Factory.create('orange');
console.log(orange.flavour)

小结:

优点

  • 调用者创建对象时只要知道其名称即可

  • 扩展性高,如果要新增一个产品,直接扩展一个工厂类即可。

  • 隐藏产品的具体实现,只关心产品的接口。

缺点

  • 每次增加一个产品时,都需要增加一个具体类,这无形增加了系统内存的压力和系统的复杂度,也增加了具体类的依赖

单例模式

单例模式的思路是:保证一个类只能被实例一次,每次获取的时候,如果该类已经创建过实例则直接返回该实例,否则创建一个实例保存并返回。

单例模式很常用,比如:

vue项目中的Vue实例

node项目中的App实例

vuex react-redux 中的store

全局唯一的组件,像弹出框,模态窗口,购物车,登录框等

class LoginFrame {static instance = nullconstructor(state){this.state = state}show(){if(this.state === 'show'){console.log('登录框已显示')return}this.state = 'show'console.log('登录框展示成功')}hide(){if(this.state === 'hide'){console.log('登录框已隐藏')return}this.state = 'hide'console.log('登录框隐藏成功')}// 通过静态方法获取静态属性instance上是否存在实例,如果没有创建一个并返回,反之直接返回已有的实例static getInstance(state){if(!this.instance){this.instance = new LoginFrame(state)}return this.instance}
}
const p1 = LoginFrame.getInstance('show')
const p2 = LoginFrame.getInstance('hide')
console.log(p1 === p2) // true

小结:

优点

  • 内存中只有一个实例,减少了内存的开销。

  • 避免了对资源多重的占用。

缺点

  • 违反了单一职责,一个类应该只关心内部逻辑,而不用去关心外部的实现

原型模式

function Person(name){this.name = name;
}Person.prototype.getName = function(){return this.name;
}let p1 = new Person('san')
let p2 = new Person('si')
console.log(p1.getName === p2.getName)

更多关于prototype的知识请去 JS原型与原型链详解

适配器模式

适配器模式的作用是解决两个软件实体间的接口不兼容的问题。使用适配器模式之后,原本由于接口由于接口不兼容而不能工作的两个软件实体可以一起工作。

class Power {charge(){return '220v'}
}class Adapter{constructor(){this.power = new Power()}charge(){let v= this.power.charge();return `${v} => 12v`}
}class Client{constructor(){this.adapter = new Adapter()}use(){console.log(this.adapter.charge())}
}let client = new Client()
client.use()

小结:

优点

  • 让任何两个没有关联的类可以同时有效运行,并且提高了复用性、透明度、以及灵活性

缺点

  • 过多的使用适配器模式,会让系统变得零乱,不易整体把控。建议在无法重构的情况下使用适配器。

代理模式

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”

let lily = {name: 'lily',age: '30',height: '160'
}// new Proxy(target, handler); 
// new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。let lilyMa = new Proxy(lily, {//get方法的两个参数分别是目标对象和所要访问的属性。get(target, key) {if (key === 'age') {return target.age - 2} else if (key === 'height') {return target.height + 5} else {return target[key]}},set(target,key,value) {if(key ==='boyfriend'){let boyfriend = value;if(boyfriend.age >40){throw new Error('too old!')} else if(boyfriend.salary <20000){throw new Error('too poor!')} else{target[key] = value}}}
}) console.log(lilyMa.age)
lilyMa.boyfriend = {age:35,salary:25000,height:180} 

小结:

代理模式符合开放封闭原则

本体对象和代理对象拥有相同的方法,在用户看来并不知道请求的本体对象还是代理对象。

代理模式 vs 适配器模式 适配器提供不同的接口,代理模式提供一摸一样的接口

代理模式 vs 装饰器模式 装饰器模式原来的功能不变还可以使用,代理模式改变原来的功能

优点

  • 职责清晰,高扩展性,智能化

缺点

  • 当对象和对象之间增加了代理可能会影响到处理的速度。

  • 实现代理需要额外的工作,有些代理会非常的复杂。

观察者模式(订阅-发布模式)

观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,

所有依赖于它的对象将得到通知。

如:当你给DOM绑定一个事件就已经使用了发布订阅模式,通过订阅DOM上的click事件,当被点击时会向订阅者发布消息。

class Star{constructor(name){this.name = name;this.state = '';this.observers = [];}getState(){return this.state;}setState(state){this.state = state;this.notifyAllObservers()}attach(observer){this.observers.push(observer)}notifyAllObservers(){if(this.observers.length>0){this.observers.forEach(observer=>observer.update())}}
}class Fun{constructor(name,star){this.name = name;this.star = star;this.star.attach(this)}update(){console.log(`${this.star.name} know ${this.star.getState()}`)}
}let s = new Star('liushishi')
let f = new Fun('me',s)
s.setState('xinai')

小结:

发布订阅模式可以使代码解耦,满足开放封闭原则

当过多的使用发布订阅模式,如果订阅消息始终都没有触发,则订阅者一直保存在内存中。

优点

  • 观察者和被观察者它们之间是抽象耦合的。并且建立了触发机制。

缺点

  • 当订阅者比较多的时候,同时通知所有的订阅者可能会造成性能问题。

  • 在订阅者和订阅目标之间如果循环引用执行,会导致崩溃。

  • 发布订阅模式没有办法提供给订阅者所订阅的目标它是怎么变化的,仅仅只知道它变化了。

装饰器模式

装饰者模式能够在不更改源代码自身的情况下,对其进行职责添加。相比于继承装饰器的做法更轻巧。通俗的讲我们给心爱的手机上贴膜,带手机壳,贴纸,这些就是对手机的装饰。

class Duck {constructor(name) {this.name = name;}eat(food) {console.log(`eat ${food}`)}
}class TangDuck {constructor(name) {this.duck = new Duck(name);}eat(food) {this.duck.eat(food);console.log('thanks')}
}let d = new TangDuck();
d.eat('apple');

还有ES6的Decorator

优点

  • 装饰类和被装饰类它们之间可以相互独立发展,不会相互耦合,装饰器模式是继承的一个替代模式,它可以动态的扩展一个实现类的功能。

缺点

  • 多层的装饰会增加复杂度

外观模式

外观模式本质就是封装交互,隐藏系统的复杂性,提供一个可以访问的接口。由一个将子系统一组的接口集成在一起的高层接口,以提供一个一致的外观,减少外界与多个子系统之间的直接交互,从而更方便的使用子系统。

class Sum{sum(a,b){return a+b}
}class Minus{minus(a,b){return a-b}
}
class Mutify{mutify(a,b){return a*b}
}
class Divide{divide(a,b){return a/b}
}class Calculate{constructor(a,b){this.sumc = new Sum(a,b);this.minusc = new Minus(a,b);this.mutifyc = new Mutify(a,b);this.dividec = new Divide(a,b);}sum(a,b){return this.sumc.sum(a,b)}minus(a,b){return this.minusc.minus(a,b)}mutify(a,b){return this.mutifyc.mutify(a,b)}divide(a,b){return this.dividec.divide(a,b)}
}let calculate = new Calculate()
console.log(calculate.sum(1,2))

优点

  • 减少系统的相互依赖,以及安全性和灵活性

缺点

  • 违反开放封闭原则,有变动的时候更改会非常麻烦,即使继承重构都不可行。

状态模式

允许一个对象在其内部状态改变的时候改变其行为,对象看起来似乎修改了它的类,通俗一点的将就是记录一组状态,每个状态对应一个实现,实现的时候根据状态去运行实现。

class SuccessState {show(){console.log('green')}
}class WarningState {show(){console.log('yellow')}
}class ErrorState {show(){console.log('red')}
}class Battery {constructor(){this.amout = 'high'this.state = new SuccessState()}show(){this.state.show();if(this.amout == 'high'){this.amout= 'middle';this.state = new WarningState()} else if(this.amout == 'middle'){this.amout= 'low';this.state = new ErrorState()}}
}let b = new Battery()
b.show()
b.show()
b.show()

优点

  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。

  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。

  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点

  • 状态模式的使用必然会增加系统类和对象的个数。

  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。

  • 状态模式对"开闭原则"的支持并不太好,对切换状态的状态模式增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

策略模式

策略模式指的是定义一系列算法,把他们一个个封装起来,目的就是将算法的使用和算法的实现分离开来。同时它还可以用来封装一系列的规则,比如常见的表单验证规则,只要这些规则指向的目标一致,并且可以被替换使用,那么就可以用策略模式来封装它们。

class Customer{constructor(kind){this.kind = kind;}pay(amount){return this.kind.pay(amount)}
}class Normal{pay(amount){return amount}
}class Member{pay(amount){return amount*.9}
}class VIP{pay(amount){return amount*.8}
}let c1 = new Customer(new Normal()).pay(100);
console.log(c1)
let c2 = new Customer(new Member()).pay(100)
console.log(c2)
let c3 = new Customer(new VIP()).pay(100)
console.log(c3)

优点

  • 算法可以自由切换,避免了使用多层条件判断,增加了扩展性

缺点

  • 策略类增多,所有策略类都需要对外暴露。

相关文章:

Javascript 设计模式

设计模式的五大设计原则(SOLID)单一职责&#xff1a;一个程序只需要做好一件事。如果功能过于复杂就拆分开&#xff0c;保证每个部分的独立开放封闭原则:对扩展开放&#xff0c;对修改封闭。增加需求时&#xff0c;扩展新代码&#xff0c;而不是修改源代码。这是软件设计的终极…...

JAVA-文档工具screw-gui

前言 为什么萌生了写文档工具得想法&#xff0c;因为在项目开发得过程中&#xff0c;经常需要补充一些文档&#xff0c;比如数据库文档、详细设计文档等等&#xff0c;文档与项目相绑定&#xff0c;在项目需求新增或变更时&#xff0c;文档也需要反反复复得修改。 1. 数据库…...

开源鸿蒙南向嵌入学习笔记——NAPI框架学习(一)

开源鸿蒙南向嵌入学习笔记——NAPI框架学习&#xff08;一&#xff09; 前言——系列介绍 本系列文章主要是记录笔者在鸿蒙南向的学习与工作中的知识点笔记记录&#xff0c;其中不止会针对鸿蒙中的学习问题进行思考与记录&#xff0c;也会对涉及到的一些嵌入式等其他领域知识&…...

Spring - Spring框架概述面试题总结

文章目录01. 什么是Spring&#xff1f;02. Spring框架的设计目标&#xff0c;设计理念&#xff0c;和核心是什么&#xff1f;03. Spring的优点是什么&#xff1f;04. Spring框架中都用到了哪些设计模式&#xff1f;05. Spring有哪些应用场景?06. Spring由哪些模块组成&#xf…...

学习python好就业么

Python的普及与数据挖掘、人工智能和数值计算等领域的蓬勃发展相关&#xff0c;但同时也与普遍编程需求的增加有关。 Python作为人工智能的头号语言&#xff0c;一方面会吸引大量计划从事人工智能的人来学习&#xff0c;另一方面自然也带动了网络上对这门“新语言”的关注和讨…...

瑞幸咖啡的最终目标并不是做国内市场大哥

出品 | 何玺 排版 | 叶媛 日前&#xff0c;瑞幸咖啡发布2022年第四季度及全年财报。数据显示&#xff0c;在刚刚过去的2022年&#xff0c;瑞幸咖啡首次实现了营收超百亿&#xff0c;门店规模也超越老对手星巴克&#xff0c;成为了国内第一连锁咖啡品牌。 那么&#xff0c;瑞幸…...

GPT 模型介绍 | GPT3 / GPT3.5 + Flask | Github源码链接

1. 模型介绍 Chatgpt 使用与 InstructGPT相同的方法&#xff0c;使用来自人类反馈的强化学习 (RLHF) 来训练该模型&#xff0c;但数据收集设置略有不同。我们使用监督微调训练了一个初始模型&#xff1a;人类 AI 训练员提供对话&#xff0c;他们在对话中扮演双方——用户和 AI…...

蓝桥杯入门即劝退(二十六)组合问题(回溯算法)

-----持续更新Spring入门系列文章----- 如果你也喜欢Java和算法&#xff0c;欢迎订阅专栏共同学习交流&#xff01; 你的点赞、关注、评论、是我创作的动力&#xff01; -------希望我的文章对你有所帮助-------- 专栏&#xff1a;蓝桥杯系列 一、题目描述 给定两个整数 n …...

现代卷积神经网络(ResNet)

专栏&#xff1a;神经网络复现目录 本章介绍的是现代神经网络的结构和复现&#xff0c;包括深度卷积神经网络&#xff08;AlexNet&#xff09;&#xff0c;VGG&#xff0c;NiN&#xff0c;GoogleNet&#xff0c;残差网络&#xff08;ResNet&#xff09;&#xff0c;稠密连接网络…...

PTA:L1-019 谁先倒、L1-020 帅到没朋友、L1-021 重要的话说三遍(C++)

目录 L1-019 谁先倒 问题描述&#xff1a; L1-020 帅到没朋友 问题描述&#xff1a; 实现代码&#xff08;只过了部分&#xff09;&#xff1a; L1-021 重要的话说三遍 问题描述&#xff1a; 实现代码&#xff1a; 无解析 L1-019 谁先倒 问题描述&#xff1a; 划拳是…...

STL常见容器之set/multiset、map/multimap

set/multiset—集合容器 特点 所有元素都会在插入时自动被排序 本质 set/multiset属于关联式容器&#xff0c;底层结构是二叉树实现 set和multiset区别 set不可以插入重复数据&#xff0c;而multiset可以set插入数据的同时会返回插入结果&#xff0c;表示插入是否成功multiset…...

ThreadLocal 实现原理

每个 Thread 中都存储着一个成员变量&#xff1a;ThreadLocalMap /** InheritableThreadLocal values pertaining to this thread. This map is* maintained by the InheritableThreadLocal class.*/ThreadLocal.ThreadLocalMap inheritableThreadLocals null; ThreadLocal 本…...

BUUCTF [羊城杯 2020]easyre 题解

一.查壳 64位无壳 二.主函数逻辑 可以得知flag长度为38,然后进行三次加密 第一次加密是base64加密,得到code1 第二次加密是将code1拆成四段赋给code2 第三次加密是将code2内的数字和字母移3位,其他字符不变 str2保存的是最终的加密字符 三.encode_one_base64 看到主函数…...

网络协议(十二):HTTPS(SSL/TLS、TLS1.2的连接)

网络协议系列文章 网络协议(一)&#xff1a;基本概念、计算机之间的连接方式 网络协议(二)&#xff1a;MAC地址、IP地址、子网掩码、子网和超网 网络协议(三)&#xff1a;路由器原理及数据包传输过程 网络协议(四)&#xff1a;网络分类、ISP、上网方式、公网私网、NAT 网络…...

九九乘法表--课后程序(Python程序开发案例教程-黑马程序员编著-第3章-课后作业)

实例9&#xff1a;九九乘法表 乘法口诀是中国古代筹算中进行乘法、除法、开方等运算的基本计算规则&#xff0c;沿用至今已有两千多年。古代的乘法口诀与现在使用的乘法口诀顺序相反&#xff0c;自上而下从“九九八十一”开始到“一一如一”为止&#xff0c;因此&#xff0c;古…...

在超算上安装文件树命令tree

超算平台使用的centos系统没有内置tree命令&#xff0c;需要通过源码安装。记录安装流程如下。 1. 下载源码包 下载链接如下&#xff1a; http://mama.indstate.edu/users/ice/tree/ 选择“Download the latest version” 如本文下载了源码包“tree-2.1.0.tgz”. 2. 源码包…...

论文投稿指南——中文核心期刊推荐(经济管理)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…...

在vue中如果computed属性是一个异步操作怎么办?

在计算属性中使用异步方法时&#xff0c;可以使用async/await来处理异步操作。由于计算属性是基于它们的依赖缓存的&#xff0c;所以我们需要使用一个返回Promise的异步方法来确保计算属性能够正常运行。 下面是一个简单的示例&#xff0c;演示如何在计算属性中使用异步方法&am…...

SRP合批问题

1&#xff09;SRP合批问题 ​2&#xff09;多个Base相机渲染到同一个渲染目标&#xff0c;移动平台花屏的问题 3&#xff09;粒子系统对GPU Instancing的支持 4&#xff09;如何修改URP下场景和UI分辨率分离&#xff08;不需要改颜色空间&#xff09; 这是第327篇UWA技术知识分…...

蓝牙5.1低功耗SOC 私有协议2.4GHz芯片HS6621

HS6621CxC是一个优化功耗真正芯片系统&#xff08;SOC&#xff09;解决方案&#xff0c;适用于蓝牙低功耗和私有的2.4GHz应用场景。它集成了一个高性能、小功率的射频收发器&#xff0c;具有蓝牙基带和丰富的外围IO扩展。还集成了电源管理&#xff0c;以提供高效的电源管理。 …...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…...