优点:封装了对象的创建过程,降低了耦合性,提供了灵活性和可扩展性。
缺点:增加了代码的复杂性,需要创建工厂类。
适用场景:当需要根据不同条件创建不同对象时,或者需要隐藏对象创建的细节时,可以使用工厂模式。
class Button {
constructor(text) {
this.text = text;
}
render() {
console.log(`Rendering button with text: ${this.text}`);
}
}
class ButtonFactory {
createButton(text) {
return new Button(text);
}
}
const factory = new ButtonFactory();
const button = factory.createButton('Submit');
button.render(); // Output: Rendering button with text: Submit
优点:确保一个类只有一个实例,节省系统资源,提供全局访问点。
缺点:可能引入全局状态,不利于扩展和测试。
适用场景:当需要全局唯一的对象实例时,例如日志记录器、全局配置对象等,可以使用单例模式。
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}
Logger.instance = this;
}
log(message) {
console.log(`Logging: ${message}`);
}
}
const logger1 = new Logger();
const logger2 = new Logger();
console.log(logger1 === logger2); // Output: true
优点:实现了对象之间的松耦合,支持广播通信,当一个对象状态改变时,可以通知依赖它的其他对象进行更新。
缺点:可能导致性能问题和内存泄漏,需要合理管理观察者列表。
适用场景:当需要实现对象之间的一对多关系,一个对象的改变需要通知其他多个对象时,可以使用观察者模式。
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
notify(message) {
this.observers.forEach((observer) => observer.update(message));
}
}
class Observer {
update(message) {
console.log(`Received message: ${message}`);
}
}
const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify('Hello, observers!'); // Output
优点:解耦了发布者和订阅者,使它们可以独立变化。增加了代码的灵活性和可维护性。
缺点:可能会导致发布者过度发布消息,造成性能问题。订阅者需要订阅和取消订阅相关的逻辑。
适用场景:当存在一对多的关系,一个对象的状态变化需要通知多个其他对象时,可以使用发布订阅模式。
class PubSub {
constructor() {
this.subscribers = {};
}
subscribe(event, callback) {
if (!this.subscribers[event]) {
this.subscribers[event] = [];
}
this.subscribers[event].push(callback);
}
unsubscribe(event, callback) {
const subscribers = this.subscribers[event];
if (subscribers) {
this.subscribers[event] = subscribers.filter(cb => cb !== callback);
}
}
publish(event, data) {
const subscribers = this.subscribers[event];
if (subscribers) {
subscribers.forEach(callback => callback(data));
}
}
}
const pubsub = new PubSub(); // 创建发布订阅对象
const callback1 = data => console.log('Subscriber 1:', data);
const callback2 = data => console.log('Subscriber 2:', data);
pubsub.subscribe('event1', callback1); // 订阅事件
pubsub.subscribe('event1', callback2); // 订阅事件
pubsub.publish('event1', 'Hello, world!'); // 发布事件
pubsub.unsubscribe('event1', callback2); // 取消订阅事件
pubsub.publish('event1', 'Hello again!'); // 再次发布事件
/**
* Subscriber 1: Hello, world!
Subscriber 2: Hello, world!
Subscriber 1: Hello again!
*/
在上述示例中,PubSub 是发布订阅的实现类,它维护一个订阅者列表 subscribers,用于存储不同事件的订阅者列表。通过 subscribe 方法订阅事件,将回调函数添加到对应事件的订阅者列表中;通过 unsubscribe 方法取消订阅事件,从对应事件的订阅者列表中移除回调函数;通过 publish 方法发布事件,遍历对应事件的订阅者列表,依次执行回调函数。通过发布订阅模式,发布者和订阅者之间解耦,可以实现松散耦合的组件间通信。
发布订阅模式适用于许多场景,如事件驱动的系统、消息队列、UI组件间的通信等,可以实现组件之间的解耦和灵活性。
发布订阅模式(Publish-Subscribe Pattern)和观察者模式(Observer Pattern)是两种常见的设计模式,它们有一些相似之处,但也存在一些区别。
相似之处:
区别:
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
update(data) {
console.log('Received data:', data);
}
}
// 创建主题对象
const subject = new Subject();
// 创建观察者对象
const observer1 = new Observer();
const observer2 = new Observer();
// 添加观察者
subject.addObserver(observer1);
subject.addObserver(observer2);
// 发送通知
subject.notify('Hello, observers!');
发布订阅模式示例:
class EventBus {
constructor() {
this.subscribers = {};
}
subscribe(event, callback) {
if (!this.subscribers[event]) {
this.subscribers[event] = [];
}
this.subscribers[event].push(callback);
}
unsubscribe(event, callback) {
const subscribers = this.subscribers[event];
if (subscribers) {
this.subscribers[event] = subscribers.filter(cb => cb !== callback);
}
}
publish(event, data) {
const subscribers = this.subscribers[event];
if (subscribers) {
subscribers.forEach(callback => callback(data));
}
}
}
// 创建事件总线对象
const eventBus = new EventBus();
// 订阅事件
eventBus.subscribe('message', data => {
console.log('Received message:', data);
});
// 发布事件
eventBus.publish('message', 'Hello, subscribers!');
在上述示例中,观察者模式中的Subject类相当于发布订阅模式中的EventBus类,Observer类相当于订阅者(观察者),notify方法相当于publish方法,update方法相当于订阅者接收到事件后的回调函数。
观察者模式和发布订阅模式都是常见的用于实现事件处理和消息通信的设计模式,根据实际场景和需求选择合适的模式进行使用。观察者模式更加简单直接,适用于一对多的关系,而发布订阅模式更加灵活,可以支持多对多的关系,并且通过中间件来解耦发布者和订阅者。
class Shape {
constructor() {
this.type = '';
}
clone() {
return Object.create(this);
}
draw() {
console.log(`Drawing a ${this.type}`);
}
}
const circlePrototype = new Shape();
circlePrototype.type = 'Circle';
const squarePrototype = new Shape();
squarePrototype.type = 'Square';
const circle = circlePrototype.clone();
circle.draw(); // Output: Drawing a Circle
const square = squarePrototype.clone();
square.draw(); // Output: Drawing a Square
class Component {
operation() {
console.log('Component operation');
}
}
class Decorator {
constructor(component) {
this.component = component;
}
operation() {
this.component.operation();
}
}
class ConcreteComponent extends Component {
operation() {
console.log('ConcreteComponent operation');
}
}
class ConcreteDecoratorA extends Decorator {
operation() {
super.operation();
console.log('ConcreteDecoratorA operation');
}
}
class ConcreteDecoratorB extends Decorator {
operation() {
super.operation();
console.log('ConcreteDecoratorB operation');
}
}
const component = new ConcreteComponent();
const decoratorA = new ConcreteDecoratorA(component);
const decoratorB = new ConcreteDecoratorB(decoratorA);
decoratorB.operation();
// Output:
// Component operation
// ConcreteComponent operation
// ConcreteDecoratorA operation
// ConcreteDecoratorB operation
class Target {
request() {
console.log('Target request');
}
}
class Adaptee {
specificRequest() {
console.log('Adaptee specificRequest');
}
}
class Adapter extends Target {
constructor(adaptee) {
super();
this.adaptee = adaptee;
}
request() {
this.adaptee.specificRequest();
}
}
const target = new Target();
target.request();
// Output: Target request
const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
adapter.request();
// Output: Adaptee specificRequest
在上述示例中,Target 定义了客户端所期望的接口,Adaptee 是一个已有的类,它的接口与 Target 不兼容。适配器 Adapter 继承自 Target,并在其内部持有一个 Adaptee 的引用,通过适配器的 request 方法调用 Adaptee 的 specificRequest 方法,从而实现了对不兼容接口的适配。客户端可以通过调用适配器的 request 方法来使用 Adaptee 的功能。
适配器模式可以用于许多场景,例如在使用第三方库时需要将其接口转换成符合自己代码规范的接口,或者在对旧系统进行重构时需要兼容旧代码和新代码之间的差异。