前端设计模式学习手册(更新中)

58 阅读5分钟

单例

某个系统只存在一个实例,同时提供集中、统一的访问接口

闭包实现单例
const Singleton = (function () {
  let instance;

  function createInstance() {
    return {
    // ...
    };
  }

  return {
    getInstance: function () {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    },
  };
})();
TypeScript 实现单例
class Singleton {
  private static instance: Singleton;
  public name: string;

  private constructor() {

  }

  static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }
}
模块化单例
// Singleton.js
const Singleton = {
  name: "Singleton Instance",
  createdAt: new Date(),
};

export default Singleton;
工厂函数+闭包实现单例
function SingletonFactory() {
  let instance = null;

  function Singleton() {
    if (instance) {
      return instance;
    }
    this.name = 'xxxx';
    instance = this;
  }

  return Singleton;
}

原型

原型模式的目的是从原型实例克隆出新的实例,对于那些有非常复杂的初始化过程的对象或者是需要耗费大量资源的情况,原型模式是更好的选择。

为什么不用new而使用原型克隆?
优点

原型克隆可以直接拷贝原型对象数据流生成的副本对象,不会再额外触发一些多余的复杂操作(类加载、实例化、初始化)

  1. 避免重复逻辑

    • 在许多场景下,new 初始化的构造函数中可能包含复杂的逻辑,如数据库连接或文件读取,克隆可以直接复制现有对象的状态,而不需要重复执行这些初始化逻辑。
  2. 节省性能开销

    • 克隆时可以跳过部分初始化步骤(如重新分配默认值或执行构造逻辑),在需要频繁创建相似对象的场景下,克隆会更高效。
  3. 定制化对象复制

    • 通过克隆,开发者可以更灵活地控制哪些属性被复制、哪些需要修改,而不是受限于构造函数的逻辑。
缺点
  1. 浅拷贝问题

    • 如果原型对象包含引用类型的字段,浅拷贝会导致新对象共享这些字段,可能产生副作用。
    • 需要手动实现深拷贝,增加复杂度。
  2. 代码复杂度

    • 对于复杂对象,维护克隆逻辑可能比构造函数初始化更复杂。
原型实现

使用 Object.create(proto) 方法创建一个以 proto 为原型的新对象,新对象继承了原型对象的所有属性和方法。

// 定义一个原型对象
const AnimalPrototype = {
    speak() {
        console.log(`${this.name} makes a noise.`);
    },
    initialize(name) {
        this.name = name;
    }
};

// 创建一个函数来生成新对象
function createAnimal(name) {
    const animal = Object.create(AnimalPrototype); // 创建一个以 AnimalPrototype 为原型的对象
    animal.initialize(name); // 初始化对象属性
    return animal;
}

// 创建对象实例
const cat = createAnimal('Cat');
cat.speak(); // 输出: Cat makes a noise.

// 扩展原型方法
AnimalPrototype.sleep = function () {
    console.log(`${this.name} is sleeping.`);
};

// 新实例继承扩展的方法
cat.sleep(); // 输出: Cat is sleeping.

工厂

对工厂制造方法进行接口规范化,以允许子类工厂决定具体制造哪类产品实例,最终降低系统耦合,使系统的可维护性、可扩展性得到提升。

工厂方法各角色定义: 举例:动物园动物、猴子、大象

  • 产品 : 所有产品的的顶级父类,如动物园动物
  • 子产品:由产品类Product派生出的产品子类,如猴子、大象
  • 工厂接口:定义工厂方法的工厂接口,可以是抽象类,它使顶级工厂制造方法抽象化、标准统一化。如动物叫这个方法
  • 工厂实现:实现了工厂接口的工厂实现类,并决定工厂方法中具体返回哪种产品子类的实例。如猴子返回猴子叫,大象返回大象叫
简单工厂
function ProductA() {
    this.type = "A";
    this.sayType = function () {
        console.log("I am Product A");
    };
}

function ProductB() {
    this.type = "B";
    this.sayType = function () {
        console.log("I am Product B");
    };
}

function Factory(type) {
    if (type === "A") {
        return new ProductA();
    } else if (type === "B") {
        return new ProductB();
    } else {
        throw new Error("Invalid product type");
    }
}
// 使用 
const productA = Factory("A"); 
productA.sayType(); // 输出:I am Product A 
const productB = Factory("B"); 
productB.sayType(); // 输出:I am Product B
抽象工厂

抽象工厂是对工厂的抽象化,为了满足不同用户对产品的多样化需求,工厂不会只局限于生产一类产品。抽象工厂将各种产品分门别类,基于此来规划各种工厂制造接口。

// 产品类
class MacButton {
    render() {
        console.log("Rendering a Mac-style button");
    }
}

class WindowsButton {
    render() {
        console.log("Rendering a Windows-style button");
    }
}

class MacForm {
    render() {
        console.log("Rendering a Mac-style form");
    }
}

class WindowsForm {
    render() {
        console.log("Rendering a Windows-style form");
    }
}

// 抽象工厂
class UIFactory {
    static createFactory(os) {
        if (os === "Mac") {
            return new MacUIFactory();
        } else if (os === "Windows") {
            return new WindowsUIFactory();
        } else {
            throw new Error("Unknown OS type");
        }
    }
}

// 具体工厂
class MacUIFactory {
    createButton() {
        return new MacButton();
    }
    createForm() {
        return new MacForm();
    }
}

class WindowsUIFactory {
    createButton() {
        return new WindowsButton();
    }
    createForm() {
        return new WindowsForm();
    }
}

// 使用
const macFactory = UIFactory.createFactory("Mac");
const macButton = macFactory.createButton();
macButton.render(); // 输出:Rendering a Mac-style button

const windowsFactory = UIFactory.createFactory("Windows");
const windowsForm = windowsFactory.createForm();
windowsForm.render(); // 输出:Rendering a Windows-style form

对比
工厂原型
通过制造新对象来生成实例(新建对象)。通过克隆已有对象来生成实例(复制对象)。

建造者

与工厂模式不同的是,建造者模式主要目的在于把繁琐的构建过程从不同对象中抽离出来,使其脱离并独立于产品类与工厂类,最终实现用同一套标准的制造工序能够产出不同的产品。

建造者模式的结构
  1. 产品类(Product): 这是需要被创建的复杂对象。通常具有多个组成部分。
  2. 建造者接口(Builder): 定义创建产品不同部分的方法。
  3. 具体建造者(ConcreteBuilder): 实现建造者接口,提供构建产品部件的具体逻辑。
  4. 指挥者(Director): 控制建造的过程。通过调用建造者的不同方法,构建最终的产品。
classDiagram
    direction TB

    class Director {
        + construct(Builder): void
        + direct(): Product
    }

    class Builder {
        <<Interface>>
        + buildPart(): void
        + getProduct(): Product
    }

    class ConcreteBuilder {
        + buildPart(): void
        + getProduct(): Product
    }

    class Product {
    }

    Director --> Builder
    Builder <|.. ConcreteBuilder
    ConcreteBuilder --> Product

// 产品类:房子
class House {
  constructor() {
    this.type = null;      // 房子类型
    this.wallColor = null; // 墙壁颜色
    this.windows = 0;      // 窗户数量
    this.roofStyle = null; // 屋顶风格
  }

  // 打印房子的描述
  describe() {
    console.log(`This is a ${this.type} house with ${this.wallColor} walls, ${this.windows} windows, and a ${this.roofStyle} roof.`);
  }
}

// 抽象建造者类
class HouseBuilder {
  constructor() {
    this.house = new House();
  }

  setType(type) {
    this.house.type = type;
    return this;
  }

  setWallColor(color) {
    this.house.wallColor = color;
    return this;
  }

  setWindows(windows) {
    this.house.windows = windows;
    return this;
  }

  setRoofStyle(style) {
    this.house.roofStyle = style;
    return this;
  }

  build() {
    return this.house;
  }
}

// 别墅建造者类
class VillaBuilder extends HouseBuilder {
  constructor() {
    super();
  }

  build() {
    return this.setType("villa")
      .setWallColor("white")
      .setWindows(10)
      .setRoofStyle("gabled")
      .house;
  }
}

// 公寓建造者类
class ApartmentBuilder extends HouseBuilder {
  constructor() {
    super();
  }

  build() {
    return this.setType("apartment")
      .setWallColor("gray")
      .setWindows(6)
      .setRoofStyle("flat")
      .house;
  }
}

// 指挥者类
class Director {
  static construct(builder) {
    return builder.build();
  }
}

// 使用指挥者构造别墅
const villa = Director.construct(new VillaBuilder());
villa.describe(); // 输出:This is a villa house with white walls, 10 windows, and a gabled roof.

// 使用指挥者构造公寓
const apartment = Director.construct(new ApartmentBuilder());
apartment.describe(); // 输出:This is an apartment house with gray walls, 6 windows, and a flat roof.

// 自定义建造过程
const customBuilder = new HouseBuilder();
const customHouse = customBuilder
  .setType("cottage")
  .setWallColor("yellow")
  .setWindows(4)
  .setRoofStyle("thatched")
  .build();
customHouse.describe(); // 输出:This is a cottage house with yellow walls, 4 windows, and a thatched roof.