我正在参加「掘金·启航计划」
简介
设计模式是针对复杂问题总结的一系列解决方案,分成创建型模式,结构性模式,行为模式三类,这里只介绍创建型模式。
单例模式
单例模式保证一个类只有一个实例,并提供一个访问该实例的全局节点。状态管理库 Vuex 的 store 单例就是使用到了单例模式。
class Singleton {
private static instance: Singleton;
public static getInstance(): Singleton {
if (!Singleton.instance) Singleton.instance = new Singleton();
return Singleton.instance;
}
}
console.log(Singleton.getInstance() === Singleton.getInstance()); // true
在实际的代码实现过程中,使用了私有静态变量 instance 来保存实例对象,使用了公开的静态方法 getInstance 来获取实例对象。运行上面的代码,可以发现最后得到的是 true,也就是说 Singleton 类的 getInstance 方法获取的实例对象是同一个,这是因为该方法第一次使用了 instance 变量保存创建了的实例对象,后面再次调用 getInstance 方法返回的就是 instance 保存的实例对象。
工厂方法模式
父类提供一个实例化对象的接口,由子类决定具体实例化的类。简单来说,就是委托其他对象来创建实例对象。
abstract class Product {
abstract say(): void;
}
class ConcreteProduct extends Product {
say(): void {
console.log("hehe");
}
}
abstract class Creator {
abstract factoryMethod(): Product;
someOperation() {
const product = this.factoryMethod();
product.say();
}
}
class ConcreteCreator extends Creator {
factoryMethod(): Product {
return new ConcreteProduct();
}
}
new ConcreteCreator().someOperation();
这里使用到了抽象类和抽象方法。其中抽象类不同于基础类,不能被实例化,也不同于接口,可以包含具体实现。抽象方法是继承类必须实现的方法,它只是签名没有具体实现,可以使用访问修饰符。
这里实例化 ConcreteCreator 的时候会调用继承的 someOperation 方法,执行 someOperation 方法的时候,会调用 factoryMethod 方法,该方法会实例化 ConcreteProduct 类,并获得里面的 say 方法。这也就符合工厂方法模式的特征,使用其他对象的方法来创建实例对象。
抽象工厂模式
该模式可以创建一系列相关的对象,而不需要知道对应的类。
interface AbstractFactory {
createProductA(): AbstractProductA;
createProductB(): AbstractProductB;
}
class ConcreteFactory implements AbstractFactory {
public createProductA(): AbstractProductA {
return new ConcreteProductA();
}
public createProductB(): AbstractProductB {
return new ConcreteProductB();
}
}
interface AbstractProductA {
usefulFunction(): void;
}
class ConcreteProductA implements AbstractProductA {
public usefulFunction() {
console.log("this is from productA");
}
}
interface AbstractProductB {
usefulFunction(): void;
anotherUsefulFunction(collaborate: AbstractProductA): void;
}
class ConcreteProductB implements AbstractProductB {
public usefulFunction() {
console.log("this is from productB");
}
public anotherUsefulFunction(collaborate: AbstractProductA) {
collaborate.usefulFunction();
this.usefulFunction();
}
}
function clientCode(factory: AbstractFactory) {
const productA = factory.createProductA();
productA.usefulFunction();
const productB = factory.createProductB();
productB.anotherUsefulFunction(productA);
}
clientCode(new ConcreteFactory());
创建 factory 对象之后,就可以调用 createProductA 方法和 createproductB 方法来获取相应的实例化对象。得到实例对象后就可以使用里面的 usefulFunction 方法,而 ConcreteProductB 类的实例化对象还拥有 anotherUsefulFunction 方法,这个方法可以创建其他产品对象。
建造者模式
该模式可以分步骤创建复杂对象
interface Builder {
producePartA(): void;
}
class ConcreteBuilder implements Builder {
private product: Product;
constructor() {
this.reset();
}
public reset() {
this.product = new Product();
}
public producePartA(): void {
this.product.parts.push("partA");
}
public getProducts() {
const res = this.product;
this.reset();
return res;
}
}
class Product {
public parts: string[] = [];
public listParts() {
console.log(`all products: ${this.parts.join(",")}`);
}
}
function clientCode() {
const builder = new ConcreteBuilder();
builder.producePartA();
builder.getProducts().listParts();
}
clientCode();
这里分成了 Product 和 ConcreteBuilder 两个类,其中 Product 类可以使用 parts 存放数据,listParts 方法可以查看 parts 里面的数据。而 ConcreteBuilder 类不但有实例化 Product 的方法,还有修改 Product 类中 parts 数组的方法。
原型模式
该模式可以复制原对象,并且新对象不会依赖原对象所属的类。
class Prototype {
public primitive: unknown;
public component: Object;
public circularComponent: ComponentWithBackReference;
clone(): this {
const clone = Object.create(this);
clone.component = Object.create(this.component);
clone.circularComponent = {
...this.circularComponent,
prototype: { ...this },
};
return clone;
}
}
class ComponentWithBackReference {
public prototype;
constructor(prototype: Prototype) {
this.prototype = prototype;
}
}
function clientCode() {
const p = new Prototype();
p.primitive = 2;
p.component = new Date();
p.circularComponent = new ComponentWithBackReference(p);
const p2 = p.clone();
console.log(p.primitive === p2.primitive);
console.log(p.component !== p2.component);
console.log(p.circularComponent !== p2.circularComponent);
console.log(p.circularComponent.prototype !== p2.circularComponent.prototype);
}
clientCode();
这里需要引用一些其他知识点,原始类型的值可以通过值判断是否相等,而对象类型的值需要判断是否指向同一地址。对象直接复制的话指向的是同一地址,因此为了创建新的对象需要做深拷贝。
这里克隆对象以及对象里面的对象使用了 Object.create 方法,而对于循环引用的对象,使用了扩展运算符对每一层进行了浅拷贝。
总结
以上五种创建型模式都各有特点,要灵活运用的话需要记住其中的区别。单例模式用于创建单个实例,工厂方法模式用来创建特定对象,抽象工厂模式可以创建一系列对象,原型模式可以克隆对象,建造者模式可以分步骤创建复杂对象。