💻 【JavaScript】设计模式(单例、策略、发布-订阅) 🏠专栏:JavaScript
👀个人主页:繁星学编程🍁
🧑个人简介:一个不断提高自我的平凡人🚀
🔊分享方向:目前主攻前端,其他知识也会阶段性分享🍀
👊格言:☀️没有走不通的路,只有不敢走的人!☀️
👉让我们一起进步,一起成为更好的自己!!!🎁
单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式核心:实例化一个类的时候
先判断,之前有没有实例化过
单例模式优点:创建对象和管理单例的职责被分布在两个不同的方法中
单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的 window 对象等。
在 JavaScript 开发中,单例模式的用途同样非常广泛。试想一下,当我们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。
/*分析:
找到一个变量,初始化的时候是null
第一次实例化后,给这个变量赋值
第二次想要实例化的时候,先去看看这个变量有没有值,如果有,就用之前的
// 一个弹窗的构造函数
class Dialog {constructor(title) {this.title = titlethis.div = document.createElement('div')this.div.style.backgroundColor = 'pink'document.body.appendChild(this.div)}changeTile(title) {this.title = titlethis.div.innerHTML = this.title}
}
// 单例模式代码
// 为了避免instance这个变量污染 改成闭包
const singleton = (function () {// 提前设置一个变量,用来记录该构造函数有没有实例化过let instance = nullreturn function (title) {// 判断构造函数有没有实例化过 // 没有实例化过if (!instance) {instance = new Dialog(title)}// 如果有,就返回这个实例化对象return instance}
})()
// 创建弹窗对象
const res = singleton('hello')
res.changeTile('小花')// 再次创建弹窗对象
const res1 = singleton('你好')
res1.changeTile('你好')
console.log(res1); // Dialog {title: '你好', div: div}
策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
策略模式的目的是:将算法的使用与算法的实现分离开来。
一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,策略类封装了具体 的算法,并负责具体的计算过程。 第二个部分是环境类Context,Context 接受客户的请求,随后 把请求委托给某一个策略类。要做到这点,说明 Context中要维持对某个策略对象的引用。
从定义上看,策略模式就是用来封装算法的。但如果把策略模式仅仅用来封装算法,未免有一点大材小用。在实际开发中,我们通常会把算法的含义扩散开来,使策略模式也可以用来封装 一系列的“业务规则”。只要这些业务规则指向的目标一致,并且可以被替换使用,我们就可以 用策略模式来封装它们。
/* 例子 已知一个商品总价 500根据折扣计算实际价格例如:80% 70% 1000-300 800-50
*/
// 通过闭包的形式计算折扣
const calcDiscount = (function () {let priceList = {"80%": function (total) { return (total * 0.8).toFixed(2) },"70%": function (total) { return (total * 0.7).toFixed(2) },"50%": function (total) { return (total * 0.5).toFixed(2) }}function inner(total, type) {if (!priceList[type]) {return "折扣错误";}// 反之折扣正确,返回计算后的价格return priceList[type](total);}// 接下来根据需求定义一系列的算法// 添加折扣inner.add = function (type, fn) {priceList[type] = fn;}// 删除折扣inner.remove = function (type) {delete priceList[type];}// 修改折扣inner.change = function (type, fn) {priceList[type] = fn;}// 查看折扣inner.look = function () {return priceList;}return inner;
})()// 测试算法
// 1.计算价格
// const res = calcDiscount(1400, "80%");
// console.log(res); // 1120.00// 2.添加折扣
// calcDiscount.add("40%", function (total) { return (total * 0.4).toFixed(2) });
// const res = calcDiscount(1500, "40%")
// console.log(res); // 600// 3.删除折扣
// calcDiscount.remove("80%");
// const res = calcDiscount(1200, "80%");
// console.log(res); // 折扣错误 // 4.修改折扣
calcDiscount.change("70%", function (total) { return (total * 0.4).toFixed(2) });
const res = calcDiscount(1200, "70%");
console.log(res); // 480.00// 5.查看折扣
// console.log(calcDiscount.look()); // {80%: ƒ, 70%: ƒ, 50%: ƒ}
优点:
缺点:
发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。
订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。
例子1:比如我们很喜欢看某个公众号号的文章,但是我们不知道什么时候发布新文章,要不定时的去翻阅;这时候,我们可以关注该公众号,当有文章推送时,会有消息及时通知我们文章更新了。
上面一个看似简单的操作,其实是一个典型的发布订阅模式,公众号属于发布者,用户属于订阅者;用户将订阅公众号的事件注册到调度中心,公众号作为发布者,当有新文章发布时,公众号发布该事件到调度中心,调度中心会及时发消息告知用户。
例子2:一个卖书的例子,当你去买书的时候,如果当时有你要买的书,可以直接买到当时当书店没有这本书的时候,你就需要先在书店预订,让老板等书到了通知你,而如果你等待的时候突然又不想买了你可以告诉老板你不要了。
在上述案例中,老板就是发布者,买者就是订阅者,不想要了就是取消订阅。
class Subscribe {constructor() {// 登记本:存放书籍和操作方法this.message = {};}// 添加订阅消息add(type, fn) {// type:订阅的书籍// fn:书籍到货后的方法// 添加前,判断登记本上是否已经存在这本书籍if (!this.message[type]) {// 如果不存在,设置一个空数组this.message[type] = [];}// 如果存在,不重复登记if (this.message[type].indexOf(fn) !== -1) { return }// 正常登记,添加对应的书籍数据this.message[type].push(fn);console.log(this.message);}// 所需的书籍到货,发布通知emit(type) {// 如果还没有这本书籍if (!this.message[type]) { return }// 如果书籍到货,通知所有想要这本书籍的人this.message[type].forEach(item => item());}// 取消订阅remove(type, fn) {// 如果没有对应的订阅消息if (!this.message[type]) { return }// 取消该书籍所有的订阅消息if (!fn) {delete this.message[type];return;}// 取消对应的订阅消息this.message[type] = this.message[type].filter(item => item !== fn);}
}
// 设置一个卖书的老板
const zs = new Subscribe();
// 进行操作
function fn1() {console.log("小王需要一本");
}
function fn2() {console.log("小张需要一本");
}
// 订阅
zs.add("西游记", fn1); // {西游记: Array(1)}
zs.add("西游记", fn2); // {西游记: Array(2)}
zs.add("三国演义", fn2); // {西游记: Array(2), 三国演义: Array(1)}
// 发布
zs.emit("西游记"); // 小王需要一本 小张需要一本
zs.emit("红楼梦");
// 取消订阅
zs.remove("西游记", fn1); // {西游记: Array(1), 三国演义: Array(1)}
zs.remove("红楼梦");
结束语:
希望对您有一点点帮助,如有错误欢迎小伙伴指正。
👍点赞:您的赞赏是我前进的动力!
⭐收藏:您的支持我是创作的源泉!
✍评论:您的建议是我改进的良药!
一起加油!!!💪💪💪