工厂模式
原理:
工厂模式通过使用工厂方法来创建对象,而不是直接使用new
关键字。工厂方法根据输入参数的不同,决定创建哪个具体的对象实例,并将其返回。
代码实现:
javascript
// 定义一个产品类
class Product {
constructor(name) {
this.name = name;
}
display() {
console.log(`Product: ${this.name}`);
}
}
// 定义工厂类
class Factory {
createProduct(name) {
return new Product(name);
}
}
// 使用工厂创建对象
const factory = new Factory();
const product1 = factory.createProduct('Product 1');
const product2 = factory.createProduct('Product 2');
product1.display(); // 输出: Product: Product 1
product2.display(); // 输出: Product: Product 2
使用场景:
- 当需要创建多个相似的对象时。
- 当对象创建过程复杂或需要隐藏创建逻辑时。
- 当希望通过一个公共的接口来创建对象时。
优点:
- 将对象的创建与使用代码分离,客户端只需关注接口而不需要关心具体的对象创建过程。
- 可以通过工厂方法来创建不同类型的对象,提供灵活性和可扩展性。
单例模式
原理:
单例模式确保一个类只有一个实例,并提供全局访问点以获取该实例。它通过私有化构造函数,限制外部直接创建对象,并提供一个静态方法来获取或创建唯一的实例。
代码实现:
ini
class Singleton {
static instance = null;
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = this;
}
}
// 获取实例
const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // 输出: true
使用场景:
- 当只需要一个全局对象来协调系统中的操作时。
- 当需要频繁访问同一个对象实例时。
- 当需要限制一个类只能有一个实例时。
优点:
- 提供了对唯一实例的全局访问,方便共享对象。
- 避免了重复创建实例的开销,节省了内存和资源
观察者模式
原理:
观察者模式定义了对象之间的一对多依赖关系。当一个对象的状态发生变化时,它的所有依赖者(观察者)都会被通知和更新。
代码实现:
javascript
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter((obs) => obs !== observer);
}
notifyObservers() {
this.observers.forEach((observer) => observer.update());
}
}
class Observer {
constructor(name) {
this.name = name;
}
update() {
console.log(`Observer ${this.name} has been notified.`);
}
}
// 创建主题和观察者
const subject = new Subject();
const observer1 = new Observer('1');
const observer2 = new Observer('2');
// 注册观察者
subject.addObserver(observer1);
subject.addObserver(observer2);
// 通知观察者
subject.notifyObservers();
使用场景:
- 当一个对象的变化需要通知其他对象,以便它们可以做出相应的响应时。
- 当对象之间的耦合度需要降低,使得它们可以独立地交互时。
优点
- 实现了对象之间的松耦合,被观察者和观察者可以独立地演化和变化。
- 可以轻松添加或移除观察者,以实现动态的发布-订阅机制。
发布-订阅模式
原理:
发布-订阅模式类似于观察者模式,但是发布者(或称为主题)不会直接通知特定的订阅者,而是通过消息代理(或称为事件总线)来分发和传递消息。订阅者可以根据自身的需求订阅感兴趣的消息。
代码实现:
javascript
class EventBus {
constructor() {
this.subscribers = {};
}
subscribe(eventName, callback) {
if (!this.subscribers[eventName]) {
this.subscribers[eventName] = [];
}
this.subscribers[eventName].push(callback);
}
unsubscribe(eventName, callback) {
if (this.subscribers[eventName]) {
this.subscribers[eventName] = this.subscribers[eventName].filter(
(cb) => cb !== callback
);
}
}
publish(eventName, data) {
if (this.subscribers[eventName]) {
this.subscribers[eventName].forEach((callback) => callback(data));
}
}
}
// 创建事件总线
const eventBus = new EventBus();
// 订阅事件
const callback1 = (data) => console.log(`Subscriber 1 received: ${data}`);
const callback2 = (data) => console.log(`Subscriber 2 received: ${data}`);
eventBus.subscribe('event1', callback1);
eventBus.subscribe('event1', callback2);
// 发布事件
eventBus.publish('event1', 'Hello, subscribers!');
使用场景:
- 当一个对象的状态变化需要通知多个订阅者时。
- 当需要将发布者和订阅者解耦,使它们可以独立地演化时。
- 当希望在系统中引入中介层以提供更灵活的消息传递机制时。
优点
- 解耦了发布者和订阅者,使它们可以独立地交互。
- 提供了更灵活的消息传递机制,可以实现更复杂的事件处理逻辑。
原型模式
原理:
原型模式通过克隆现有对象来创建新对象,而不是依赖显式的实例化过程。每个对象都可以作为另一个对象的原型,新对象会继承原型对象的属性和方法。
代码实现:
javascript
class Prototype {
constructor(name) {
this.name = name;
}
clone() {
return Object.create(Object.getPrototypeOf(this));
}
}
// 创建原型对象
const prototype = new Prototype('Prototype');
// 克隆对象
const clone1 = prototype.clone();
const clone2 = prototype.clone();
console.log(clone1.name); // 输出: Prototype
console.log(clone2.name); // 输出: Prototype
使用场景:
- 当创建对象的过程比较昂贵或复杂时,而且新对象的创建与现有对象的状态无关时。
- 当希望通过修改原型对象来影响所有克隆对象时。
- 当需要避免使用
new
关键字直接实例化对象时。
优点
- 避免了创建对象的昂贵或复杂过程,提高了性能和效率。
- 可以通过修改原型对象来影响所有克隆对象,实现了对象状态的批量修改。
适配器模式
原理:
适配器模式将一个类的接口转换成另一个接口,以满足客户端的需求。它通过创建一个适配器类来实现接口转换,并在适配器类中调用被适配类的方法。
代码实现:
javascript
class Adaptee {
specificRequest() {
return 'Specific request';
}
}
class Adapter {
constructor(adaptee) {
this.adaptee = adaptee;
}
request() {
return this.adaptee.specificRequest();
}
}
// 使用适配器
const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
console.log(adapter.request()); // 输出: Specific request
使用场景:
- 当需要将一个已有类的接口转换成另一个接口时。
- 当希望通过一个统一的接口来使用多个不兼容的类时。
- 当需要在不影响现有代码的情况下,对已有类的方法进行扩展或修改时。
优点
- 可以将已有类与新代码进行无缝衔接,使它们能够协同工作。
- 可以实现对象之间的接口转换,提供了灵活性和可扩展性。
装饰者模式
原理:
装饰者模式动态地给对象添加新的行为或功能,同时不改变其原始类结构。它通过创建一个装饰器类来包装原始对象,并在装饰器类中添加额外的行为。
代码实现:
javascript
class Component {
operation() {
return 'Component operation';
}
}
class Decorator {
constructor(component) {
this.component = component;
}
operation() {
return `${this.component.operation()} + Decorator operation`;
}
}
// 使用装饰者
const component = new Component();
const decorator = new Decorator(component);
console.log(decorator.operation()); // 输出: Component operation + Decorator operation
使用场景:
- 当需要在不改变现有对象结构的情况下,动态地给对象添加新的行为时。
- 当希望通过透明的方式为对象添加功能,而不影响其使用方式和客户端代码时。
- 当不适合使用子类来扩展对象功能时。
优点
- 可以透明地扩展对象的功能,而不会影响客户端代码。
- 允许通过装饰器类组合和嵌套多个装饰器,实现复杂的功能组合。
策略模式
原理:
策略模式定义了一系列算法,将它们封装成独立的可互换的策略对象,并使得客户端可以在运行时动态地选择使用不同的策略。客户端通过与策略对象进行交互来实现不同的行为。
代码实现:
scala
class Strategy {
execute() {
// 策略执行的具体操作
}
}
class ConcreteStrategy1 extends Strategy {
execute() {
console.log('Strategy 1');
}
}
class ConcreteStrategy2 extends Strategy {
execute() {
console.log('Strategy 2');
}
}
// 使用策略
const strategy1 = new ConcreteStrategy1();
const strategy2 = new ConcreteStrategy2();
strategy1.execute(); // 输出: Strategy 1
strategy2.execute(); // 输出: Strategy 2
使用场景:
- 当需要在多个算法或行为之间进行动态选择时。
- 当希望将算法的实现与使用它的客户端代码分离,以便它们可以独立地演化和修改时。
- 当不希望使用大量的条件语句来处理不同的情况时。
优点
- 实现了算法的封装和多态性,可以根据需要灵活地切换算法。
- 将算法的实现与使用它的客户端代码分离,使得它们可以独立演化和修改。
模块模式
原理:
模块模式使用函数作用域和闭包来封装和组织代码,实现模块化和私有性。它通过返回一个包含公共方法和属性的对象,来实现对外部的封装。
代码实现:
javascript
const module = (function() {
let privateVariable = 'Private';
function privateMethod() {
console.log('Private method');
}
return {
publicVariable: 'Public',
publicMethod: function() {
console.log('Public method');
}
};
})();
console.log(module.publicVariable); // 输出: Public
module.publicMethod(); // 输出: Public method
使用场景:
- 当希望将相关的方法和属性封装在一个单独的对象中时。
- 当希望限制对方法和属性的访问,并保持私有性时。
- 当需要实现模块化,避免全局命名冲突和污染时。
优点
- 将相关的方法和属性封装在一个单独的对象中,提供了组织和管理代码的方式。
- 通过闭包实现了私有性,可以隐藏内部实现细节,防止外部访问和修改。
代理模式
原理:
代理模式为一个对象提供一个代理或占位符,并控制对其的访问。代理对象可以在访问被代理对象之前或之后添加额外的逻辑,如延迟加载、权限控制、缓存等。
代码实现:
javascript
class RealSubject {
request() {
console.log('Real subject request');
}
}
class Proxy {
constructor(realSubject) {
this.realSubject = realSubject;
}
request() {
// 在调用真实对象方法之前或之后执行额外操作
console.log('Proxy request');
this.realSubject.request();
}
}
// 使用代理
const realSubject = new RealSubject();
const proxy = new Proxy(realSubject);
proxy.request(); // 输出: Proxy request, Real subject request
使用场景:
- 当需要在访问对象之前或之后执行额外操作时。
- 当希望通过代理控制对对象的访问权限时。
- 当需要延迟加载对象或实现缓存等功能时。
优点:
- 可以在访问对象之前或之后执行额外操作,如延迟加载、权限控制、缓存等。
- 提供了对真实对象的访问控制,可以限制对对象的直接访问。
迭代器模式
原理:
迭代器模式提供了一种访问集合对象元素的方式,而无需暴露集合的内部结构。它将迭代逻辑封装在迭代器对象中,客户端通过迭代器来遍历集合。
代码实现:
kotlin
// 定义集合对象
class Collection {
constructor() {
this.items = [];
}
addItem(item) {
this.items.push(item);
}
getIterator() {
return new Iterator(this.items);
}
}
// 定义迭代器对象
class Iterator {
constructor(collection) {
this.collection = collection;
this.index = 0;
}
hasNext() {
return this.index < this.collection.length;
}
next() {
return this.collection[this.index++];
}
}
// 使用迭代器遍历集合
const collection = new Collection();
collection.addItem("Item 1");
collection.addItem("Item 2");
collection.addItem("Item 3");
const iterator = collection.getIterator();
while (iterator.hasNext()) {
console.log(iterator.next());
}
使用场景:
- 当集合对象的内部结构可能经常变化时,使用迭代器可以减少对客户端代码的影响。
- 当需要对集合对象进行不同类型的遍历时,迭代器提供了统一的接口,使得遍历逻辑更加灵活和可扩展。
- 当需要在遍历过程中对集合元素进行筛选、过滤或转换时,可以通过迭代器来实现。
优点:
- 将遍历集合的责任从客户端代码中抽离出来,简化了客户端代码。
- 隐藏了集合的内部结构,提供了更好的封装性和安全性。
- 支持不同类型的集合,提供了统一的迭代接口。
状态模式
原理:
状态模式允许对象在内部状态发生改变时改变其行为,看起来就像是对象类发生了改变。它将每个状态封装在一个独立的类中,并允许对象在不同状态之间切换。
代码实现:
scala
// 定义状态接口
class State {
handle(context) {
// 默认实现
}
}
// 定义具体状态类
class ConcreteStateA extends State {
handle(context) {
console.log("State A");
context.setState(new ConcreteStateB());
}
}
class ConcreteStateB extends State {
handle(context) {
console.log("State B");
context.setState(new ConcreteStateA());
}
}
// 定义上下文类
class Context {
constructor() {
this.state = new ConcreteStateA();
}
setState(state) {
this.state = state;
}
request() {
this.state.handle(this);
}
}
// 使用状态模式
const context = new Context();
context.request(); // 输出 "State A"
context.request(); // 输出 "State B"
context.request(); // 输出 "State A"
使用场景:
- 当一个对象的行为取决于其内部状态,并且在不同状态下具有不同行为时,可以使用状态模式来管理状态转换和行为。
- 当需要在运行时根据条件动态地改变对象的行为时,状态模式提供了一种优雅的方式来实现。
- 当对象有大量的条件语句,而且随着状态的增加会变得更加复杂时,可以使用状态模式来简化代码结构。
优点:
- 将对象的状态和行为封装在独立的类中,提高了代码的可读性和可维护性。
- 避免了使用大量的条件语句来处理不同的状态,简化了代码结构。
- 新增或修改状态变得更加容易,不会对其他状态产生影响。