“Favor object composition over class inheritance.” ~ The Gang of Four, “Design Patterns: Elements of Reusable Object-Oriented Software”
What is composition and why is it better than inheritance?
I wrote in the previous post about Factory Functions and why they're better than constructors. Factory Functions use composition to define objects and their properties.
Composition is a way of defining objects based on what they do while inheritance defines objects based on what they are.
Let’s say we’re creating two objects of cat and dog, simplicity we create a prototype object of animal which they inherit shared properties from,
function Cat(){
//property and method definitions
}
function Dog(){
//property and method definitions
}
Animal.prototype.eat = function(){
//code for eat
}
but then what if we’re also creating a robot cat that has similar properties with cat but not all those properties are similar(e.g. it doesn’t eat), if we inherit the property from animal we’re gonna end up with properties we don’t need on the robot cat object.
Now let’s say we’re creating both a robot cat and robot dog, we can also create for simplicity a parent robotAnimal that both robotCat and robotDog can inherit shared properties from. But they also have similar properties with the real animals and at the same time different properties. The whole program is gonna be jumbled up with unwanted properties in all the objects.
function RobotCat(){
//cannot inherit the eat method because it doesn't eat
}
function RobotDog(){
//also can't inherit eat method
}
Robot.prototype.drive = function(){
//code for drive
}
This is where composition comes in, instead of creating all these classes and inheriting them, you can create a factory function and using object.assign you can copy what properties you need for each object. That way you don’t end up with a jumbled-up program with object properties all over the place.
const driver = (state) => ({
drive: state.position = state.position + state.speed
})
const RobotCat(name){
let state = {
name,
speed,
position
}
return Object.assign(
{},
driver(state)
)
}
This is just a brief explanation of what you can do with factory functions and composition. If you want to dive deeper into this topic, you can check out this article, there's also a great video at the end of the article about composition.