JavaScript 常用设计模式
一、构造器模式
构造器模式(Constructor Pattern)是一种常见的设计模式,用于创建对象。它在JavaScript中广泛使用,特别是在创建自定义对象和类时。构造器模式的核心思想是通过构造函数来创建对象,构造函数通常以大写字母开头,用于表示它是一个构造函数。
构造器模式的核心思想
- 创建一个构造函数,通常以大写字母开头,用于表示它是一个构造函数。
- 在构造函数中定义对象的属性和方法。这些属性和方法通常通过
this关键字添加到新创建的对象上。 - 使用
new关键字来实例化对象。这将创建一个新对象,并执行构造函数内部的代码,初始化对象的属性。 - 对象实例化后,可以访问构造函数中定义的属性和方法。
ES5 和 ES6 中的构造器模式示例
ES6 构造器模式示例
在ES6中,构造器模式的语法变得更现代,使用类来定义对象和方法。
// 步骤1: 创建类
class PersonES6 {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 步骤2: 在构造函数中定义方法
sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
// 步骤3: 使用类实例化对象
const person1 = new PersonES6("Alice", 30);
const person2 = new PersonES6("Bob", 25);
// 步骤4: 调用方法
person1.sayHello();
person2.sayHello();
ES6的类语法提供了更简洁和现代的方式来定义构造函数和方法,但原则仍然相同。
ES5 构造器模式示例
// 步骤1: 创建构造函数
function PersonES5(name, age) {
this.name = name;
this.age = age;
}
// 步骤2: 在构造函数中定义方法
PersonES5.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
// 步骤3: 使用构造函数实例化对象
const person1 = new PersonES5("Alice", 30);
const person2 = new PersonES5("Bob", 25);
// 步骤4: 调用方法
person1.sayHello();
person2.sayHello();
应用场景
- 创建自定义对象和类,尤其是需要多个实例且具有相似属性和方法的对象。
- 封装对象的状态和行为,以提高代码的可维护性和可读性。
注意的问题
- 使用
new关键字来实例化对象,以确保属性和方法正确添加到新对象。 - 方法通常被添加到构造函数的原型上,以确保它们在所有实例之间共享,从而节省内存。
- 构造器模式适用于创建较少数量的对象。如果需要创建大量相似的对象,可能需要考虑其他设计模式,如工厂模式。
ES6的类(class)中的方法默认会被添加到类的原型(prototype)上
二、原型模式
原型模式(Prototype Pattern)是一种常见的设计模式,用于创建对象的模式。它利用了JavaScript中对象的原型链,通过创建一个原型对象,然后将其他对象继承自这个原型对象来实现属性和方法的共享。
什么是原型模式?
原型模式是基于对象的原型链来共享属性和方法。在JavaScript中,每个对象都有一个原型,这个原型可以包含属性和方法,然后其他对象可以继承这个原型。原型模式的核心思想是:
- 创建一个原型对象,包含要共享的属性和方法。
- 使用原型对象作为其他对象的原型,通过继承来共享属性和方法。
- 通过复制或链接原型对象来创建新的对象实例。
原型模式的案例
ES5 原型模式示例
// 步骤1: 创建原型对象
function PersonPrototype() {
this.name = "John";
this.age = 30;
}
PersonPrototype.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
// 步骤2和步骤3: 创建新对象实例
const person1 = new PersonPrototype();
const person2 = new PersonPrototype();
// 共享原型方法
person1.sayHello();
person2.sayHello();
console.log(person1.sayHello === person2.sayHello); // 输出 true
ES6 原型模式示例
在ES6中,原型模式同样适用,但使用了更现代的类语法:
// 步骤1: 创建原型对象
class PersonPrototype {
constructor() {
this.name = "John";
this.age = 30;
}
sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
// 步骤2和步骤3: 创建新对象实例
const person1 = new PersonPrototype();
const person2 = new PersonPrototype();
// 共享原型方法
person1.sayHello();
person2.sayHello();
console.log(person1.sayHello === person2.sayHello); // 输出 true
应用场景
原型模式适用于以下场景:
- 当需要创建多个对象实例,并且这些实例具有相似的属性和方法时,可以使用原型模式来共享这些属性和方法。
- 当需要实现继承和多态,以便创建更具扩展性的对象层次结构时,原型模式非常有用。
- 原型模式也可以用于创建对象的深拷贝,以便创建对象的克隆。
注意的问题
在使用原型模式时,需要注意以下问题:
- 所有实例共享原型对象的属性和方法,因此当一个实例修改原型上的属性或方法时,会影响所有其他实例。要小心不要在实例上直接修改原型对象。
- 如果需要在不同实例之间具有不同属性,可以在构造函数内部使用
this来定义实例特定的属性。 - 在ES6中,类的方法默认被添加到原型上,这符合原型模式的思想。 ES6 类提供了更现代的方式来实现原型继承和创建对象。
原型模式是一个有用的设计模式,用于创建共享属性和方法的对象,但要小心共享可能导致的副作用。在适当的情况下,原型模式可以提高代码的可维护性和性能。
三、工厂模式
工厂模式是一种创建对象的设计模式,它的目标是将对象的创建过程抽象出来,以便根据需要创建不同类型的对象。工厂模式提供了一个工厂函数或方法,它接受参数并返回一个新的对象。这样可以将对象的创建和初始化逻辑封装在一个工厂中,从而实现更好的代码复用和封装
个人理解:工厂模式就有点相当于switch 根据不同的类型返回一个对象
什么是工厂模式?
工厂模式是一种创建对象的设计模式,它通过一个工厂函数或方法来创建对象。工厂函数或方法接受参数,根据参数的不同,可以创建不同类型的对象。这样,工厂模式可以帮助封装对象的创建逻辑,提供更好的代码组织和复用。工厂模式的核心思想是:
- 创建一个工厂函数或方法,用于创建对象。
- 工厂接受参数,并根据参数的不同来创建不同类型的对象。
- 工厂返回创建的对象。
工厂模式的案例
ES5 工厂模式示例
// 步骤1: 创建工厂函数
function createPerson(name, age) {
const person = {};
person.name = name;
person.age = age;
person.sayHello = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
return person;
}
// 步骤2: 使用工厂函数创建对象
const person1 = createPerson("Alice", 30);
const person2 = createPerson("Bob", 25);
// 步骤3: 调用方法
person1.sayHello();
person2.sayHello();
ES6 工厂模式示例
在ES6中,可以使用类的静态方法来实现工厂模式。
// 步骤1: 创建类
class PersonFactory {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 步骤2: 静态工厂方法
static createPerson(name, age) {
return new PersonFactory(name, age);
}
sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
// 步骤3: 使用工厂方法创建对象
const person1 = PersonFactory.createPerson("Alice", 30);
const person2 = PersonFactory.createPerson("Bob", 25);
// 步骤4: 调用方法
person1.sayHello();
person2.sayHello();
使用静态方法的工厂模式允许你直接通过类而不是类的实例来创建对象。这就意味着你不需要先创建类的实例,而是可以直接调用类的工厂方法来生成对象。这提供了更直接和简洁的方式来创建对象,而不必显式地使用 new 关键字实例化类。
静态工厂方法通常用于封装对象的创建逻辑,提供更好的可读性和灵活性。它允许你将对象的创建过程与对象的使用分开,降低了客户端代码与具体对象创建细节之间的耦合。
因此,使用静态方法的工厂模式是一种有效的设计模式,可用于对象的创建,特别是在需要动态选择不同类型对象时。这种方式可以提供更清晰的代码组织,更好的可维护性和更直观的对象创建过程。
应用场景
工厂模式适用于以下场景:
- 当需要创建多个相似类型的对象时,工厂模式可以用于封装对象的创建逻辑,减少代码重复。
- 当对象的创建过程复杂,需要根据不同参数创建不同类型的对象时,工厂模式提供了一种封装的方式。
- 工厂模式可以用于隐藏对象创建的细节,使代码更具模块化和可维护性。
vue使用到的案例
- 组件创建:
Vue组件是通过Vue.extend()方法创建的。这个方法返回一个新的组件构造函数,这就是一个工厂方法,它用于创建组件实例。例如:
const MyComponent = Vue.extend({
// 组件选项
});
const myInstance = new MyComponent();
在这个例子中,Vue.extend 可以看作是一个工厂方法,用于创建组件构造函数。然后,通过 new 关键字可以创建组件实例。
- 组件注册:
Vue.js中使用Vue.component方法来注册全局组件,这个方法可以被视为一个工厂方法,它接受组件选项并创建组件构造函数。这个构造函数后续用于创建组件实例。
Vue.component('my-component', {
// 组件选项
});
这里的 Vue.component 方法将组件选项对象转化为组件构造函数,从而使你可以创建多个组件实例。
注意的问题
在使用工厂模式时,需要注意以下问题:
- 工厂模式通常用于创建简单的对象,但对于复杂对象,可能需要考虑其他创建型模式,如建造者模式。
- 工厂模式可以帮助封装对象的创建逻辑,但可能会导致对象的紧耦合。一些参数传递到工厂函数或方法,可能需要更好的抽象和解耦。
ES6中,类的静态方法可以用于实现工厂模式,但要注意是否需要公开静态方法以供其他模块使用。
四、抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,旨在提供一个接口用于创建相关或依赖对象的家族,而无需指定其具体类。这允许你创建一组对象,这些对象之间相互关联,而不必关心它们的具体类型。
什么是抽象工厂模式?
抽象工厂模式是一种设计模式,它提供了一个抽象工厂接口,该接口定义了一组用于创建一组相关对象的方法。每个具体的工厂类都实现这个抽象工厂接口,并负责创建特定类型的对象。抽象工厂模式的核心思想是:
- 定义一个抽象工厂接口,该接口包含一组方法,每个方法用于创建一组相关对象(产品家族)。
- 创建不同的具体工厂类,每个具体工厂类都实现抽象工厂接口,并负责创建特定类型的对象。
- 在客户端代码中,使用抽象工厂接口而不是具体工厂类,以创建对象,从而实现对象的家族创建。
- 说明一下:抽象工厂模式的核心思想是将对象的创建抽象出来,并提供一个通用的接口。
- 抽象工厂接口:抽象工厂定义了一组用于创建相关对象家族的方法。这些方法是抽象的,不包含具体实现。它充当了工厂的接口,提供了一种创建产品的通用方式。
- 具体工厂类:具体工厂类实现了抽象工厂接口中定义的方法,这些方法负责创建特定类型的产品。每个具体工厂类可以生产一组相关的产品,这些产品共同组成一个产品家族。
- 产品类:产品类是由具体工厂类创建的,每个产品类都有自己的特征和行为。这些产品类属于不同的产品家族,但它们都实现了一个共同的接口或继承了一个共同的抽象类。
- 客户端:客户端代码使用抽象工厂接口来创建产品,而不必关心具体的工厂类或产品类。客户端代码只与抽象工厂接口和产品接口交互。
抽象工厂模式的案例
ES5 抽象工厂模式示例
// 步骤1: 创建抽象工厂接口
function AbstractFactory() {
this.createProductA = function() {};
this.createProductB = function() {};
}
// 步骤2: 创建具体工厂类
function ConcreteFactory1() {
this.createProductA = function() {
return new ProductA1();
};
this.createProductB = function() {
return new ProductB1();
};
}
function ConcreteFactory2() {
this.createProductA = function() {
return new ProductA2();
};
this.createProductB = function() {
return new ProductB2();
};
}
// 步骤3: 创建抽象产品接口
function AbstractProductA() {
this.productType = "A";
}
function AbstractProductB() {
this.productType = "B";
}
// 步骤4: 创建具体产品类
function ProductA1() {}
ProductA1.prototype = new AbstractProductA();
function ProductA2() {}
ProductA2.prototype = new AbstractProductA();
function ProductB1() {}
ProductB1.prototype = new AbstractProductB();
function ProductB2() {}
ProductB2.prototype = new AbstractProductB();
// 步骤5: 使用抽象工厂创建对象
const factory1 = new ConcreteFactory1();
const productA1 = factory1.createProductA();
const productB1 = factory1.createProductB();
const factory2 = new ConcreteFactory2();
const productA2 = factory2.createProductA();
const productB2 = factory2.createProductB();
ES6 抽象工厂模式示例
// 步骤1: 创建抽象工厂接口
class AbstractFactory {
createProductA() {}
createProductB() {}
}
// 步骤2: 创建具体工厂类
class ConcreteFactory1 extends AbstractFactory {
createProductA() {
return new ProductA1();
}
createProductB() {
return new ProductB1();
}
}
class ConcreteFactory2 extends AbstractFactory {
createProductA() {
return new ProductA2();
}
createProductB() {
return new ProductB2();
}
}
// 步骤3: 创建抽象产品接口
class AbstractProductA {
constructor() {
this.productType = "A";
}
}
class AbstractProductB {
constructor() {
this.productType = "B";
}
}
// 步骤4: 创建具体产品类
class ProductA1 extends AbstractProductA {}
class ProductA2 extends AbstractProductA {}
class ProductB1 extends AbstractProductB {}
class ProductB2 extends AbstractProductB {}
// 步骤5: 使用抽象工厂创建对象
const factory1 = new ConcreteFactory1();
const productA1 = factory1.createProductA();
const productB
1 = factory1.createProductB();
const factory2 = new ConcreteFactory2();
const productA2 = factory2.createProductA();
const productB2 = factory2.createProductB();
应用场景
抽象工厂模式适用于以下场景:
- 当需要创建一组相关对象,这些对象之间存在一定的依赖关系时,抽象工厂模式可以确保对象之间的一致性。
- 当系统需要在运行时选择创建不同类型的对象家族时,抽象工厂模式提供了一种方便的方式来实现这一目标。
- 当需要封装对象的创建细节,以减少客户端代码与具体对象的耦合时,抽象工厂模式提供了更高的抽象级别。
注意事项
在使用抽象工厂模式时,需要注意以下问题:
- 抽象工厂模式增加了系统的复杂性,因为它引入了多个工厂类和产品类。因此,只有当需要管理对象之间的复杂关系时才使用抽象工厂模式。
- 在增加新的产品类或工厂类时,需要同时更新抽象工厂接口和具体工厂类,这可能导致一些修改和维护成本。因此,抽象工厂模式适用于相对稳定的产品家族。
- 抽象工厂模式通常需要与其他设计模式结合使用,如单例模式、工厂方法模式等,以确保系统的稳定性和灵活性。
与工厂模式的区别
- 工厂模式(Factory Pattern)旨在创建单一类型的对象,通常通过一个工厂类来完成。工厂模式的关注点是如何创建一个对象,而不关心对象的家族或关系。工厂模式通常用于创建对象的封装和解耦。
- 抽象工厂模式(Abstract Factory Pattern)旨在创建一组相关的对象,这些对象之间存在某种关系,它们属于一个产品家族。抽象工厂模式提供一个接口,用于创建一组相关对象,而不关心具体的创建过程。抽象工厂模式的关注点是如何创建一组相关对象,以满足特定的需求。
工厂模式更侧重于单一对象的创建,而抽象工厂模式更适用于一组相关对象的创建。抽象工厂模式在产品家族的层次结构上提供了更高级别的抽象,可以用来管理多个产品家族 。这使得它适用于创建有关系的、互相协同工作的对象组
五、建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,旨在将复杂对象的构建过程与其表示分离,从而允许你创建不同类型的对象,同时保持相同的构建过程。这种模式适用于创建包含多个属性和选项的对象。
什么是建造者模式?
建造者模式的核心思想是将一个复杂对象的构建拆分为多个简单对象的构建过程,然后通过一个指导者(Director)来协调这些构建过程,最终组合成所需的复杂对象。建造者模式通常包括以下主要角色:
- 产品(Product):表示构建过程中的复杂对象。产品通常具有多个属性和选项。
- 抽象建造者(Builder):定义了用于构建产品的方法和属性。每个具体建造者都会实现这个接口以构建特定类型的产品。
- 具体建造者(Concrete Builder):实现了抽象建造者接口,负责构建具体的产品。
- 指导者(Director):协调具体建造者来构建产品,指导者知道如何调用具体建造者的方法来构建产品。
- 说明
- 建造者模式的核心思想是将对象的构建过程抽离出来,将其拆分为多个步骤,然后使用指导者来协调这些步骤,最终创建一个复杂对象。
- 这个模式的主要目标之一是将对象的构建与表示分离,以便创建不同类型的对象,同时保持相同的构建过程。这有助于提高代码的可读性、可维护性,以及创建复杂对象时的灵活性。
- 在建造者模式中,具体的建造过程由具体建造者类负责实现,而指导者类则协调这些具体建造者的操作,确保它们按正确的顺序执行,最终产生所需的复杂对象。这种分离和组合的方式使得建造者模式非常适用于构建具有多个属性和选项的对象,特别是在构建过程复杂且具有多种配置选项的情况下。
建造者模式的案例
ES5 建造者模式示例
// 步骤1: 创建产品
function Computer() {
this.cpu = "";
this.memory = "";
this.storage = "";
}
// 步骤2: 创建抽象建造者
function ComputerBuilder() {
this.computer = new Computer();
this.buildCPU = function(cpu) {
this.computer.cpu = cpu;
};
this.buildMemory = function(memory) {
this.computer.memory = memory;
};
this.buildStorage = function(storage) {
this.computer.storage = storage;
};
this.getResult = function() {
return this.computer;
};
}
// 步骤3: 创建具体建造者
const highEndComputerBuilder = new ComputerBuilder();
highEndComputerBuilder.buildCPU("Intel i7");
highEndComputerBuilder.buildMemory("16GB");
highEndComputerBuilder.buildStorage("512GB SSD");
const lowEndComputerBuilder = new ComputerBuilder();
lowEndComputerBuilder.buildCPU("Intel i3");
lowEndComputerBuilder.buildMemory("8GB");
lowEndComputerBuilder.buildStorage("256GB HDD");
// 步骤4: 创建指导者
function Director(builder) {
this.builder = builder;
this.construct = function() {
this.builder.buildCPU("Intel i5");
this.builder.buildMemory("8GB");
this.builder.buildStorage("256GB SSD");
};
}
const director = new Director(highEndComputerBuilder);
director.construct();
const highEndComputer = highEndComputerBuilder.getResult();
const lowEndComputer = lowEndComputerBuilder.getResult();
ES6 建造者模式示例
// 步骤1: 创建产品
class Computer {
constructor() {
this.cpu = "";
this.memory = "";
this.storage = "";
}
}
// 步骤2: 创建抽象建造者
class ComputerBuilder {
constructor() {
this.computer = new Computer();
}
buildCPU(cpu) {
this.computer.cpu = cpu;
return this;
}
buildMemory(memory) {
this.computer.memory = memory;
return this;
}
buildStorage(storage) {
this.computer.storage = storage;
return this;
}
getResult() {
return this.computer;
}
}
// 步骤3: 创建具体建造者
const highEndComputerBuilder = new ComputerBuilder();
highEndComputerBuilder
.buildCPU("Intel i7")
.buildMemory("16GB")
.buildStorage("512GB SSD");
const lowEndComputerBuilder = new ComputerBuilder();
lowEndComputerBuilder
.buildCPU("Intel i3")
.buildMemory("8GB")
.buildStorage("256GB HDD");
// 步骤4: 创建指导者
class Director {
constructor(builder) {
this.builder = builder;
}
construct() {
this.builder
.buildCPU("Intel i5")
.buildMemory("8GB")
.buildStorage("256GB SSD");
}
}
const director = new Director(highEndComputerBuilder);
director.construct();
const highEndComputer = highEndComputerBuilder.getResult();
const lowEndComputer = lowEndComputerBuilder.getResult
();
建造者模式的应用场景
建造者模式适用于以下情况:
- 创建一个复杂对象,这个对象有多个属性和选项,构建过程比较复杂。
- 需要创建不同类型的对象,但它们共享相同的构建步骤。
- 需要避免构造器(constructor)具有太多参数,以提高代码的可读性和可维护性。
- 希望通过指导者来控制构建过程,确保创建的对象是合法的。
建造者模式的注意事项
- 建造者模式将构建过程拆分为多个步骤,需要使用指导者来协调这些步骤。这可以增加代码的复杂性,因此只有在创建复杂对象时才使用它。
- 建造者模式的灵活性和可配置性使其适用于创建多种不同配置的对象,但也可能导致构建错误的对象,因此需要小心使用。
- 在实际应用中,建造者模式通常与其他模式(如工厂方法模式)一起使用,以满足更复杂的需求。
六、单例模式
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供全局访问点以获取该实例。单例模式的主要特点包括以下几点:
- 一个单例类只能有一个实例。
- 该实例必须是全局可访问的,通常通过一个公共的接口来获取。
- 单例类通常延迟实例化,即只有在第一次请求实例时才创建对象。
单例模式常用于以下情况:
- 需要全局访问某个对象的唯一实例,例如全局配置、日志记录器、数据库连接等。
- 需要确保某个类只有一个实例,以节省系统资源或确保数据一致性。
ES5 单例模式示例
var Singleton = (function () {
var instance;
function createInstance() {
// 私有构造函数
function SingletonObject() {
this.value = "I am the unique instance";
}
return new SingletonObject();
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
// 使用单例
var instance1 = Singleton.getInstance();
var instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true,两次获取的实例是相同的
ES6 单例模式示例
class Singleton {
constructor() {
if (!Singleton.instance) {
this.value = "I am the unique instance";
Singleton.instance = this;
}
return Singleton.instance;
}
}
// 使用单例
const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // true,两次获取的实例是相同的
单例模式的应用场景
-
全局配置对象:单例模式可用于创建一个全局配置对象,以保存应用程序的配置信息,并在整个应用中共享这些配置。
-
日志记录器:一个日志记录器对象通常只有一个实例,以便记录整个应用的日志。
-
数据库连接池:数据库连接是有限资源,单例模式可用于创建一个数据库连接池,确保连接池中只有一个实例,以提高资源利用率。
-
缓存管理器:单例模式可用于创建一个全局缓存管理器,用于缓存数据、页面片段或其他信息。
单例模式的注意事项
-
单例模式的目标是确保一个类只有一个实例。在多线程环境下,需要考虑线程安全性,可以使用互斥锁来保护实例化过程。
-
单例模式可能会引入全局状态,因此应慎重使用,确保它不会导致不必要的耦合和复杂性。
vuex的单例
- 单例状态容器:在
Vuex中,全局状态存储在一个单一的状态容器中,这个容器只有一个实例。这符合单例模式的核心思想,确保应用程序中只有一个状态容器。 - 全局访问:通过
store对象,你可以在应用的任何组件中访问全局状态。不管组件层次结构有多深,都可以轻松获取和修改全局状态,这与单例模式的全局可访问性相符。 - 唯一数据源:
Vuex鼓励将应用程序的状态存储在单一数据源中,以确保状态的一致性和可维护性。这符合单例模式的理念,即一个唯一的实例来存储和管理数据。 - 单例实例创建:在 Vue.js 应用程序的入口文件中,通常会创建一个
Vuexstore 的单例实例,这个实例将在整个应用程序中使用。这与单例模式中的单例对象创建一致。
简单的vuex的实现
let storeInstance = null;
class Store {
constructor() {
this.state = {
count: 0
};
}
increment() {
this.state.count++;
}
}
export function createStore() {
if (!storeInstance) {
storeInstance = new Store();
}
return storeInstance;
}
在js 中单例模式好多都是使用闭包实现
七、装饰器模式
装饰器模式是一种结构型设计模式,它允许向对象动态地添加新功能,同时不修改其结构。这种模式以对客户端透明的方式扩展对象的功能。通常情况下,装饰器模式通过创建一个包装对象来实现,在这个包装对象中维护了原始对象,并且可以在原始对象的基础上添加一些额外的行为。
装饰器模式的关键组成部分有:
-
Component(组件):定义一个接口,可以为这些对象动态地添加职责。 -
ConcreteComponent(具体组件):实现Component接口的类,可以被装饰。 -
Decorator(装饰器抽象类):继承Component接口,通常持有一个Component对象的引用,并可以通过该引用调用原始对象的方法,以扩展原始对象的功能。 -
ConcreteDecorator(具体装饰器):实现Decorator接口,通过装饰器来增强Component的功能。
下面我们分别用 ES5 和 ES6 来实现一个简单的装饰器模式示例,假设我们有一个基础的文本编辑器,我们想要为其添加特定的装饰器功能。
ES5 实现装饰器模式
// 定义基础文本编辑器
function TextEditor() {
this.text = '';
this.write = function (text) {
this.text += text;
};
}
// 定义装饰器基类
function Decorator(editor) {
this.editor = editor;
this.write = function (text) {
this.editor.write(text);
};
}
// 具体装饰器:加粗装饰器
function BoldDecorator(editor) {
Decorator.call(this, editor);
this.write = function (text) {
this.editor.write('<strong>' + text + '</strong>');
};
}
// 使用示例
var editor = new TextEditor();
var boldEditor = new BoldDecorator(editor);
boldEditor.write('This is bold text.');
console.log(editor.text); // Output: This is bold text.
ES6 实现装饰器模式
class TextEditor {
constructor() {
this.text = '';
}
write(text) {
this.text += text;
}
}
class Decorator {
constructor(editor) {
this.editor = editor;
}
write(text) {
this.editor.write(text);
}
}
class BoldDecorator extends Decorator {
constructor(editor) {
super(editor);
}
write(text) {
super.write(`<strong>${text}</strong>`);
}
}
// 使用示例
const editor = new TextEditor();
const boldEditor = new BoldDecorator(editor);
boldEditor.write('This is bold text.');
console.log(editor.text); // Output: This is bold text.
应用场景
装饰器模式常用于以下情况:
-
扩展功能:在不改变现有对象结构的情况下,动态地为对象添加功能。
-
多层装饰:可以通过多次装饰来实现复杂功能,每个装饰器独立,易于管理和扩展。
-
避免继承的静态特征:相比继承,装饰器模式更灵活,可以动态选择要装饰的对象。
注意事项
- 注意避免装饰器链过长,可能会影响性能和代码的可读性。
- 理解装饰器模式的目的和适用场景,不应滥用装饰器,应合理选择使用继承或装饰器模式。