I've used the first two videos in this series to learn about some basic OOP concepts.
Lately, I primarily write in Node, so I'm working with prototypical inheritance on the front-end and back-end. However, these tutorials showcase OOP concepts with Java. Java is a strictly-typed language which utilizes classical inheritance.
This question pertains to both classical and prototypical inheritance, but in different ways.
This problem is a little bit difficult to put into words, so I'll use an example:
I've created a super-class called animal. I then create two sub-classes of animal: horse and donkey. Now my program requires a hybrid of the two sub-classes. Creating a mule actually seems to be a little tricky.
At first the answer seemed obvious; create a stand-alone mule sub-class. But that kind of defeats the purpose of OOP. Creating a new sub-class when I already have the traits is a violation of the DRY principle.
To confirm that this is an appropriate way to create my mule I asked myself two questions:
1) Is a mule a horse?
2) Is a mule a donkey?
The answer seemed to be a resounding kind of that leans towards a yes.
I'm completely lost as to how this would be accomplished with classical inheritance. I could not come up with what I considered a "good" solution with interfaces or abstract classes.
In a language which use prototypical inheritance like JavaScript, I might "selectively breed" a mule by pulling down only the methods and instance variables that applied to a mule. However, this seems to be rather close to creating a brand-new sub-class.
What is the "correct" way to handle this problem in both classical and prototypical inheritance?
Decomposition might help reaching the DRY goal.
Every behavior/role that not obviously should be inherited, might be considered of being implemented as mixin or trait. Thus theirs code reuse at different places at class level is much easier and more elegant now via composition.
As for JavaScript, there is only delegation. Inheritance at one hand is supported by an implicit delegation automatism via the prototype chain, whereas composition gets achieved by delegating functionality explicitly via
call
orapply
.This makes things much easier since one only needs to deal with objects/instances and methods/function-objects. The class/inheritance part in JavaScript is covered by either constructor functions and every constructor function's
prototype
or by (blueprint-)objects that are passed as prototypes toObject.create
. Factories will be useful in providing an API and hiding the preferred implementation of one of the above mentioned approaches.Nevertheless the core principles remain untouched with both of them ... dealing with a) objects and function-objects with b) inheritance and with c) composition.
The following provided example therefore chooses just ECMAScript-3 features and constructor functions. From there it can be easily transferred(/transpiled) to either class syntax or
Object.create
.The OP's example is well chosen, for a mule is neither a horse nor a donkey. It still does belong to the genus Equus but features its very own chromosome pairs that are distinct from the one of horses or donkeys. Yet it features behavior and visible markers of both of them. Inheritance therefore, if at all, is achieved via the Equus. Other behavior and appearance that is either specific or generic to each of both species just will be mixed into a mule.
Function based mixins / traits / talents in JavaScript always get applied to objects/instances, thus even behavior that will be inherited via the prototype chain, can be collected into such functionality that again can be applied to a prototypal object if necessary/appropriate.
The following example makes use of this technique and also does comment on it in order to demonstrate DRY-ness and code reuse at this 2 delegation levels of JavaScript.
side note - recommended resources on functions based Mixins / Traits / Talents in JavaScript
Additionally I do recommend reading some of the listed answers of mine given on SO, that are related to this topic too.
The concept you are looking for is traits (you actually mentioned it). I will use a different example, that I find more appropriate:
Further reading: Traits in PHP, Traits in Javascript.