eol
eol

Reputation: 24565

Inheritance with factory method/class pattern

I was reading about different methods of object creation in JavaScript instead of using new and ES6-classes. One method is using the factory method/factory class pattern (taken from 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"

How would I implement a class like Animal or rather another factory funtion which my dog function can "inherit" from? Would I pass the animal object to the dog function and set the prototype of the object being returned to the passed animal object?

Upvotes: 2

Views: 5122

Answers (4)

Tzahi Leh
Tzahi Leh

Reputation: 2470

I checked this issue because I decided to use Factory Functions instead of classes. I assume you have a general factory function, like Animal or Mammal, which has some characteristics (such as sound, like some suggested), and you want to inherit them and add some specific characteristic in another factory function.

Let's build an Animal factory function:

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"

And let's build a Dog factory function:

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!"

What should I do if I want to inherit all the good things I get from Animal that I have already?
Using ES6 Spread Syntax helps us to achieve a very neat way of doing so:

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!"

So what actually happened?
In Dog factory we invoked the Animal factory into anAnimal, so anAnimal is now an object. In the object that Dog factory returns, we spread the anAnimal object, and after that added properties of Dog. If you give an Object two identical keys with different values, it will take the latter:

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

Therefore, you don't need to worry if Dog uses any keys that were given already from Animal, because they are overwritten.

Upvotes: 4

Matías Fidemraizer
Matías Fidemraizer

Reputation: 64943

You would use 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();

BTW, my opinion is that you should go with ES2015+ class/inheritance and leave the use of custom factories and Object.create for corner cases where you really need them:

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

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

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

Upvotes: 9

Michael Yang
Michael Yang

Reputation: 385

In addition to these other answers, I'd recommend taking a look at this excellent book on Javascript design patterns, Learning JavaScript Design Patterns.

In particular, take a look at its chapter on factories.

To achieve inheritance in a factory pattern, its original factory class accepts a configuration object. More specific factories just extend the original factory class by passing in objects.

Upvotes: 1

Kev
Kev

Reputation: 16321

@nils is right, it's not clear what/why you want to inherit, so I'm just guessing, but maybe this is what you mean :)

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

Upvotes: 3

Related Questions