设计模式之五种创建型模式(typescript 实现)

95 阅读4分钟

我正在参加「掘金·启航计划」

简介

设计模式是针对复杂问题总结的一系列解决方案,分成创建型模式,结构性模式,行为模式三类,这里只介绍创建型模式。

单例模式

单例模式保证一个类只有一个实例,并提供一个访问该实例的全局节点。状态管理库 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 方法,而对于循环引用的对象,使用了扩展运算符对每一层进行了浅拷贝。

总结

以上五种创建型模式都各有特点,要灵活运用的话需要记住其中的区别。单例模式用于创建单个实例,工厂方法模式用来创建特定对象,抽象工厂模式可以创建一系列对象,原型模式可以克隆对象,建造者模式可以分步骤创建复杂对象。

参考资料

  1. refactoringguru.cn/design-patt…
  2. chat.openai.com/