to the Path of Summoning and Commanding Objects (a.k.a. Object Oriented Programming)
Many ways to build a Golem there are,
cast its blueprint in clay
then recite the instantiation chants,
or put together the parts
that'll made the whole alive,
or bring it forth at once
with no prior thought required.
Many ways to build a Golem there are,
The constructor function represents how an object should be constructed (created) while the prototype represents a piece of reusable behavior. In practice, the constructor function usually defines the data members within a “class” while the prototype defines its methods.
You can instantiate a new
Minion object by using the
new operator on the constructor function:
As a result of instantiating an
orc we get a new
Minion object with two properties
Minion object also has a hidden property called
[[prototype]] that points to its
prototype which is an object that has a method
toString. This prototype and its
toString method are shared across all instances of the
When you call
orc object has a
toString method and if it can’t find it, like in this case, it goes down the prototype chain until it does. The prototype chain is established by the object itself, its prototype, its prototype’s prototype and so on. In this case, the prototype chain leads to the
Minion.prototype object that has a
toString method. This method will then be called and evaluated as
We can mimic classical inheritance by defining a new “class”
Wizard and make it inherit from
We achieve classical inheritance by:
- calling the
Minionconstructor function from the
- assigning a
Minionobject (created via
Object.create) as prototype of the
With the constructor delegation we ensure that a
Wizard object has all the properties of a
Minion object. While with the prototype chain we ensure that all the methods in the
Minion prototype are available to a
We can also augment the
Wizard prototype with new methods:
Or even override or extend existing methods within its base “class”
Finally, we can verify that everything works as expected by instantiating our very own powerful wizard:
As you can see from these previous examples, writing “classes” prior to ES6 was no easy feat. It required a lot of moving components and a lot of code. That’s why ES6 brings classes along which provide a much nicer syntax to what you’ve seen thus far. This means that instead of having to handle constructor functions and prototypes yourself, you get the new
class keyword that nicely wraps both into a more coherent and developer friendly syntax:
You also get access to the
super keywords, where
extend lets you establish class inheritance and
super lets you access methods from parent classes:
Again, we can verify that it works just like it did before by instantiating a classy wizard:
It is important to highlight though that ES6 classes are just syntactic sugar. Under the hood, these ES6 classes that you have just seen are translated into constructor function/prototype pairs.
OOP Beyond Classes
In the world of prototypical inheritance you only have objects, and particularly objects that are based upon other objects which we call prototypes. Prototypes lend behaviors to other objects by means of delegation (via the prototype chain) or by the so called concatenative inheritance which consists in copying behaviors.
Let’s illustrate the usefulness of this type of inheritance with an example. Imagine that, in addition to wizards, we also need to have some thieves for those occassions when we need to use more gentle/shrew hand against our enemies. A
ClassyThief class could look something like this:
And let’s say that a couple of weeks from now, we realize that it would be nice to have yet another type of minion, one that can both cast spells and steals, and why not? Play some music. Something like a Bard. In pseudo-code we would describe it as follows:
Well, we’ve put ourselves in a pickle here. Classical inheritance tends to build rigid taxonomies of types where something is a
Wizard, something is a
Thief but it cannot be both. How would we solve the issue of the
Bard using classical inheritance in C#? Well…
- We could move both
stealsmethods to a base class
SpellCastingAndStealingMinionthat all three types could inherit. The
ClassyThiefwould throw an exception when casting spell and so would the
ClassyWizardwhen stealing. Not a very good solution (goodbye Liskov principle 3)
- We could create a
SpellCastingAndStealingMinionthat duplicates the functionality in
ClassyWizardand make the
Bardinherit from it. This solution would imply code duplication and thus additional maintenance.
- We could define interfaces for these behaviors
ICanCastSpellsand make each class implement these interfaces. Nicer but we would need to provide an specific implementation in each separate class. No so much code reuse here.
Imagine that we broke down all these behaviors and encapsulated them inside separate objects (
Now that we have encapsulated each behavior in a separate object we can compose them together to provide the necessary functionality to a
thief and a
And in a very expressive way we can see how a
wizard is someone than can cast spells, a
thief is someone that can steal and a
bard someone that not only can cast spells and steal but can also play music. By stepping out of the rigid limits of classical inheritance and strong and static typing, we get to a place where we can easily reuse behaviors and compose new objects in a very flexible and extensible manner.
We can verify that indeed this approach works beautifully:
Object.assign in the examples is an ES6 method that lets you extend an object with other objects. This is effectively the concatenative prototypical inheritance we mentioned previously.
Combining Classes with Object Composition
Do you remember that ES6 classes are just syntactic sugar over the existing prototypical inheritance model? They may look like classical inheritance but they are not. This means that the following mix of ES6 classes and object composition would work:
In this example we extend the
ClassyBard prototype with new functionality that will be shared by all future instances of
ClassyBard. If we instantiate a new bard we can verify that it can steal, cast spells and play music:
This is an example of delegation-based prototypical inheritance in which methods such as
playsMusic are delegated to a single prototype object (instead of being appended to each object).
The Path of the Object Summoner Step by Step
In White Tower Summoning Enhanced: the Marvels of ES6 Classes you’ll learn about the new ES6 Class syntax and how it provides a much better class development experience over what it was possible prior to ES6.
In Black Tower Summoning: Objects Interweaving Objects with Mixins we’ll go beyond classical inheritance into the arcane realm of object composition with mixins. You’ll learn about the extreme extensibility of object-oriented programming based on object composition. How you can define small pieces of reusable behavior and properties and combine them together to create powerful objects (effectively achieving multiple inheritance).
In **Black Tower Summoning: Safer Object Composition with Traits ** you’ll learn about an object composition alternative to mixins called traits. Traits are as reusable and composable as mixins but are even more flexible and safe as they let you define required properties and resolve conflicts.
Object APIs, ES6 proxies and the new ES6
The Liskov substitution principle is one of the S.O.L.I.D. principles of object-oriented design. It states that derived classes must be substitutable for their base classes. This means that a derived class should behave as portrayed by its base class and not break the expectations created by its interface. In this particular example if you have a
stealsmethod in the base class, and a derived class throws an exception when you call them you are violating this principle. That’s because the derived class breaks the expectations established by the base class (i.e. that you should be able to use both methods). ↩