设计模式-创建型

16 阅读6分钟

设计模式-创建型

本文主要介绍下创建型设计模式,包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式,提供前端场景和 ES6 代码的实现过程。 供自己以后查漏补缺,也欢迎同道朋友交流学习。

引言

设计模式是面试中比较常问的知识点,根据分类主要分为创建型结构型行为型。通过这些模式,开发者可以更好地理解问题,并找到合适的解决方案。

本文主要介绍下创建型设计模式,包括单例模式工厂方法模式抽象工厂模式建造者模式原型模式,提供前端场景和 ES6 代码的实现过程。

什么是创建型

创建型模式(Creational Patterns)主要关注对象的创建过程。这些模式提供了创建对象的最佳方式,同时隐藏了创建对象的具体细节,通常用于解决对象创建过程中的一些常见问题,如对象的复杂性、对象的复用、对象的依赖关系等,使得代码更加简洁、灵活和易于维护。

单例模式(Singleton)

单例模式确保一个类只有一个实例,并提供一个全局访问点。通过这种方式,可以避免重复创建实例,从而节省资源并保持全局状态的一致性。

前端中的单例模式场景

  • 全局状态管理:例如 Vuex 这种全局状态管理就使用的单例模式,确保状态的唯一性和一致性。
  • 事件总线:在前端开发中,事件总线通常用于组件之间的通信,使用单例模式确保事件总线的唯一性。
  • 全局缓存:在前端开发中,全局缓存通常用于存储一些静态数据,例如图片、字体等,使用单例模式确保数据的唯一性。

单例模式-JS实现

ES6 中,可以通过类的静态属性来实现单例模式。

class Singleton {
  constructor() {
    if (!Singleton.instance) {
      Singleton.instance = this
    }

    return Singleton.instance
  }
}

const instance1 = new Singleton();
const instance2 = new Singleton();

console.log(instance1 === instance2); // true

工厂方法模式(Factory Method)

工厂方法模式定义了一个用于创建对象的接口,但由子类决定要实例化的类是哪一个。

工厂方法让类的实例化推迟到子类进行,从而将对象的创建过程与使用过程分离,提高了代码的灵活性和可扩展性。

前端中的工厂方法模式场景

  • 组件库:可以使用工厂方法模式来创建不同的 UI 组件实例,使得组件的创建过程更加灵活和可扩展。
  • 接口请求封装:对于不同的请求方式(如 GETPOST),可以使用工厂方法模式来封装不同的请求逻辑。

工厂方法模式-JS实现

// 定义工厂类
class Factory {
  create(type) {
    if (type === 'A') {
      return new createA()
    } else if (type === 'B') {
      return new createB()
    }
  }
}

const factory = new Factory();
const factoryA = factory.create('A');
const factoryB = factory.create('B');

抽象工厂模式(Abstract Factory)

抽象工厂模式(Abstract Factory Pattern)提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。

抽象工厂模式的核心思想是将产品的创建过程封装起来,使得代码与具体产品的创建过程解耦

前端中的抽象工厂模式场景

  • UI组件库:一个应用可能需要支持多种主题风格(如暗色模式和亮色模式),每种风格都有一套相关的 UI 组件(如按钮、输入框等),可以通过抽象工厂模式来创建不同风格的组件。
  • 跨平台应用:在开发跨平台的前端应用时,可以使用抽象工厂模式来创建适应不同平台的 UI 组件,确保组件在不同平台上的风格和行为一致性。

抽象工厂模式-JS实现

// 抽象产品接口
class AbstractButton {
  create() {
    throw new Error("抽象方法不能调用");
  }
}

class AbstractInput {
  create() {
    throw new Error("抽象方法不能调用");
  }
}

// 具体产品类
class DarkButton extends AbstractButton {
  create() {
    console.log("创建暗色模式按钮");
  }
}

class DarkInput extends AbstractInput {
  create() {
    console.log("创建暗色模式输入框");
  }
}

class LightButton extends AbstractButton {
  create() {
    console.log("创建亮色模式按钮");
  }
}

class LightInput extends AbstractInput {
  create() {
    console.log("创建亮色模式输入框");
  }
}

// 抽象工厂接口
class AbstractFactory {
  createButton() {
    throw new Error("抽象方法不能调用");
  }

  createInput() {
    throw new Error("抽象方法不能调用");
  }
}

// 具体工厂类
class DarkThemeFactory extends AbstractFactory {
  createButton() {
    return new DarkButton();
  }

  createInput() {
    return new DarkInput();
  }
}

class LightThemeFactory extends AbstractFactory {
  createButton() {
    return new LightButton();
  }

  createInput() {
    return new LightInput();
  }
}

// 客户端代码
function createUI(factory) {
  const button = factory.createButton();
  const input = factory.createInput();
  button.create();
  input.create();
}

const darkFactory = new DarkThemeFactory();
const lightFactory = new LightThemeFactory();

createUI(darkFactory); // 创建暗色模式的按钮和输入框
createUI(lightFactory); // 创建亮色模式的按钮和输入框

建造者模式(Builder)

建造者模式(Builder Pattern)将一个复杂对象的构建与它的表示进行分离,使得同样的构建过程可以创建不同的表示。

这种模式主要用于创建包含多个组成部分的复杂对象,通过将对象的构建过程分解为多个步骤,使得客户端可以更灵活地控制对象的创建过程,同时隐藏对象的内部表示细节。

前端中的建造者模式场景

  • HTML文档生成:在前端开发中,可以使用建造者模式来生成复杂的 HTML 文档结构。例如,一个表单生成器可以根据不同的配置和需求,逐步构建出包含多个输入字段按钮等元素的表单。
  • 组件配置:对于一些可配置的复杂组件,如图表、地图等,可以使用建造者模式来逐步设置组件的各个属性和行为,使得组件的配置过程更加灵活和可读。

建造者模式-JS实现

// 产品类
class Product {
  constructor() {
    this.parts = [];
  }

  add(part) {
    this.parts.push(part);
  }

  show() {
    console.log("产品包含以下部分:");
    this.parts.forEach((part) => {
      console.log(part);
    });
  }
}

// 抽象建造者接口
class Builder {
  constructor() {
    this.product = new Product();
  }

  buildPartA() {
    throw new Error("抽象方法不能调用");
  }

  buildPartB() {
    throw new Error("抽象方法不能调用");
  }

  getResult() {
    return this.product;
  }
}

// 具体建造者类
class ConcreteBuilder extends Builder {
  buildPartA() {
    this.product.add("部分A");
  }

  buildPartB() {
    this.product.add("部分B");
  }
}

// 指挥者类
class Director {
  constructor(builder) {
    this.builder = builder;
  }

  construct() {
    this.builder.buildPartA();
    this.builder.buildPartB();
  }
}

// 客户端代码
const builder = new ConcreteBuilder();
const director = new Director(builder);

director.construct();
const product = builder.getResult();
product.show();
// 输出:
// 产品包含以下部分:
// 部分A
// 部分B

原型模式(Prototype)

原型模式通过拷贝现有的实例来创建新的实例,而不是通过新建实例。

原型模式的关键在于实现对象的克隆功能,通常通过实现一个克隆接口来完成。

前端中的原型模式场景

  • UI组件克隆:一个表格组件可能需要多个相似的行或列,通过克隆现有的行或列实例来创建新的实例,可以提高组件的渲染效率。
  • 数据模型克隆:在一个表单应用中,用户可能需要多次填写类似的表单数据,通过克隆现有的数据模型对象来创建新的表单数据对象,可以减少数据初始化的开销。

原型模式-JS实现

// 定义一个可克隆的对象
class Prototype {
  constructor(name, category) {
    this.name = name;
    this.category = category;
  }

  clone() {
    return Object.assign({}, this);
  }
}

// 创建一个原型对象
const original = new Prototype('Original', '类型1');

// 克隆原型对象
const cloned = original.clone();

console.log(cloned);
// {name: "Original", category: "类型1"}
console.log(original === cloned);
// false

设计模式学习专栏系列

项目地址