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

常见的设计模式

什么是设计模式?

设计模式是对软件设计开发过程中反复出现的某类问题的通用解决方案。设计模式更多的是指导思想和方法论,而不是现成的代码,当然每种设计模式都有每种语言中的具体实现方式。学习设计模式更多的是理解各种模式的内在思想和解决的问题,毕竟这是前人无数经验总结成的最佳实践,而代码实现则是对加深理解的辅助。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。在本文中,我将介绍其中常见的五种设计模式在JavaScript中实际使用场景:

单例设计模式

单例模式仅允许类或对象具有单个实例,并且它使用全局变量来存储该实例。

  • 实现方法是判断是否存在该对象的实例,如果已存在则不再创建
  • 使用场景适用于业务场景中只能存在一个的实例,比如弹窗,购物车
var ShopCar = (function () {var instance = init();function init() {/*这里定义单例代码*/ return {buy(good) {this.goods.push(good);},goods: [],};}return {getInstance: function () {return instance;},};
})();
let car1 = ShopCar.getInstance();
let car2 = ShopCar.getInstance();
car1.buy('橘子');
car2.buy('苹果'); //[ '橘子', '苹果' ]
console.log(car1.goods);
console.log(car1 === car2); // true

策略模式

策略模式定义一系列的算法,将每一个算法封装起来,并让他们可以相互替换。

  • 实现方法定义一组可变的策略类封装具体算法,定义一组不变的环境类将请求委托给某一个策略类
  • 使用场景适用于业务场景中需要判断多种条件,甚至包含复杂条件嵌套的,可以使用策略模式来提升代码的可维护性和可读性。比如支付,博客权限校验
// 定义几个策略类
var PaymentMethodStrategy = {BankAccount: function (money) {return money > 50 ? money * 0.7 : money;},CreditCard: function (money) {return money * 0.8;},Alipay: function (money) {return money;},
};
/*环境类*/
var userPay = function (selectedStrategy, money) {return PaymentMethodStrategy[selectedStrategy](money);
};
console.log('银行卡支付价格为:' + userPay('BankAccount', 100)); //70
console.log('支付宝支付价格为:' + userPay('Alipay', 100)); //100
console.log('信用卡支付价格为:' + userPay('CreditCard', 100)); //80

观察者模式

观察者模式是对象的行为模式,在对象之间定义了一对多的依赖关系,就是多个观察者和一个被观察者之间的关系,当被观察者发生变化的时候,会通知所有的观察者对象,他们做出相对应的操作。

  • 实现方法定义一组可变的策略类封装具体算法,定义一组不变的环境类将请求委托给某一个策略类
  • 使用场景适用于业务场景中当一个对象的状态发生变化时,需要自动通知其他关联对象,自动刷新对象状态,或者说执行对应对象的方法,比如你是一个老师,需要通知班里家长的时候,你可以建一个群(列表)。每次通知事件的时候只要循环执行这个列表就好了(群发),而不用关心这个列表里有谁。
两种模式的异同

从概念上理解,两者没什么不同,都在解决对象之间解耦,通过事件的方式在某个时间点进行触发,监听这个事件的订阅者可以进行相应的操作。

在实现上有所不同,观察者模式对订阅事件的订阅者通过发布者自身来维护,后续的一些列操作都要通过发布者完成;订阅发布模式是订阅者和发布者中间会有一个事件总线,操作都要经过事件总线完成。

观察者模式的事件名称,通常由发布者指定发布的事件,当然也可以自定义,这样看是否提供自定义的功能。

观察者模式在很多场景中都在使用,除了上述中在 DOM 上监听事件外,还有最常用的是 Vue 组件中父子之间的通信。

<template><div><h2>父级</h2><Child @custom="customHandler"></Child></div>
</template>
<script>export default {methods: {customHandler(data){console.log('拿到数据,我要干点事', data);}}}
</script>
<template><div><h2>子级</h2><button @click="clickHandler">改变了</button></div>
</template>
<script>export default {methods: {clickHandler(){this.$emit('custome', 123)}}}
</script>

发布订阅模式

发布订阅模式指的是希望接收通知的对象(Subscriber)基于一个主题通过自定义事件订阅主题,发布事件的对象(Publisher)通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象。

订阅发布模式在用 Vue 写业务也会使用到,应用场景是在跨多层组件通信时,如果利用父子组件通信一层层订阅发布,可维护性和灵活性很差,一旦中间某个环节出问题,整个传播链路就会瘫痪。这时采用独立出来的 EventBus 解决这类问题,只要能访问到 EventBus 对象,便可通过该对象订阅和发布事件。

// EventBus.js
import Vue from 'vue'
export default const EventBus = new Vue()
<template><div><h2>父级</h2><Child></Child></div>
</template>
<script>import EventBus from './EventBus'export default {// 加载完就要监控moutend(){EventBus.on('custom', (data) => {console.log('拿到数据', data);})}}
</script>
<template><div><h2>嵌套很深的子级</h2><button @click="clickHandler">改变了</button></div>
</template>
<script>import EventBus from './EventBus'export default {methods: {clickHandler(){EventBus.emit('custom', 123)}}}
</script>

装饰器模式

在不改变原来的结构和功能基础上,动态装饰一些针对特别场景所适用的方法或属性,即添加一些新功能以增强它的某种能力

  • 实现方法定义一组可变的策略类封装具体算法,定义一组不变的环境类将请求委托给某一个策略类
  • 使用场景原有方法维持不变,在原有方法上再挂载其他方法来满足现有需求;函数的解耦,将函数拆分成多个可复用的函数,再将拆分出来的函数挂载到某个函数上,实现相同的效果但增强了复用性。比如多孔插座,机车改装
const Man = function () {this.run = function () {console.log('跑步');};
};
const Decorator = function (old) {this.oldAbility = old.run;this.fly = function () {console.log('具备飞行能力');};this.newAbility = function () {this.oldAbility();this.fly();};
};
const man = new Man();
const superMan = new Decorator(man);
superMan.fly(); // 具备飞行能力

代理模式

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

  • 实现方法定义一个委托者和一个代理,需要委托的事情在代理中完成
  • 使用场景在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用。代理可以帮客户过滤掉一些请求并且把一些开销大的对象,延迟到真正需要它时才创建。中介购车、代购、课代表替老师收作业

Proxy 是 ES6 提供的专门以代理角色出现的代理器,Vue 3.0 的响应式数据部分弃用了 Object.defineProperty,使用 Proxy 来代替它。

var proxy = new Proxy(target, handler);
现在用Proxy模拟一下另一种场景:为了保护不及格的同学,课代表拿到全班成绩单后只会公示及格人的成绩。对考分有疑问的考生,复议后新分数比以前大10分才有权利去更新成绩const scoreList = { wang: 90, li: 60, wu: 100 };
const yyProxy = new Proxy(scoreList, {get: function (scoreList, name) {if (scoreList[name] > 69) {console.log('输出成绩');return scoreList[name];} else {console.log('不及格的成绩无法公示');}},set: function (scoreList, name, val) {if (val - scoreList[name] > 10) {console.log('修改成绩');scoreList[name] = val;} else {console.log('无法修改成绩');}},
});
yyProxy['wang'] = 98; //无法修改成绩
yyProxy['li'] = 80; //修改成绩
http://www.xdnf.cn/news/18728.html

相关文章:

  • 博士招生 | 南洋理工大学 PINE Lab 招收全奖博士
  • [新启航]新启航激光频率梳 “光量子透视”:2μm 精度破除遮挡,完成 130mm 深孔 3D 建模
  • 【国密证书】CentOS 7 安装 GmSSL 并生成国密证书
  • Docker移动安装目录的两种实现方案
  • 微硕WINSOK高性能MOS管WSF90N10,助力洗衣机能效与可靠性升级
  • Java:IO流——基础篇
  • Redis高级篇:在Nginx、Redis、Tomcat(JVM)各环节添加缓存以实现多级缓存
  • 一文丝滑使用Markdown:从写作、绘图到转换为Word与PPT
  • MongoDB /redis/mysql 界面化的数据查看页面App
  • M3-Agent:让AI拥有长期记忆的新尝试
  • UML 时序图中交互片段操作符的详细解析与 C/C++ 实现示例
  • React 高阶组件
  • 服务器初始化
  • APM 系列(一):Skywalking 与 Easyearch 集成
  • 如何在项目中集成XXL-JOB
  • 在线提取维基百科Wikipedia文章页面及离线批处理Wikipedia XML Dump文件
  • 通信中间件 Fast DDS(二) :详细介绍
  • 安卓Android低功耗蓝牙BLE连接异常报错133
  • 计算机实习经历包装/编写
  • 嵌入式系统学习Day22(进程)
  • STL——vector的使用(快速入门详细)
  • Ansible自动化运维介绍与安装
  • 信贷模型域——清收阶段模型(贷后模型)
  • 简述mysql中索引类型有哪些,以及对数据库的性能的影响?
  • QML中的QtObject
  • C# NX二次开发:绘图区控件和指定矢量控件详解
  • vscode--快捷键
  • 【Android 16】Android W 的冻结机制框架层分析
  • QT新建文件或者项目解释:那些模板分别是什么意思?
  • 音视频学习(五十七):RTSP信令交互流程