JavaScript设计模式「基于ES2024」:其他模式-混入模式

90 阅读2分钟

混入模式是一种在不使用继承的情况下,用包含方法的对象来增强类的模式。这种模式允许我们轻松地将功能添加到类中,而不需要使用复杂的继承链。在JavaScript中,由于其原型继承的特性,混入模式特别有用和灵活。

// 定义混入
const swimmingMixin = {
    swim() {
        console.log(`${this.name} is swimming.`);
    },
    dive() {
        console.log(`${this.name} is diving.`);
    }
};

const flyingMixin = {
    fly() {
        console.log(`${this.name} is flying.`);
    },
    land() {
        console.log(`${this.name} is landing.`);
    }
};

const speakingMixin = {
    speak(phrase) {
        console.log(`${this.name} says: "${phrase}"`);
    }
};

// 使用混入的基础类
class Animal {
    constructor(name) {
        this.name = name;
    }

    eat() {
        console.log(`${this.name} is eating.`);
    }

    sleep() {
        console.log(`${this.name} is sleeping.`);
    }
}

// 应用混入的辅助函数
function applyMixins(derivedCtor, mixins) {
    mixins.forEach((mixin) => {
        Object.getOwnPropertyNames(mixin).forEach((name) => {
            Object.defineProperty(
                derivedCtor.prototype,
                name,
                Object.getOwnPropertyDescriptor(mixin, name)
            );
        });
    });
}

// 使用混入创建具体类
class Duck extends Animal {
    constructor(name) {
        super(name);
    }

    quack() {
        console.log(`${this.name} quacks.`);
    }
}
applyMixins(Duck, [swimmingMixin, flyingMixin, speakingMixin]);

class Fish extends Animal {
    constructor(name) {
        super(name);
    }
}
applyMixins(Fish, [swimmingMixin]);

class Parrot extends Animal {
    constructor(name) {
        super(name);
    }
}
applyMixins(Parrot, [flyingMixin, speakingMixin]);

// 使用 Symbol 创建更安全的混入
const swimSymbol = Symbol('swim');
const diveSymbol = Symbol('dive');

const saferSwimmingMixin = {
    [swimSymbol]() {
        console.log(`${this.name} is swimming safely.`);
    },
    [diveSymbol]() {
        console.log(`${this.name} is diving safely.`);
    }
};

class Dolphin extends Animal {
    constructor(name) {
        super(name);
        Object.assign(this, saferSwimmingMixin);
    }

    performSwim() {
        this[swimSymbol]();
    }

    performDive() {
        this[diveSymbol]();
    }
}

// 使用示例
function demonstrateMixins() {
    const donald = new Duck("Donald");
    donald.eat();
    donald.swim();
    donald.fly();
    donald.speak("Quack quack!");

    const nemo = new Fish("Nemo");
    nemo.swim();
    nemo.dive();

    const polly = new Parrot("Polly");
    polly.fly();
    polly.speak("I'm a parrot!");

    const flipper = new Dolphin("Flipper");
    flipper.eat();
    flipper.performSwim();
    flipper.performDive();
}

demonstrateMixins();

实现思路

  1. 定义混入

    • 创建了几个包含方法的简单对象(swimmingMixin, flyingMixin, speakingMixin)。
    • 这些方法将被添加到使用混入的类中。
  2. 基础类 Animal

    • 定义了所有动物共有的基本行为。
  3. 应用混入的辅助函数 applyMixins

    • 使用 Object.getOwnPropertyNamesObject.defineProperty 来将混入的方法添加到类的原型中。
  4. 具体类(Duck, Fish, Parrot

    • 继承自 Animal 类。
    • 使用 applyMixins 函数来添加额外的行为。
  5. 使用 Symbol 的更安全的混入(Dolphin 类)

    • 使用 Symbol 作为方法名,以避免命名冲突。
    • 直接在构造函数中使用 Object.assign 来添加混入方法。

优点

  • 代码复用:可以在不同的类之间共享功能,而不需要复杂的继承结构。
  • 灵活性:可以动态地向类添加功能。
  • 组合 over 继承:提供了一种比传统继承更灵活的方式来组合功能。
  • 避免冲突:使用 Symbol 可以创建唯一的方法名,避免命名冲突。