【问题标题】:Inheritance with factory method/class pattern工厂方法/类模式的继承
【发布时间】:2017-05-09 07:49:13
【问题描述】:

我正在阅读有关在 JavaScript 中创建对象的不同方法,而不是使用 new 和 ES6 类。一种方法是使用工厂方法/工厂类模式(取自https://medium.com/humans-create-software/factory-functions-in-javascript-video-d38e49802555):

const dog = () => {
  const sound = 'woof'
  return {
    talk: () => console.log(sound)
  }
}
const sniffles = dog()
sniffles.talk() // Outputs: "woof"

我将如何实现像Animal 这样的类,或者更确切地说是我的狗函数可以“继承”的另一个工厂函数?我是否将动物对象传递给狗函数并将返回的对象的原型设置为传递的动物对象?

【问题讨论】:

  • 您想要实现的究竟是什么?您可能不需要继承。
  • 只是一般性地询问,但例如 Animal 类可以实现其所有子类都希望实现的行为。或者像 move() 这样的方法,它会在某些子类中被覆盖。
  • 为什么投反对票?

标签: javascript inheritance


【解决方案1】:

你会使用Object.create:

const animal = () => ({
  talk: function() {
    console.log(this.sound);
  }
});

const dog = () => Object.create(animal(), {
  sound: {
    value: "woof"
  }
});

// or...

const dog2 = () => {
  var someDog = Object.create(animal());
  someDog.sound = "woof";

  return someDog;
};

var someDog = dog();
someDog.talk();

var someDog2 = dog2();
someDog2.talk();

顺便说一句,我的意见是,您应该使用 ES2015+ 类/继承,不要使用自定义工厂,而 Object.create 用于您真正需要它们的极端情况:

class Animal {
  talk() {
    return console.log(this.sound);
  }
}

class Dog extends Animal {
  constructor() {
    super();
    this.sound = "woof";
  }
}

var dog = new Dog();
dog.talk();

【讨论】:

  • 谢谢,我就是这个意思! :)
  • @eol 没问题! ;)
  • 很好的答案,虽然我认为工厂函数总是比类或构造函数更可取。我认为 Eric Elliot 的文章很好地总结了这种情况的原因:medium.com/javascript-scene/…
  • @ChristosDimitroulas 我会检查的!谢谢你:)
【解决方案2】:

@nils 是对的,不清楚你想要继承什么/为什么要继承,所以我只是猜测,但也许这就是你的意思:)

const animal = (sound) => {
  return {
    talk: () => console.log(sound)
  }
}
const dog = () => animal('woof')
const sniffles = dog()
sniffles.talk() // Outputs: "woof"

【讨论】:

  • 这是一种可能的方法
【解决方案3】:

除了这些其他答案,我建议您看看这本关于 Javascript 设计模式的优秀书籍,Learning JavaScript Design Patterns

特别是,看看它关于factories的章节。

为了实现工厂模式的继承,它的原始工厂类接受一个配置对象。更具体的工厂只是通过传入对象来扩展原始工厂类。

【讨论】:

    【解决方案4】:

    我检查了这个问题,因为我决定使用工厂函数而不是类。 我假设你有一个通用的工厂函数,比如AnimalMammal,它有一些特性(比如声音,就像一些建议的那样),你想继承它们并在另一个工厂函数中添加一些特定的特性。

    让我们构建一个动物工厂函数:

    const Animal = ({color = "green", numberOfLegs = 4} = {}) => {
      const SetColor = (newColor) => {
        color = newColor;
      };
      const GetColor= () => color;
      const GetNumberOfLegs = () => numberOfLegs;
      const MakeSound = () => console.log("Screetch");
      return {
        GetColor,
        GetNumberOfLegs,
        MakeSound
      }
    }
    const newCreature = Animal({color: black, numberOfLegs: 3})
    newCreature.MakeSound() // -> "Screetch"
    

    让我们构建一个 Dog 工厂函数:

    const Dog = ({name = "rex", color = "black", numberOfLegs = 4} = {}) => {
      const MakeSound = () => console.log("Woof Woof");
      const Roll = () => console.log(`${name} made a roll!`)
      return {
        MakeSound,
        Roll
      }
    }
    const sniffles = Dog({name: "sniffles", color: black, numberOfLegs: 4})
    sniffles.MakeSound() // -> "Woof Woof"
    sniffles.Roll() // -> "sniffles made a roll!"
    

    如果我想继承我已经拥有的从Animal 获得的所有好东西,我该怎么办?
    使用 ES6 Spread Syntax 可以帮助我们实现一种非常简洁的方式:

    const Dog = ({name = "rex", color = "black", numberOfLegs = 4} = {}) => {
      const anAnimal = Animal({color, numberOfLegs}); // used the Animal factory!
      const MakeSound = () => console.log("Woof Woof");
      const Roll = () => console.log(`${name} made a roll!`)
      return {
        ...anAnimal, // And That's where magic happens!
        MakeSound,
        Roll
      }
    }
    const sniffles = Dog({name: "sniffles", color: black, numberOfLegs: 4})
    sniffles.GetNumberOfLegs() // -> 4
    sniffles.MakeSound() // -> "Woof Woof"
    sniffles.Roll() // -> "sniffles made a roll!"
    

    那么到底发生了什么?
    Dog 工厂中,我们将Animal 工厂调用到anAnimal,因此anAnimal 现在是一个对象。 在Dog工厂返回的对象中,我们传播了anAnimal对象,然后添加了Dog的属性。 如果你给一个对象两个相同的具有不同值的键,它将采用后者:

    const someObject = {a: 1, b: 2, a: 3};
    someObject // -> {a: 3, b: 2}
    

    因此,您不必担心Dog 是否使用了已经从Animal 提供的任何密钥,因为它们已被覆盖。

    【讨论】:

      猜你喜欢
      • 2020-10-25
      • 1970-01-01
      • 2013-06-13
      • 1970-01-01
      • 2020-11-22
      • 2023-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多