前言
整理了一下常见的设计模式
观察者模式
特点:
- 主题和观察者的解耦,由于不存在引用关系,使得独立地变化,互不影响。
- 一个主题对象可以有多个观察者对象,形成一对多的依赖关系。
简易版本主题:
// 简易版代码
class Subject{
// 一个主题对象维护一个观察者数组
constructor(){
this.observerList = [];
}
// 添加观察者
addObserver(observer){
this.observerList.push(observer);
}
// 移除观察者
removeObserver(observer){
const index = this.observerList.indexOf(observer);
this.observerList.splice(index, 1);
}
// 通知观察者
notifyObserver(message){
const observers = this.observerList;
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 = {}; // 存储订阅的监听器
}
/**
* 发布者管理
* @param {string} type 消息类型
* @param {*} content 消息内容
*/
publish(type, content) {
const existContent = this.messages[type];
if (!existContent) {
this.messages[type] = [];
}
this.messages[type].push(content);
}
/**
* 订阅者管理
* @param {string} type 消息类型
* @param {function} cb 回调函数
*/
subscribe(type, cb) {
const existListener = this.listeners[type];
if (!existListener) {
this.listeners[type] = [];
}
this.listeners[type].push(cb);
}
/**
* 通知订阅者
* @param {string} type 消息类型
*/
notify(type) {
const messages = this.messages[type];
const subscribers = this.listeners[type] || [];
subscribers.forEach((cb) => cb(messages));
}
}
发布者:
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_B, 'imitation game');
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);
总的来说,订阅发布者模式是一种非常灵活和可扩展的设计模式,它被广泛应用于各种场合,比如:
- 事件发布者(例如 DOM 元素)调用
addEventListener
等方法来注册相关事件,监听点击、滚动、监听事件,将事件作为消息广播给所有订阅者。 Vue
组件间通信可以用事件总线来实现,调用$on()
或者$emit()
// 创建事件总线实例
const EventBus = new Vue()
// 组件A中监听事件
EventBus.$on('event-name', (data) => {
console.log('Component A received data:', data)
})
// 组件B中触发事件
EventBus.$emit('event-name', someData)
Vue
的双向绑定:
nodejs
里的常用的server
也是基于订阅发布者模式
const { EventEmitter } = require("stream");
const server = http.createServer();
console.log(server instanceof EventEmiter);// true
// 常见的server的方法
const http = require('http');
const server = http.createServer();
// 监听服务器开始监听指定端口的事件
server.on('listening', () => {
console.log('Server is listening at port 3000...');
});
// 监听客户端请求的事件,并进行处理
server.on('request', (req, res) => {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<h1>Hello, World!</h1>');
});
// 监听服务器关闭的事件
server.on('close', () => {
console.log('Server has been closed...');
});
server.listen(3000);
- 简而言之:解耦了组件间的耦合度,维护性强,因为并没有引用关系。
区别
两种设计模式思路是一样的,举个生活例子:
- 观察者模式:某公司给自己员工发月饼发粽子,是由公司的行政部门发送的,这件事不适合交给第三方,原因是“公司”和“员工”是一个整体
- 发布-订阅模式:某公司要给其他人发各种快递,因为“公司”和“其他人”是独立的,其唯一的桥梁是“快递”,所以这件事适合交给第三方快递公司解决
上述过程中,如果公司自己去管理快递的配送,那公司就会变成一个快递公司,业务繁杂难以管理,影响公司自身的主营业务,因此使用何种模式需要考虑什么情况两者是需要耦合的
两者区别如下图:
- 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
- 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
- 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)
单例模式
单例模式只会在全局作用域下创建一次实例对象,让所有需要调用的地方都共享这一单例对象,如下图所示:
但是一般情况我们不认为全局变量是一个单例模式,原因是:
- 全局命名污染
- 不易维护,容易被重写覆盖
// 单例构造函数
function CreateSingleton (name) {
this.name = name;
this.getName();
};
// 获取实例的名字
CreateSingleton.prototype.getName = function() {
console.log(this.name)
};
// 单例对象
const Singleton = (function(){
var instance;
return function (name) {
if(!instance) {
instance = new CreateSingleton(name);
}
return instance;
}
})();
// 创建实例对象1
const a = new Singleton('a');
// 创建实例对象2
const b = new Singleton('b');
console.log(a===b); // true
应用场景:
- Vuex使用单例模式可以创建一个全局的状态管理对象,用于统一管理应用的状态
- 单例模式可以创建一个全局的缓存管理对象,确保统一的数据缓存策略
- 在需要管理弹窗、提示框或对话框等组件的情况下,使用单例模式可以创建一个全局的弹窗管理对象
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END