javascript 设计模式

115 阅读20分钟

设计模式

概念

设计模式是在软件设计过程中面对重复出现的问题所提出的通用解决方案。

作用

使用设计模式的好处是可以提高代码的可复用性、提供了经过验证的解决方案、提高了代码的可维护性。

Javascript中常见的设计模式

创建型模式:

  • 工厂方法模式(Factory Method)
  • 抽象工厂模式(Abstract Factory)
  • 建造者模式(Builder)
  • 原型模式(Prototype)
  • 单例模式(Singleton)

结构型模式:

  • 适配器模式(Adapter)
  • 桥接模式(Bridge)
  • 组合模式(Composite)
  • 装饰者模式(Decorator)
  • 外观模式(Facade)
  • 享元模式(Flyweight)
  • 代理模式(Proxy)

行为型模式:

  • 链式操作模式(Chain Of Responsibility)
  • 命令模式(Command)
  • 迭代器模式(Iterator)
  • 中介者模式(Mediator)
  • 备忘录模式(Memento)
  • 观察者模式(Observer)
  • 状态模式(State)
  • 策略模式(Strategy)
  • 模板方法模式(Template Method)
  • 访问者模式(Visitor)
  • 解释器模式(Interpreter)

接下来我会用javascript构建demo的方式和大家一起去了解以上的设计模式。

工厂方法模式(Factory Method)

概念

定义一个用于创建对象的接口,让实现这个接口的类来决定实例化哪一个类。工厂方法让类的实例化推迟到子类中进行。

特点

  • 工厂方法函数是可以生成对象的函数,但不需要显示指定创建对象的类。
  • 调用者只需知道传入工厂方法的参数,无需关心如何创建对象。
  • 通过工厂方法创建的对象通常具有一定的共同功能。
  • 可以通过继承工厂方法函数来实现多态性。

代码

class Car {
    constructor(name){
        this.name = name
    }

    drive(){
        console.log('driving...')
        console.log(this.name)
    }
}

class Audi extends Car {
    constructor(){
        super('Audi')
    }
}

class Benz extends Car{
    constructor(){
        super('Benz');
    }
}

function carFactory(type){
    let car;
    if(type==='audi'){
        car = new Audi();
    }else if(type === 'benz'){
        car = new Benz();
    }

    return car;
}

const audi = carFactory('audi')
const benz = carFactory('benz')

audi.drive()
benz.drive()

抽象工厂模式(Abstract Factory)

概念

是一种创建型设计模式,旨在提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定他们的具体类。它提供了一种方法来封装对象的创建过程,使得客户端代码与具体类的实例化解耦,从而使系统更具灵活性和可拓展性。

特点

  • 抽象工厂(Abstact Factory): 定义一个接口,用于创建一系列相关产品。抽象工厂中通常包含多个方法,每个方法用于创建不同类型的产品。
  • 具体工厂(Concrete Factory): 实现抽象工厂接口,负责实际创建具体产品的实例。
  • 抽象产品(Abstact Product): 定义一类产品的接口。抽象产品通常对应于一组相关产品,其具体实现由具体产品类提供。
  • 具体产品(Concrete Product): 实现抽象产品接口,表示实际的产品对象。

代码

javascript中没有严格的接口概念,但是我们可以通过函数和对象的灵活性来实现类似的模式。

// 抽象产品
class Button {
    constructor(text){
        this.text = text;
    }

    render(){
        console.log(`Rendering a button with text: ${this.text}`)
    }
}


// 具体产品
class WindowsButton extends Button {
    render(){
        console.log(`Rendering a Windows button with text: ${this.text}`);
    }
}

class MacOSButton extends Button {
    render(){
        console.log(`Rendering a Windows button with text: ${this.text}`);
    }
}

// 抽象工厂
class GUIFactory{
    createButton(text){
        throw new Error("Method createButton() must be implementd.");
    }
}

class WindowsFactory extends GUIFactory {
    createButton(text){
        return new WindowsButton(text);
    }
}

class MacOSFactory extends GUIFactory {
    createButton(text){
        return new WindowsButton(text);
    }
}

//客户端代码
function createUI(factory, buttonText){
    const button = factory.createButton(buttonText)
    button.render();
}

const windowsFactory = new WindowsFactory();
const macosFactory = new MacOSFactory()

createUI(windowsFactory, "OK"); // Rendering a Windows button with text: OK
createUI(macosFactory, "Cancel"); // Rendering a macOS button with text: Cancel

建造者模式(Builder)

概念

建造者模式旨在通过将复杂对象的构建过程与其表示分离,以便以逐步方式创建不同类型的对象。这种模式对于创建具有多个部分和配置选项的复杂对象特别有用,同时也能够提供更好的代码可读性和可维护性。

特点

建造者模式的主要目标是将对象的构建步骤解耦,使得可以在不同的上下文中使用相同的构建步骤来构建不同的对象变体。

  • 产品(Product):表示最终构建出的复杂对象,该对象可能由多个部分组成。
  • 抽象建造者(Abstract Builder):定义构建产品的抽象接口,包括构建各个部分的方法。这个接口可以是一个类或者一组函数。
  • 具体建造者(Concrete Builder):实现抽象建造者接口,负责实际的构建步骤,以及返回最终构建好的产品。具体建造者可以有不同的变体,每个变体可以构建出不同类型的产品。
  • 指导者(Director):可选的组件,负责控制构建过程的顺序和步骤,通常与客户端代码交互来创建最终的产品。
优势
  • 分离关注点: 将构建步骤从客户端代码中分离出来,使得客户端不需要关心对象的构建细节。
  • 支持不同变体: 通过具体建造者的不同实现,可以创建不同类型的产品变体,而不必改变客户端代码。
  • 代码可读性和可维护性: 建造者模式使构建过程更加清晰,有助于代码的理解和维护

代码

// 产品类
    class Computer{
        constructor(){
            this.cpu = "";
            this.memory = "";
            this.storage = "";
        }
        toString(){
            return `CPU: ${this.cpu}, Memory: ${this.memory}, Storage: ${this.storage}`;
        }
    }

    // 建造者类
    class CompouterBuilder{
        constructor(){
            this.computer = new Computer();
        }


        buildCPU(cpu){
            this.computer.cpu = cpu;
            return this;
        }

        buildMemory(memory){
            this.computer.memory = memory;
            return this;
        }

        buildStorage(storage){
            this.computer.storage = storage;
            return this
        }

        getResult(){
            return this.computer;
        }
    }
    // 指导者类
    class Director{
        constructor(builder){
            this.builder = builder;
        }
        construct(){
            this.builder.buildCPU("Intel i7").buildMemory("16GB").buildStorage("512GB SSD");
        }
    }
    // 客户端代码
    const builder = new CompouterBuilder();
    const director = new Director(builder);
    director.construct();
    const computer = builder.getResult();
    console.log("Computer built:", computer.toString());

原型模式(Prototype)

概念

原型设计模式是一种创建型设计模式,它允许你通过复制(克隆)现有对象来创建新对象,而不需要从头开始重新创建。这种模式通过使用原型实例作为创建对象的模板,从而避免了构造函数的开销和对象创建的复杂性。

特点

  • javascript本身就具有原型继承的特性。
  • 每个对象都有一个内部属性[[Prototype]],它指向另一个对象
  • 当你访问一个对象的属性时,如果对象本身没有这个属性,javascript会沿着原型链向上查找,直到找到对应的属性或者到达原型链的顶部(通常是Object.prototype)。

代码

原型模式在javascript中的应用非常简单,比如我们可以使用现有对象作为原型,通过克隆来创新对象,而不必显示地指定每个属性。这在创建多个相似对象时非常有用,只需要定义一次共享的属性和方法。

    //原型对象
    const carPrototype = {
        init(make, model){
            this.make = make;
            this.model = model;
        },
        dirve(){
            console.log(`Driving ${this.make} ${this.model}`);
        }
    }

    //创建新对象并克隆原型
    const car1 = Object.create(carPrototype);
    car1.init("Toyota","Camry");

    const car2 = Object.create(carPrototype);
    car2.init("Honda","Accord");

    car1.dirve();
    car2.dirve();

单例模式(Singleton)

概念

单例模式(Singleton Pattern)是一种创建型设计模式,用于确保一个类只有一个实例,并提供全局访问点来访问该实例。

特点

  • 全局唯一实例: 单例模式确保一个类只有一个实例,无论在何处调用,始终返回同一个实例。这需要对共享状态或资源的情况特别有用。
  • 延迟实例化: 单例模式允许延迟实例化,即在第一次请求实例时才创建它。这可以节省资源,避免不必要的对象创建。
  • 全局访问点: 单例模式提供了一个全局的访问点,可以在代码中的任何地方获取到单例实例。这样可以方便的访问和使用该实例。
  • 私有实例: 通过闭包等机制,单例实例通常可以隐藏在私有范围内,防止直接访问或修改。
  • 共享状态: 单例模式适用于需要在多个部分之间共享状态或数据的场景,因为只有一个实例,所以共享的状态可以在不同的地方被访问和修改。
  • 模块化思想: 单例实例与模块化开发思想相契合,可以将相关功能封装在单例实例中,使代码更加组织化和模块化。
  • 灵活性: 尽管单例模式确保只有一个实例,但它可以在需要时扩展,以满足不同的需求。
  • 容易理解和维护: 单例模式可以使代码更加清晰,因为它可以将对象的创建管理过程集中在一个地方,使代码易于理解和维护。

代码

    const Sigleton = (function(){
        let instance;

        function createInstance(){
            //创建实例的逻辑
            return {
                getInstanceInfo(){
                    return "This is the singleton instance.";
                }
            }
        }

        return {
            getInstance(){
                if(!instance){
                    instance = createInstance();
                }
                return instance;
            }
        }
    })();

    //获取单例实例
    const instance1 = Sigleton.getInstance();
    const instance2 = Sigleton.getInstance();

    console.log(instance1 === instance2);
    console.log(instance1.getInstanceInfo());
    console.log(instance2.getInstanceInfo());

适配器模式(Adapter)

概念

主要通过封装一个类来解决接口不兼容的问题,使得原本不匹配的接口可以正常工作。

特点

  • 转换接口: 适配器模式可以转换一种接口适配另一种接口,使得原本因接口不兼容而无法工作的两个类可以一起工作。
  • 增加透明性: 适配器模式使客户端意识不到它在使用一个与自己要求不匹配的接口,增加了透明性。
  • 增加复用性: 适配器类可以复用旧的类,无需修改旧类的代码,同时支持新的接口。
  • 符合开闭原则: 适配器模式使新的接口能在不改变原有代码的情况下实现复用,符合开闭原则。
  • 单一职责: 适配器仅负责接口转换,目标类依然关注业务本身,职责划分清晰。
  • 增强灵活性: 可以通过使用不同的适配器来适配不同的目标类,增加程序的灵活性。
  • 无侵入性: 适配器模式不需要改变目标类的代码,就可以实现接口适配,无侵入性。

代码

定义适配器类,这个类包装不匹配的目标类( adaptee )。 适配器类实现目标接口,内部将请求转换并委托给被适配的类处理。 客户端通过适配器类接口与目标接口相通信,而不需要知道被适配类的接口

// 旧的接口
class OldSystem {
  request() {
    return 'Old System Response';
  }
}

// 新的接口
class NewSystem {
  getResponse() {
    return 'New System Response';
  }
}

// 适配器将旧的接口转换成新的接口
class Adapter {
  constructor(oldSystem) {
    this.oldSystem = oldSystem;
  }

  getResponse() {
    return this.oldSystem.request();
  }
}

// 使用适配器来统一接口
function clientCode(system) {
  console.log(system.getResponse());
}

const oldSystem = new OldSystem();
const adapter = new Adapter(oldSystem);
clientCode(adapter);

const newSystem = new NewSystem();
clientCode(newSystem);

桥接模式(Bridge)

概念

用于将抽象部分与其实现部分分离,使它们可以独立变化。桥接模式通过组合的方式,将不同维度的变化分离开来,以减少类之间的耦合性。这使得你可以修改抽象部分和实现部分的其中一个而不影响另一个部分。

特点

  • 抽象和实现之间的解耦: 当一个类存在多个维度的变化(例如,形状和颜色),桥接模式可以将这些维度的变化分离,避免类的爆炸性增长。
  • 不同层次的继承: 使用继承来处理多维度的变化可能会导致类层次结构的复杂性,而桥接模式可以减少这种复杂性。
  • 实现部分可以独立变化: 桥接模式允许抽象和实现部分可以独立地进行扩展和变化,从而更容易适应未来的需求变化。

代码

// 实现部分
class Color {
  constructor(name) {
    this.name = name;
  }
}

class RedColor extends Color {
  constructor() {
    super("Red");
  }
}

class BlueColor extends Color {
  constructor() {
    super("Blue");
  }
}

// 抽象部分
class Shape {
  constructor(color) {
    this.color = color;
  }

  draw() {
    console.log(`Drawing a ${this.color.name} ${this.constructor.name}`);
  }
}

class Circle extends Shape {
  draw() {
    console.log(`Drawing a ${this.color.name} Circle`);
  }
}

class Square extends Shape {
  draw() {
    console.log(`Drawing a ${this.color.name} Square`);
  }
}

// 使用桥接模式
const redCircle = new Circle(new RedColor());
const blueSquare = new Square(new BlueColor());

redCircle.draw();   // Drawing a Red Circle
blueSquare.draw();  // Drawing a Blue Square

组合模式(Composite)

概念

组合模式(Composite Pattern)是一种结构性设计模式,它允许你将对象组织成树状结构以表示部分-整体的层次关系。组合模式使得单个对象(叶节点)和对象的组合(容器节点)可以被一致地处理,从而使得客户端代码可以透明地操作单个对象和组合对象,而不需要区分它们的具体类型。

特点

  • 部分-整体关系: 组合模式用于描述对象之间的部分-整体关系,将对象组织成树状结构,其中每个节点可以是单个对象或者其他的组合对象。
  • 透明性: 组合模式使得客户端可以统一地对待单个对象和组合对象,即使在代码层面不区分它们的具体类型。这就是组合模式的透明性特点。
  • 统一接口: 在组合模式中,所有的节点都实现了相同的接口,使得客户端可以通过相同的方式操作所- 有的对象,无论是叶节点还是组合节点。
  • 递归结构: 组合模式的实现通常会使用递归,通过递归访问整个树状结构的节点,实现了一致性的操作。
  • 简化客户端代码: 组合模式可以减少客户端代码的复杂性。客户端不需要关心对象的具体层次结构,只需要调用统一的接口方法即可。
  • 灵活性: 组合模式使得你可以轻松地添加新的对象类型,而无需修改现有的客户端代码。这使得你的代码更加灵活和可扩展。
  • 复杂对象组织: 组合模式适用于处理包含嵌套、复杂的对象组织关系的问题,例如树状结构。 代码复用: 组合模式可以在不同的场景中重复使用,因为它提供了一种通用的方式来处理对象组织关系。

代码


class TreeNode {
  constructor(name) {
    this.name = name;
    this.children = [];
  }

  add(child) {
    this.children.push(child);
  }

  remove(child) {
    const index = this.children.indexOf(child);
    if (index !== -1) {
      this.children.splice(index, 1);
    }
  }

  display(indent = 0) {
    console.log(' '.repeat(indent) + this.name);
    for (const child of this.children) {
      child.display(indent + 2);
    }
  }
}

// 创建树状结构
const root = new TreeNode('Root');
const branch1 = new TreeNode('Branch 1');
const branch2 = new TreeNode('Branch 2');
const leaf1 = new TreeNode('Leaf 1');
const leaf2 = new TreeNode('Leaf 2');
const leaf3 = new TreeNode('Leaf 3');

root.add(branch1);
root.add(branch2);
branch1.add(leaf1);
branch1.add(leaf2);
branch2.add(leaf3);

// 显示整个树状结构
root.display();

装饰者模式(Decorator)

概念

装饰者模式(Decorator Pattern)是一种结构性设计模式,它允许你在不改变已有对象的基础上,动态地将新功能附加到对象上。这种模式通过将对象包装在装饰器对象中,以实现不同层次的功能叠加。

特点

  • 动态添加功能: 当你需要在运行时动态地为对象添加新的功能或行为,而不是通过继承或修改现有类来实现。
  • 避免类爆炸: 装饰者模式可以减少类的数量,避免创建大量的子类来处理不同组合的功能。
  • 开放封闭原则: 装饰者模式遵循开放封闭原则,即对扩展是开放的,对修改是封闭的

代码

// 原始组件
class Coffee {
  cost() {
    return 5;
  }
}

// 装饰器基类
class CoffeeDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }

  cost() {
    return this.coffee.cost();
  }
}

// 具体装饰器类
class MilkDecorator extends CoffeeDecorator {
  constructor(coffee) {
    super(coffee);
  }

  cost() {
    return this.coffee.cost() + 2;
  }
}

class SugarDecorator extends CoffeeDecorator {
  constructor(coffee) {
    super(coffee);
  }

  cost() {
    return this.coffee.cost() + 1;
  }
}

// 使用装饰器
const plainCoffee = new Coffee();
const coffeeWithMilk = new MilkDecorator(plainCoffee);
const coffeeWithMilkAndSugar = new SugarDecorator(coffeeWithMilk);

console.log("Plain Coffee Cost:", plainCoffee.cost());  // Plain Coffee Cost: 5
console.log("Coffee with Milk Cost:", coffeeWithMilk.cost());  // Coffee with Milk Cost: 7
console.log("Coffee with Milk and Sugar Cost:", coffeeWithMilkAndSugar.cost());  // Coffee with Milk and Sugar Cost: 8

外观模式(Facade)

概念

外观模式(Facade Pattern)是一种结构性设计模式,它提供了一个统一的接口,用于访问子系统中的一组接口。外观模式可以将复杂的子系统隐藏在一个简单的接口背后,使得客户端可以更轻松地与子系统进行交互,同时减少了客户端和子系统之间的直接耦合。

特点

  • 简化复杂系统: 当系统包含多个组件、子系统或接口时,外观模式可以将这些复杂性隐藏起来,提供一个更简单的接口供客户端使用。
  • 提供统一接口: 外观模式可以将多个相关但不同的接口封装成一个统一的接口,使客户端更容易理解和使用。
  • 降低耦合度: 外观模式可以降低客户端与系统的耦合度,因为客户端不需要了解系统的内部结构。

代码

class ShoppingCart {
  addItem(item) {
    console.log(`Added item: ${item}`);
  }

  removeItem(item) {
    console.log(`Removed item: ${item}`);
  }

  checkout() {
    console.log('Checked out. Thank you for shopping!');
  }
}

class PaymentService {
  makePayment(amount) {
    console.log(`Paid $${amount}`);
  }
}

class ShippingService {
  shipItems(items) {
    console.log(`Shipped items: ${items}`);
  }
}

// 外观对象
class OnlineStore {
  constructor() {
    this.shoppingCart = new ShoppingCart();
    this.paymentService = new PaymentService();
    this.shippingService = new ShippingService();
  }

  purchaseItems(items) {
    items.forEach((item) => {
      this.shoppingCart.addItem(item);
    });

    this.shoppingCart.checkout();
    this.paymentService.makePayment(100); // 假设总金额是 $100
    this.shippingService.shipItems(items);
  }
}

// 使用外观对象
const onlineStore = new OnlineStore();
const itemsToPurchase = ['Item 1', 'Item 2', 'Item 3'];
onlineStore.purchaseItems(itemsToPurchase);

享元模式(Flyweight)

概念

享元模式(Flyweight Pattern)是一种结构性设计模式,它旨在通过共享对象以减少内存使用和提高性能。享元模式的核心思想是共享相似的对象,以减少创建大量相似对象的开销。

特点

  • 大量相似对象: 当系统中存在大量相似的对象,这些对象之间的区别可以通过外部状态来表示,而内部状态可以被共享。
  • 内存优化: 享元模式可以降低内存使用,因为相似对象的内部状态被共享,不需要为每个对象都分配内存。
  • 性能优化: 由于对象的共享,创建和销毁对象的成本降低,从而提高了系统的性能。

代码

class Color {
  constructor(name) {
    this.name = name;
  }
}

class ColorFactory {
  constructor() {
    this.colors = {};
  }

  getColor(name) {
    if (!this.colors[name]) {
      this.colors[name] = new Color(name);
    }
    return this.colors[name];
  }

  getNumberOfColors() {
    return Object.keys(this.colors).length;
  }
}

// 使用享元模式
const colorFactory = new ColorFactory();

const red = colorFactory.getColor('red');
const green = colorFactory.getColor('green');
const blue = colorFactory.getColor('blue');
const redAgain = colorFactory.getColor('red');

console.log(red === redAgain);  // true,相同颜色对象被共享
console.log(colorFactory.getNumberOfColors());  // 3,共享的颜色对象数量为3

代理模式(Proxy)

概念

代理模式(Proxy Pattern)是一种常用的设计模式,它允许你创建一个代理对象,用来控制对另一个对象的访问。代理对象充当一个中介,可以在访问原始对象之前或之后执行一些附加的逻辑。

特点

  • 保护代理: 代理对象用于控制对原始对象的访问,可以实现权限控制、安全性验证等。
  • 虚拟代理: 代理对象用于延迟加载原始对象,这在处理大型对象或资源密集型操作时很有用,以提高性能。
  • 缓存代理: 代理对象用于缓存原始对象的结果,当相同的操作被多次调用时,可以直接返回缓存的结果,避免重复计算。
  • 远程代理: 代理对象用于在本地操作远程对象,例如通过网络访问远程资源。

代码

class Image {
  constructor(url) {
    this.url = url;
    this.loadImage();
  }

  loadImage() {
    console.log(`Loading image from ${this.url}`);
  }

  display() {
    console.log(`Displaying image: ${this.url}`);
  }
}

class ImageProxy {
  constructor(url) {
    this.url = url;
    this.image = null;
  }

  display() {
    if (!this.image) {
      this.image = new Image(this.url);
    }
    this.image.display();
  }
}

// 使用代理模式
const imageProxy = new ImageProxy('image.jpg');
imageProxy.display();  // Loading image from image.jpg,Displaying image: image.jpg(仅在第一次显示时加载)
imageProxy.display();  // Displaying image: image.jpg(已加载,直接显示)

链式操作模式(Chain Of Responsibility)

概念

链式操作模式(Chain of Responsibility Pattern)是一种行为性设计模式,它允许你构建一个对象链,每个对象都可以处理请求并将其传递给下一个对象,直到请求被处理或达到链的末端。这种模式将请求发送者和接收者解耦,使多个对象都有机会处理请求。

特点

  • 请求处理链: 当一个请求需要经过多个处理步骤时,链式操作模式可以将处理步骤组织成一个链,每个步骤依次处理请求。
  • 解耦请求发送者和接收者: 链式操作模式将请求发送者和接收者解耦,使它们不需要直接知道彼此的存在。
  • 动态组合: 链式操作模式允许你在运行时动态地组合处理步骤,以满足不同的需求。

代码

class PurchaseRequest {
  constructor(amount) {
    this.amount = amount;
  }
}

class Approver {
  setNext(nextApprover) {
    this.nextApprover = nextApprover;
  }

  processRequest(request) {
    if (this.nextApprover) {
      this.nextApprover.processRequest(request);
    }
  }
}

class Manager extends Approver {
  processRequest(request) {
    if (request.amount <= 1000) {
      console.log(`Manager approves purchase of $${request.amount}`);
    } else {
      super.processRequest(request);
    }
  }
}

class Director extends Approver {
  processRequest(request) {
    if (request.amount <= 5000) {
      console.log(`Director approves purchase of $${request.amount}`);
    } else {
      super.processRequest(request);
    }
  }
}

class CEO extends Approver {
  processRequest(request) {
    console.log(`CEO approves purchase of $${request.amount}`);
  }
}

// 构建处理链
const manager = new Manager();
const director = new Director();
const ceo = new CEO();

manager.setNext(director);
director.setNext(ceo);

// 使用链式操作处理请求
const purchaseRequest1 = new PurchaseRequest(800);
const purchaseRequest2 = new PurchaseRequest(3500);
const purchaseRequest3 = new PurchaseRequest(10000);

manager.processRequest(purchaseRequest1);  // Manager approves purchase of $800
manager.processRequest(purchaseRequest2);  // Director approves purchase of $3500
manager.processRequest(purchaseRequest3);  // CEO approves purchase of $10000

命令模式(Command)

概念

命令模式(Command Pattern)是一种行为型设计模式,它将请求或操作封装成一个对象,从而使请求的发送者和接收者解耦,同时允许对请求进行参数化、排队、记录和撤销。

特点

  • 命令(Command): 定义了执行操作的接口,通常包含一个 execute 方法。具体的命令对象实现了这个接口,并封装了一个或多个操作。
  • 接收者(Receiver): 知道如何执行命令相关的操作,实际执行操作的对象。
  • 调用者(Invoker): 负责触发命令的执行。它将命令对象传递给命令的接收者,并在需要时执行命令。
  • 客户端(Client): 创建命令对象,并将其与接收者关联,然后将命令传递给调用者执行。

代码

// 命令对象
class LightOnCommand {
  constructor(light) {
    this.light = light;
  }

  execute() {
    this.light.turnOn();
  }
}

class LightOffCommand {
  constructor(light) {
    this.light = light;
  }

  execute() {
    this.light.turnOff();
  }
}

// 接收者对象
class Light {
  turnOn() {
    console.log('Light is ON');
  }

  turnOff() {
    console.log('Light is OFF');
  }
}

// 调用者对象
class RemoteControl {
  constructor() {
    this.command = null;
  }

  setCommand(command) {
    this.command = command;
  }

  pressButton() {
    this.command.execute();
  }
}

// 客户端代码
const light = new Light();
const lightOnCommand = new LightOnCommand(light);
const lightOffCommand = new LightOffCommand(light);

const remoteControl = new RemoteControl();
remoteControl.setCommand(lightOnCommand);
remoteControl.pressButton();  // Light is ON

remoteControl.setCommand(lightOffCommand);
remoteControl.pressButton();  // Light is OFF

迭代器模式(Iterator)

概念

迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供一种方法来顺序访问一个聚合对象的元素,而不需要暴露该聚合对象的内部结构。通过迭代器模式,可以将遍历与聚合对象分离,从而使得遍历过程更加简单和灵活。

特点

  • 分离遍历与实现: 迭代器模式将遍历集合的行为与集合本身的实现分离开来。这使得你可以独立地变化集合的实现,而不影响遍历操作的代码。
  • 统一的遍历接口: 迭代器模式提供了一个统一的遍历接口,客户端无需关心不同集合的内部结构,可以使用相同的方法来遍历不同类型的集合。
  • 支持多种遍历方式: 迭代器模式可以定义多种不同的迭代方式,比如正向遍历、反向遍历、跳跃遍历等,客户端可以根据需要选择合适的迭代方式。
  • 封装集合内部实现: 迭代器模式将集合的内部实现封装起来,只暴露一个简单的迭代接口。这有助于保护集合的数据完整性和一致性。
  • 支持延迟加载: 迭代器模式可以支持延迟加载,即只在需要的时候才加载集合元素,从而提高性能和资源利用率。
  • 支持多个并发迭代: 迭代器模式可以让多个客户端同时迭代同一个集合,每个客户端都拥有独立的迭代状态,互不干扰。
  • 可组合性: 迭代器模式可以与其他模式结合使用,如组合模式、工厂模式等,从而实现更复杂的功能。
  • 抽象迭代器和具体迭代器: 迭代器模式通常包括一个抽象迭代器接口和一个或多个具体迭代器实现,用于遍历不同类型的集合。

代码

class Iterator {
  constructor(collection) {
    this.collection = collection;
    this.index = 0;
  }

  hasNext() {
    return this.index < this.collection.length;
  }

  next() {
    if (this.hasNext()) {
      return this.collection[this.index++];
    }
    return null;
  }
}

// 客户端代码
const numbers = [1, 2, 3, 4, 5];
const iterator = new Iterator(numbers);

while (iterator.hasNext()) {
  console.log(iterator.next());  // 1, 2, 3, 4, 5
}

中介者模式(Mediator)

概念

中介者模式(Mediator Pattern)是一种行为型设计模式,它用于减少对象之间的直接交互,将对象之间的通信集中到一个中介者对象中,从而降低对象之间的耦合度。

特点

  • 中介者(Mediator): 定义一个接口用于与各个同事对象通信。中介者对象通常持有对各个同事对象的引用,并负责协调它们之间的交互。
  • 具体中介者(Concrete Mediator): 实现中介者接口,负责处理各个同事对象之间的通信。
  • 同事对象(Colleague): 定义一个接口用于与中介者通信。同事对象通过中介者来与其他同事对象通信,而不是直接与其他同事对象通信。
  • 具体同事对象(Concrete Colleague): 实现同事对象接口,与其他同事对象通信时通过中介者来处理。

代码

class ControlTower {
  constructor() {
    this.aircrafts = [];
  }

  registerAircraft(aircraft) {
    this.aircrafts.push(aircraft);
    aircraft.setControlTower(this);
  }

  requestTakeoff(aircraft) {
    if (this.aircrafts.includes(aircraft)) {
      console.log(`${aircraft.getName()} requesting takeoff.`);
      // 假设一些逻辑,如检查天气等
      console.log(`Clear for takeoff: ${aircraft.getName()}`);
    } else {
      console.log(`${aircraft.getName()} is not registered.`);
    }
  }
}

class Aircraft {
  constructor(name) {
    this.name = name;
    this.controlTower = null;
  }

  setControlTower(controlTower) {
    this.controlTower = controlTower;
  }

  getName() {
    return this.name;
  }

  requestTakeoff() {
    this.controlTower.requestTakeoff(this);
  }
}

// 客户端代码
const controlTower = new ControlTower();

const aircraft1 = new Aircraft('Flight 1');
const aircraft2 = new Aircraft('Flight 2');

controlTower.registerAircraft(aircraft1);
controlTower.registerAircraft(aircraft2);

aircraft1.requestTakeoff();
aircraft2.requestTakeoff();

备忘录模式(Memento)

概念

备忘录模式(Memento Pattern)是一种行为型设计模式,它用于捕获一个对象的内部状态,并在不破坏对象封装的情况下将状态保存在一个备忘录对象中,从而可以在之后恢复对象到先前的状态。

核心思想

将对象的状态保存在一个备忘录对象中,然后可以随时将对象恢复到之前保存的状态。这有助于实现撤销、重做和状态恢复等功能,同时避免了将状态暴露给外部。

特点

  • 原发器(Originator): 定义一个接口用于创建备忘录对象和恢复状态。原发器保存其内部状态,并可以从备忘录对象中恢复状态。
  • 备忘录(Memento): 存储原发器的内部状态。备忘录对象通常只能由原发器创建和访问,以保持状态的封装性。
  • 负责人(Caretaker): 负责管理备忘录对象。负责人可以保存多个备忘录对象,以支持多次状态恢复。

代码

class EditorMemento {
  constructor(content) {
    this.content = content;
  }

  getContent() {
    return this.content;
  }
}

class Editor {
  constructor() {
    this.content = '';
  }

  type(text) {
    this.content += text;
  }

  getContent() {
    return this.content;
  }

  save() {
    return new EditorMemento(this.content);
  }

  restore(memento) {
    this.content = memento.getContent();
  }
}

class History {
  constructor() {
    this.states = [];
  }

  pushState(memento) {
    this.states.push(memento);
  }

  popState() {
    return this.states.pop();
  }
}

// 客户端代码
const editor = new Editor();
const history = new History();

editor.type('Hello, ');
editor.type('world!');

history.pushState(editor.save());

editor.type(' This is the new content.');

console.log(editor.getContent()); // Hello, world! This is the new content.

editor.restore(history.popState());

console.log(editor.getContent()); // Hello, world!

观察者模式(Observer)

概念

观察者模式(Observer Pattern)是一种行为型设计模式,它用于定义对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。

核心思想

将主题(被观察者)和观察者分离开来,使主题不需要知道有哪些观察者,而观察者只需要关心主题的变化。当主题的状态发生变化时,它会通知所有注册的观察者,观察者会自动更新自己的状态。

特点

  • 主题(Subject): 定义一个接口,包含注册、移除和通知观察者的方法。主题的状态变化会通知所有注册的观察者。
  • 具体主题(Concrete Subject): 实现主题接口,维护观察者列表,并在状态发生变化时通知观察者。
  • 观察者(Observer): 定义一个更新方法,用于接收主题的通知,并更新自己的状态。
  • 具体观察者(Concrete Observer): 实现观察者接口,维护自己的状态,并在接收到主题通知时更新自己的状态。

代码

class NewsPublisher {
  constructor() {
    this.subscribers = [];
  }

  subscribe(subscriber) {
    this.subscribers.push(subscriber);
  }

  unsubscribe(subscriber) {
    this.subscribers = this.subscribers.filter(s => s !== subscriber);
  }

  notify(news) {
    this.subscribers.forEach(subscriber => {
      subscriber.update(news);
    });
  }
}

class Subscriber {
  constructor(name) {
    this.name = name;
  }

  update(news) {
    console.log(`${this.name} received news: ${news}`);
  }
}

// 客户端代码
const publisher = new NewsPublisher();

const subscriber1 = new Subscriber('Subscriber 1');
const subscriber2 = new Subscriber('Subscriber 2');
const subscriber3 = new Subscriber('Subscriber 3');

publisher.subscribe(subscriber1);
publisher.subscribe(subscriber2);
publisher.subscribe(subscriber3);

publisher.notify('New product launch!'); // 所有订阅者都会收到通知

publisher.unsubscribe(subscriber2);

publisher.notify('Special offer!'); // subscriber2 不再收到通知

状态模式(State)

概念

状态模式(State Pattern)是一种行为型设计模式,它允许对象在其内部状态发生改变时改变其行为,从外部看起来就像是改变了其类一样。状态模式主要目的是将对象的状态从其方法中分离出来,以实现更好的可维护性和扩展性。

核心思想

将各种状态封装成独立的类,对象内部维护一个指向当前状态对象的引用,从而可以根据状态的变化来改变对象的行为。

特点

  • 环境(Context): 维护一个指向当前状态的引用,用于将请求委派给当前状态对象进行处理。环境对象可以随时切换状态,从而改变对象的行为。
  • 抽象状态(State): 定义一个接口,用于封装不同状态的行为。每个具体状态类都会实现这个接口,实现具体的状态行为。
  • 具体状态(Concrete State): 实现抽象状态接口,定义该状态下的具体行为。不同的具体状态类对应不同的行为逻辑。

代码

class Light {
  constructor() {
    this.state = new OffState();
  }

  setState(state) {
    this.state = state;
  }

  turnOn() {
    this.state.turnOn(this);
  }

  turnOff() {
    this.state.turnOff(this);
  }
}

class OffState {
  turnOn(light) {
    console.log('Turning light on...');
    light.setState(new OnState());
  }

  turnOff(light) {
    console.log('Light is already off.');
  }
}

class OnState {
  turnOn(light) {
    console.log('Light is already on.');
  }

  turnOff(light) {
    console.log('Turning light off...');
    light.setState(new OffState());
  }
}

// 客户端代码
const light = new Light();

light.turnOn(); // Turning light on...
light.turnOff(); // Turning light off...
light.turnOff(); // Light is already off.
light.turnOn(); // Turning light on...

策略模式(Strategy)

概念

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,将每个算法封装成一个独立的策略类,使得这些算法可以互相替换,而客户端代码无需改变。

核心思想

将算法的使用与算法的实现分离开来,使得客户端可以独立选择并切换不同的算法,从而达到代码的灵活性和可维护性。

特点

策略(Strategy): 定义一个策略接口或抽象类,声明算法的方法。 具体策略(Concrete Strategy): 实现策略接口,提供具体的算法实现。 环境(Context): 维护一个策略对象的引用,将具体的算法委派给策略对象来处理。

代码

class BubbleSort {
  sort(arr) {
    console.log('Sorting using bubble sort...');
    // 实现冒泡排序算法
    return arr;
  }
}

class QuickSort {
  sort(arr) {
    console.log('Sorting using quick sort...');
    // 实现快速排序算法
    return arr;
  }
}

class Sorter {
  constructor(strategy) {
    this.strategy = strategy;
  }

  setStrategy(strategy) {
    this.strategy = strategy;
  }

  sort(arr) {
    return this.strategy.sort(arr);
  }
}

// 客户端代码
const bubbleSort = new BubbleSort();
const quickSort = new QuickSort();

const sorter = new Sorter(bubbleSort);

let arr = [5, 2, 8, 1, 4];
console.log(sorter.sort(arr)); // Sorting using bubble sort...

sorter.setStrategy(quickSort);
console.log(sorter.sort(arr)); // Sorting using quick sort...

模板方法模式(Template Method)

概念

模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一个算法的框架,将算法的具体步骤延迟到子类来实现。模板方法模式主要用于在一个类中定义算法的基本结构,但允许子类为其中的某些步骤提供具体实现。

核心思想

将算法的骨架放在父类中,将具体的步骤实现交给子类。这样可以确保算法的结构不变,但每个子类可以根据需要重写特定的步骤。

特点

  • 抽象模板(Abstract Template): 定义了一个算法的框架,其中包含了算法的各个步骤。抽象模板中的某些步骤可能是抽象的,需要由子类来实现。
  • 具体模板(Concrete Template): 实现抽象模板中的具体步骤,完成算法的实际实现

代码

//抽象模板
class Beverage {
  prepare() {
    this.boilWater();
    this.brew();
    this.pourInCup();
    this.addCondiments();
  }

  boilWater() {
    console.log('Boiling water');
  }

  pourInCup() {
    console.log('Pouring into cup');
  }

  // 抽象方法,需要子类实现
  brew() {}

  // 抽象方法,需要子类实现
  addCondiments() {}
}
//具体模板
class Coffee extends Beverage {
  brew() {
    console.log('Dripping coffee through filter');
  }

  addCondiments() {
    console.log('Adding sugar and milk');
  }
}
//具体模板
class Tea extends Beverage {
  brew() {
    console.log('Steeping the tea');
  }

  addCondiments() {
    console.log('Adding lemon');
  }
}

// 客户端代码
const coffee = new Coffee();
const tea = new Tea();

console.log('Making coffee...');
coffee.prepare();

console.log('\nMaking tea...');
tea.prepare();

访问者模式(Visitor)

概念

访问者模式(Visitor Pattern)是一种行为型设计模式,它用于在不改变被访问对象的类的情况下,定义一个新的操作(访问者),用于对对象结构中的元素进行操作。

核心思想

将数据结构和数据操作分离开来,使得可以在不修改数据结构的情况下,通过访问者对象对数据进行不同的操作。这有助于在需要对数据结构进行多种操作时,避免修改每个元素的类。

特点

  • 访问者(Visitor): 定义一个接口或抽象类,包含对不同类型元素的访问方法。访问者对象可以根据需要对元素进行不同的操作。
  • 具体访问者(Concrete Visitor): 实现访问者接口,提供对元素的具体操作实现。
  • 元素(Element): 定义一个接口或抽象类,声明接受访问者的方法。这个方法会将访问者对象传递给元素,使得元素可以接受访问者的访问。
  • 具体元素(Concrete Element): 实现元素接口,提供接受访问者的具体实现。
  • 对象结构(Object Structure): 包含多个元素对象的集合。访问者会遍历对象结构中的元素并对其进行操作。

代码

//具体访问者
class DiscountVisitor {
  visitProduct(product) {
    console.log(`Discount applied to product: ${product.name}`);
    // 实际的打折逻辑
  }

  visitService(service) {
    console.log(`Discount applied to service: ${service.name}`);
    // 实际的打折逻辑
  }
}
//具体元素
class Product {
  constructor(name) {
    this.name = name;
  }

  accept(visitor) {
    visitor.visitProduct(this);
  }
}
//具体元素
class Service {
  constructor(name) {
    this.name = name;
  }

  accept(visitor) {
    visitor.visitService(this);
  }
}

// 客户端代码
const discountVisitor = new DiscountVisitor();

const product = new Product('Product A');
const service = new Service('Service X');

product.accept(discountVisitor); // Discount applied to product: Product A
service.accept(discountVisitor); // Discount applied to service: Service X

解释器模式(Interpreter)

概念

解释器模式(Interpreter Pattern)是一种行为型设计模式,它用于定义一个语言的语法,并提供一个解释器来解释语言中的表达式。

核心思想

将语法规则表示为类的层次结构,并为每个语法规则定义一个相应的类。这些类可以通过递归调用来解释语言中的表达式,从而实现对语言的解释。

特点

  • 抽象表达式(Abstract Expression): 定义一个抽象接口,声明解释器的方法。
  • 终结符表达式(Terminal Expression): 实现抽象表达式接口,用于表示语言中的终结符。终结符表达式通常是一些基本的语法单位,如变量、常量等。
  • 非终结符表达式(Non-Terminal Expression): 实现抽象表达式接口,用于表示语言中的非终结符。非终结符表达式通常由终结符表达式和其他非终结符表达式组合而成,形成语法规则。
  • 上下文(Context): 包含要解释的语言表达式。上下文可以存储变量的值等信息,供解释器使用。

代码

//上下文
class Context {
  constructor(input) {
    this.input = input;
    this.index = 0;
  }

  getCurrentToken() {
    return this.input[this.index];
  }

  nextToken() {
    this.index++;
  }
}
//抽象表达式
class Expression {
  interpret(context) {
    // 抽象方法,需要子类实现
  }
}
// 终结符表达式
class NumberExpression extends Expression {
  interpret(context) {
    const currentToken = context.getCurrentToken();
    context.nextToken();
    return parseInt(currentToken);
  }
}
// 非终结符表达式
class AddExpression extends Expression {
  interpret(context) {
    const left = new NumberExpression();
    const right = new NumberExpression();
    return left.interpret(context) + right.interpret(context);
  }
}

// 客户端代码
const input = '1+2';
const context = new Context(input);

const expression = new AddExpression();
console.log(expression.interpret(context)); // 3