设计模式是软件开发人员在软件开发过程中面临的一类问题的最佳解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。设计模式不只是存在于前端,而是软件开发中都会遇到同类问题的一种解决思路,主要采用了面向对象编程的思想解决问题。
设计模式五大基本原则:单一功能原则、开放封闭原则、里式替换原则、接口隔离原则和依赖反转原则。
“需求指导设计,设计指导开发”,所以设计模式是软件开发中必须要掌握的基础知识,不管从事什么开发,只有掌握了设计模式才能更好的去做项目的设计,成为项目负责人。精通设计模式也是成为高级工程师或项目负责人的一个必备技能和必要条件。
设计模式总共有23种,分为三大类:
① 创建型模式,共5种:工厂方法模式
、抽象工厂模式、单例模式
、建造者模式、原型模式
。
② 结构型模式,共7种:适配器模式、装饰器模式
、代理模式
、外观模式、桥接模式、组合模式、享元模式。
③ 行为型模式,共11种:策略模式、模板方法模式、观察者模式
、迭代器模式
、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
注:标红的7种模式是常用的设计模式,必须要掌握的,其它设计模式可以做了解。
三大类型模式的特点:
① 创建型模式,通过以某种方式控制对象的创建来解决问题。创建型模式由两个主导思想构成。一是将系统使用的具体类封装起来,二是隐藏这些具体类的实例创建和结合的方式。
② 结构型模式,解析类和对象的内部结构和外部组合,通过优化程序结构解决模块之间的耦合问题。
③ 行为型模式,通过对象之间的交互行为来解决问题。
.
七种常用设计模式简单介绍:
① 工厂模式,封装实例的创建过程,解放 new Class(),Vue、React 底层都有采用。
② 单例模式,全局只允许有一个实例,多则出错。用 TS 语言实现更优雅。
③ 原型模式,原型和原型链是 JS 基础知识,还扩展了 JS 对象属性描述符。
④ 装饰器模式,Decorator 现在已是 JS 的标准语法,装饰器正在前端普及开来。
⑤ 代理模式,编程处处有代理,无论开发环境还是线上环境,如:ES6 中的代理对象 Proxy,Vue 3 数据劫持使用 Proxy 实现数据响应式……
⑥ 观察者模式,前端最常用的设计模式,Vue、React 两大前端框架也使用了该设计模式。(很重要,必须掌握,面试常考)
⑦ 迭代器模式,遍历数据不仅仅是 for 和 forEach,还有更高级的 Iterator, JS 早已支持。
观察者模式中存在两个对象,一个是被观察对象 Subject
,另一个是观察者 Observer
,可以有多个观察者。当被观察对象 Subject
的状态发生改变时,所有依赖于它的观察者 Observer
都将得到通知,并自动更新。观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯,需要知道彼此的存在,定义了对象之间的一种一对多的依赖关系。
举个例子:我们平时微信关注的都有一些微信公众号,只有关注或者说订阅了公众号,才会收到公众号的推送文章,关注后,只要公众号内容有更新,就会推送到每个订阅用户的手上。这里公众号就是被观察对象 Subject,用户就是观察者 Observer。
下面的观察者模式图示,清楚的展示了其如何实现:当被观察对象 Subject 状态发生改变时,会调用自身的 notify() 方法通知观察者 Observers 调取自身绑定的更新函数 update() 同步更新。
JS 代码实现:
/**
* @Author: yehuixiang
* @Date: 2022年010月21日 11:00
* @Description: 观察者模式
*/
class Subject {
constructor() {
this.subscriber = [] // 订阅者列表
}
// 添加观察者
add(observer) {
this.subscriber.push(observer)
}
// 取消观察者
cancel(observer) {
const index = this.subscriber.indexOf(observer)
this.subscriber.splice(index, 1)
}
// 通知所有订阅者去更新状态
notify() {
this.subscriber.forEach(item => item.update())
}
}
class Observer {
constructor() {
this.state = false
}
update() {
this.state = !this.state
console.log('%c 状态已更新!', 'color:green;font-size:18px')
}
}
// 创建观察者
var observer1 = new Observer(),ovserver2 = new Observer()
// 创建目标对象
var subject = new Subject()
// 添加订阅者
subject.add(observer1);subject.add(ovserver2)
// 目标主动通知订阅者
subject.notify()
发布-订阅者模式只是观察者模式的别名,随着时间推移,在观察者基础上,发展成一种不同的设计模式,功能更强大。发布-订阅者模式中存在三个对象,一个是发布者 Publisher/Subject
,一个是调度中心/事件总线/消息中心 Event Channel
,另一个是订阅者 Subscriber/Observer
。发送者不会将消息直接发送给订阅者,也就是说发布者和订阅者不知道彼此的存在。事件总线相当于发布者和订阅者的桥梁,将两者关联起来,接收所有发布者传入的消息并相应地分发它们给订阅者。发布者和订阅者可以是 一对多、多对多 的关系。
举个例子:生活中,我们 (Subscriber) 都会买房子,买房子一般都会找中介 (EventBus) ,中介手上有很多房型消息,我们可以关注 ($on 订阅) 自己感兴趣的一些房型,当售楼处 (Publisher) 发布消息或者房型信息更新时,会告知 ( $ emit) 给中介,中介就会收到消息,去通知我们。这样的话我们就不用和任何一家售楼处保持紧密的联系,只需要与某个中介联系,可以通过中介知道所有房型的变化。也就是说我们与售楼处是没有耦合关系的。
下面的发布-订阅者模式图示,清楚的展示了其如何实现:当发布者 Publisher 发布消息时,会调用 $emit() 方法通知事件总线 EventBus 去执行通过 $on 绑定的订阅者 Subscriber 更新函数 update(),实现异步更新。
注:Vue 中事件总线 EventBus 通信方式,以及响应式原理就是采用了该设计模式。
JS 代码实现:
/**
* @Author: yehuixiang
* @Date: 2022年010月21日 11:00
* @Description: 发布-订阅者模式
*/
// 消息中心
class EventBus {
constructor() {
this.eventMap = new Map();
}
// 订阅
$on(eventName, callback) {
// 如果有多个订阅者订阅了,依次保存
if(this.eventMap.has(eventName)){
this.eventMap.get(eventName).push(callback);
}else {
// 没人订阅,直接添加
this.eventMap.set(eventName,[callback]);
}
}
// 发布
$emit(eventName, data) {
// 取出对应事件名称的所有订阅者
const callList = this.eventMap.get(eventName);
if(callList?.[0]) {
callList.forEach(fn => fn.call(this, data))
}else {
console.log('未找到对应事件')
}
}
// 移除事件
$off(eventName, callback) {
if(!callback) {
this.eventMap.delete(eventName)
return
}
const callList = this.eventMap.get(eventName);
if(callList?.[0]) {
const filterCallList = callList.filter(fn => fn === callback);
this.eventMap.set(eventName, filterCallList);
}
}
}
const $bus = new EventBus();
// 监听事件
const callback_1 = args => {
console.log('我是监听事件1', args);
};
const callback_2 = args => {
console.log('我是监听事件2', args);
};
$bus.$on('test',callback_1);
$bus.$on('test',callback_2);
$bus.$emit('test',[1,2,3]);
setTimeout(() => {
$bus.$off('test');
console.log('已经移除test')
$bus.$emit('test',[1,2,3]);
}, 1000)
观察者模式中,Subject 自己维护观察者列表并进行注册和通知。发布订阅模式,则是引入一个中间平台(消息中心)进行注册和通知,相当于从 Subject 中解耦出来。所以观察者模式中被观察对象和观察者之间是直接联系的,发布订阅者模式两者则是间接联系,多出来一个消息中心作为桥梁,对两者进行管理。