发布/订阅模式和观察者模式的区别是什么?
发布/订阅模式(Publish/Subscribe Pattern)和观察者模式(Observer Pattern)都是常见的软件设计模式,用于实现对象间的松耦合通信。它们之间的区别如下:
通信方式: 在发布/订阅模式中,发布者(或称为主题)和订阅者之间通过一个消息队列(或称为事件总线)进行通信。发布者将消息发布到消息队列,然后订阅者从消息队列中订阅感兴趣的消息。而在观察者模式中,观察者直接订阅并接收主题对象的通知。
关注点: 发布/订阅模式关注的是消息的传递和处理,而观察者模式关注的是对象之间的状态变化。
耦合性: 发布/订阅模式中,发布者和订阅者之间的耦合度较低。发布者只需要将消息发布到消息队列,不需要关心具体的订阅者。订阅者只需要关注自己感兴趣的消息类型,不需要知道发布者的细节。而在观察者模式中,主题对象和观察者之间的耦合度较高,主题对象需要知道观察者的存在并直接通知观察者。
数量关系: 在发布/订阅模式中,一个消息可以同时被多个订阅者接收,发布者和订阅者之间是多对多的关系。而在观察者模式中,一个主题对象可以有多个观察者,但观察者只能观察一个主题对象,是一对多的关系。
灵活性: 由于发布/订阅模式使用了消息队列,发布者和订阅者之间的连接是间接的,可以更灵活地添加或移除订阅者,而不会对其他部分产生影响。观察者模式中,主题对象和观察者之间的连接是直接的,添加或移除观察者可能需要对主题对象进行修改。
总的来说,发布/订阅模式更加灵活和松耦合,适用于多对多的通信场景,而观察者模式更加紧密耦合,适用于一对多的通信场景,且关注对象之间的状态变化。选择使用哪种模式需要根据具体的应用场景和需求来决定。
装饰器模式一般会在什么场合使用?
装饰器模式(Decorator Pattern)通常在以下场合使用:
功能扩展: 装饰器模式可以在不修改现有对象结构的情况下,动态地添加新的功能或行为。通过将对象放入装饰器中,可以通过叠加多个装饰器实现功能的递增扩展。这种扩展方式对于需要在运行时根据需求灵活地添加或移除功能的情况非常有用。
避免类爆炸: 当存在大量相似但有细微差别的类时,使用装饰器模式可以避免创建过多的类。通过使用装饰器,可以将各种功能组合在一起,而不需要为每种组合创建一个独立的类。
透明性: 装饰器模式可以保持对象接口的一致性,使得客户端无需关心对象是否经过装饰。客户端可以像使用原始对象一样使用被装饰后的对象,无需关心装饰器的存在。
动态组合: 装饰器模式允许在运行时动态地组合对象,而不是在编译时静态地组合。这提供了更大的灵活性,可以根据需求组合不同的装饰器,以获得所需的行为。
单一职责原则: 装饰器模式可以将功能的实现细分到单个装饰器中,每个装饰器只关注一个特定的功能。这有助于遵循单一职责原则,使得每个类或装饰器只负责完成一个清晰定义的任务。
需要注意的是,装饰器模式适用于在不修改现有对象结构的情况下进行功能扩展,但可能会引入较多的类和对象,增加了系统的复杂性。因此,在使用装饰器模式时需要权衡利弊,确保它能够带来更好的灵活性和可维护性,而不会过度复杂化代码。
简单实现一个发布 / 订阅模式
发布/订阅模式是一种消息传递模式,其中发送者(发布者)不会将消息直接发送给特定的接收者(订阅者)。相反,发布的消息会被分类到某些类别,而订阅者则可以订阅一个或多个类别,从而只接收感兴趣的消息。
以下是一个简单的发布/订阅模式的 TypeScript 实现:
class PubSub {
private subscribers: { [key: string]: Function[] } = {};
// 订阅事件
subscribe(event: string, callback: Function) {
if (!this.subscribers[event]) {
this.subscribers[event] = [];
}
this.subscribers[event].push(callback);
}
// 取消订阅
unsubscribe(event: string, callback: Function) {
if (this.subscribers[event]) {
this.subscribers[event] = this.subscribers[event].filter(
(subscriber) => subscriber !== callback
);
}
}
// 发布事件
publish(event: string, data: any) {
if (this.subscribers[event]) {
this.subscribers[event].forEach((callback) => callback(data));
}
}
}
在这个实现中,PubSub
类有三个方法:subscribe
、unsubscribe
和 publish
。subscribe
方法用于订阅一个事件,unsubscribe
方法用于取消订阅,publish
方法用于发布一个事件。每个事件都有一个名字,以及一个或多个回调函数,这些回调函数会在事件被发布时被调用。
希望这个答案对你有所帮助!
常用设计模式有哪些并举例使用场景
设计模式是在软件开发中经常出现的问题的通用、可重用的解决方案。以下是一些常用的设计模式及其使用场景:
- 单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。例如,一个全局配置对象,它需要在整个应用程序中被共享。
class Singleton {
constructor(data) {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = this;
this.data = data;
}
getData() {
return this.data;
}
}
const singleton = new Singleton('Singleton');
console.log(singleton.getData()); // 输出:Singleton
- 工厂模式(Factory):提供一个创建对象的接口,让子类决定实例化哪一个类。例如,创建不同类型的用户,如管理员和普通用户。
class UserFactory {
createUser(type) {
switch(type) {
case 'admin':
return new AdminUser();
case 'regular':
return new RegularUser();
default:
throw new Error('Invalid user type');
}
}
}
- 观察者模式(Observer):当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并被自动更新。例如,实现一个事件系统,当事件发生时,所有注册的监听器都会收到通知。
class EventEmitter {
constructor() {
this.listeners = {};
}
on(event, listener) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(listener);
}
emit(event, args) {
if (this.listeners[event]) {
this.listeners[event].forEach(listener => listener(args));
}
}
}
- 策略模式(Strategy):定义一系列算法,并将每一个算法封装起来,使它们可以互相替换。例如,实现一个排序函数,可以根据不同的策略(如快速排序、冒泡排序等)进行排序。
class Sorter {
constructor(sortStrategy) {
this.sortStrategy = sortStrategy;
}
sort(array) {
return this.sortStrategy.sort(array);
}
}
以上只是一些基本的设计模式,实际上,设计模式的种类非常多,你可以根据你的项目需求选择合适的设计模式。希望这个解释对你有所帮助!
观察者模式和发布订阅模式的区别
观察者模式
所谓观察者模式,其实就是为了实现 松耦合(loosely coupled)。
用《Head First设计模式》里的气象站为例子,每当气象测量数据有更新,changed()
方法就会被调用,于是我们可以在 changed()
方法里面,更新气象仪器上的数据,比如温度、气压等。
但是这样写有个问题,就是如果我们想在 changed()
方法被调用时,更新更多的信息,比如湿度,那就需要去修改 changed()
的代码,这就是紧耦合的坏处。
怎么解决呢?使用观察者模式,面向接口编程,实现松耦合。
观察者模式里面,changed()
方法所在的实例对象,也就是被观察者(Subject,或者叫 Observable),它只需要维护一套观察者(Observer)的集合,这些 Observer 实现相同的接口,Subject 只需要知道,通知 Observer 时,需要调用那个统一方法就好了:
发布订阅模式
很多人都和我一样,认为发布订阅模式里面的 Publisher,就是观察者模式里面的 Subject,而 Subscriber 就是 Observer。Publisher 变化时,就会去主动通知 Subscriber。
其实并不是。
在发布订阅模式里,发布者并不会主动通知订阅者,换句话说,发布者和订阅者彼此互不相识。
互不相识?那么他们是如何通信的?
答案是,通过第三者,也就是在消息队列里面,我们常说的经纪人 Broker。
发布者只需要告诉 Broker,我要发送的消息,topic 时 AAA;
订阅者只需要告诉 Broker,我要订阅的消息是 AAA;
于是当 Broker 收到发布者发过来的消息时,就会把消息推送给订阅方,当然也有可能是订阅方主动过来取,具体看实现方式。
也就是说,发布订阅模式里,发布者和订阅者这件不是松耦合,而是完全解耦。
放一张极简图给大家对比:
总结
从表面上看:
- 观察者模式里,只有两个角色 —— 观察者 + 被观察者
- 而发布订阅模式里,却不仅仅只有发布者和订阅者两个角色,还有一个经常被我们忽略的 —— 经纪人Broker
往更深层次讲:
- 观察者和被观察者,是松耦合的关系
- 发布者和订阅者,则完全不存在耦合
从使用层面上讲:
- 观察者模式,多用于单个应用内部
- 发布订阅模式,则更多的是一种跨应用的模式(cross-application pattern),比如我们常用的消息中间件