设计模式-创建型
本文主要介绍下创建型设计模式,包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式,提供前端场景和 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
组件实例,使得组件的创建过程更加灵活和可扩展。 - 接口请求封装:对于不同的请求方式(如
GET
、POST
),可以使用工厂方法模式来封装不同的请求逻辑。
工厂方法模式-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