【问题标题】:Composition Pattern in Javascript with Classes but without Mixins?带有类但没有 Mixins 的 Javascript 中的组合模式?
【发布时间】:2021-11-09 19:48:39
【问题描述】:

以下是在 Javascript 中实现组合模式的有效策略吗?我想使用类而不是构造函数或普通对象,而且我知道 Mixins 不是最佳实践。一个问题是,在这种方法中,添加到Person 对象的方法没有附加到原型,因此每个方法都需要内存分配。谢谢!

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

function fly() {
  return {
    fly() {
      console.log(`${this.name} can fly!`);
    },
  };
}

function swim() {
  return {
    swim() {
      console.log(`${this.name} can swim!`);
    },
  };
}

function makeFlyingPerson(name) {
  return Object.assign(new Person(name), fly());
}

function makeSwimmingPerson(name) {
  return Object.assign(new Person(name), swim());
}

【问题讨论】:

  • 不过,这仍然是 mixins。您将两个对象混合在一起 - 一个对象的方法/属性被添加到另一个对象。
  • 如果它按照您想要的方式工作,它就是有效的。也就是说,我认为fly、swim 等没有任何理由成为函数,正如@VLAZ 指出的那样,这个 is 混合。说到:“而且我知道 Mixins 不是最佳实践” 为什么不呢?根据谁?
  • 如果您使用 Class 语法的动机是为了简化 Java 和 JavaScript 之间的大脑切换,我建议您的代码对于试图理解这一点的 Java 开发人员来说会非常混乱。如果这是您的目标,那么请一直使用 Class 语法,以便其他人可以轻松掌握它。 FWIW。
  • @r_zelazny ... 对于提供的示例,任何 mixin 支持技术只有在 OP 实际上不希望每个人实例具有飞行和游泳行为/特征时才有意义。否则Person.prototype 仍然是分配此类行为的最佳位置。在这种情况下,稍后通过将所需行为直接混入原型中来增强已经实现的Person 类的原型是完全有效的。

标签: javascript design-patterns composition es6-class


【解决方案1】:

...添加到Person 对象的方法未附加到原型,因此每个方法都需要分配内存

没错,但这是一个微不足道的数量,每个对象每个方法的一个属性的成本(保存该方法的函数引用)。属性不是什么都没有,但它们并不大。为免生疑问:function 对象被所有实例重用,而不是复制。

fly 和 swim 没有理由成为函数,但(至少,从问题中看不出),直接使用对象:

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

const flyMethods = {
    fly() {
        console.log(`${this.name} can fly!`);
    },
};

const swimMethods = {
    swim() {
        console.log(`${this.name} can swim!`);
    },
};

function makeFlyingPerson(name) {
    return Object.assign(new Person(name), flyMethods);
}

function makeSwimmingPerson(name) {
    return Object.assign(new Person(name), swimMethods);
}

请注意,这仍然使用 mixins(您的原始版本和上述版本)。

除非您打算将fly/flyMethods 和swim/swimMethods 与Person 以外的其他类一起重用,否则使用extends 似乎更简单,并且会给您提供原型方法重用:

class FlyingPerson extends Person {
    fly() {
        // ...
    }
}

如果您正在在多个类中重用fly/flyMethods 等,另一种选择是使用工厂构建函数从各种方法集创建原型,然后重复使用它:

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

const flyMethods = {
    fly() {
        console.log(`${this.name} can fly!`);
    },
};

const swimMethods = {
    swim() {
        console.log(`${this.name} can swim!`);
    },
};

function extendWith(cls, name, ...mixins) {
    // We use the wrapper object so that the class constructor's name is assigned from `name`
    const obj = {
        [name]: class extends cls {
        }
    };
    Object.assign(obj[name].prototype, ...mixins);
    return obj[name];
}

const FlyingPerson = extendWith(Person, "FlyingPerson", flyMethods);
const SwimmingPerson = extendWith(Person, "SwimmingPerson", swimMethods);
const FlyingSwimmingPerson = extendWith(Person, "FlyingSwimmingPerson", flyMethods, swimMethods);

const joe = new FlyingSwimmingPerson("Joe");
joe.fly();
joe.swim();

class Animal {
    name;
    type;
    constructor(name, type) {
        this.name = name;
        this.type = type;
    }
}

const FlyingSwimmingAnimal = extendWith(Animal, "FlyingSwimmingAnimal", flyMethods, swimMethods);

console.log(FlyingSwimmingAnimal.name); // FlyingSwimmingAnimal
const splippery = new FlyingSwimmingAnimal("Slippery");
splippery.fly();
splippery.swim();

【讨论】:

  • 我想知道 OP 问题中的这一点。哪一点对 OP 更重要 - 开发人员沟通或内聚/最小耦合。
  • 如果应该增强对象,这些函数会更有意义,例如,flyer = obj => obj.fly = fly。然后允许一个人制作flier(person) 甚至swimmer(flier(person)) 来获得两者。当然,只有对象,这可以是Object.assign(person, swimMethods, flyMethods)。
  • 感谢您的回答!
  • @r_zelazny - 我的荣幸!我已将其添加到 FWIW 的末尾。编码愉快!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-12
  • 2011-03-26
  • 2016-01-15
  • 2010-09-28
  • 1970-01-01
  • 2017-03-20
相关资源
最近更新 更多