On Github fmvilas / Classical-Inheritance-vs-Modular-Patterns
Yes! In Javascript!
Usually, patterns are grouped by:
Some examples of creational patterns are:
var vehicle = { getModel: function () { console.log( "The model of this vehicle is.." + this.model ); } }; var car = Object.create(vehicle, { "id": { value: MY_GLOBAL.nextId(), enumerable: true // writable:false, configurable:false by default }, "model": { value: "Ford", enumerable: true } });
Prototype Pattern in JavaScript - Code example extracted from Addy Osmani's Learning JavaScript Design Patterns book.
Some examples of structural patterns are:
var myModule = (function() { var _queryDOM = function() { // your code here }; var _renderMap = function() { google.maps.render(); }; return { queryDOM: _queryDOM, renderMap: _renderMap }; })(); myModule.renderMap();
Some examples of behavioral patterns are:
var myModule = function() { ... Observer.observe('dom.element.select', function(el) { console.log('Element %s was selected.', el.getAttribute('id')); }); ... }; var anotherModule = function() { ... Observer.publish('dom.element.select', document.getElementById('anId')); ... };
Some examples of behavioral patterns are:
This is the one used by Node.js.
Why classical inheritance succeeded that way?
...and then you think: "That's pretty nice Fran, but the slide title says Classical Inheritance!"
No, really. We all know how inheritance works.
You inherit properties from your parents.
But you don't make actions nor you react in a concrete way because you inherit it.
So, okay, it's not exactly as real life. It's even better.
But, wait! Are these things exclusive for classical inheritance?
NO
It's object oriented programming.
But OOP is not only made of classical inheritance!
Remember the creational patterns?
We also have Prototypical Inheritance (Prototype Pattern).
And now is when JavaScript comes to the scene.
JavaScript is an object oriented language but not a Class-based object oriented language.
It's a Prototype-based object oriented language.
It means that there are no classes in JavaScript.
Everything is an object and objects have prototypes which, at the same time are objects.
WTF!
That's it! You can simulate classes with Function's objects but they are not, and the primary evidence is that you can modify these "classes" at runtime, which is completely the opposite to the definition of a Class.
What we have in JavaScript is Prototypical Inheritance.
Let's see an example!
var Animal = { bornIn: Date.now, eat: function() { console.log('Eating...'); } }; var Dog = function() { this.bark = function() { console.log('Guau!'); }; this.bornIn = new Date(this.bornIn()); }; Dog.prototype = Animal; var myDog = new Dog(); myDog.eat(); // Outputs: Eating... myDog.bark(); // Outputs: Guau! console.log(myDog.bornIn); // Outputs something like: Tue Jun 17...
Let's explain it step by step.
var Animal = { bornIn: Date.now, // That's a reference to the built-in function eat: function() { console.log('Eating...'); } }; ...
Animal will have two functions, bornIn() and eat().
... var Dog = function() { // Here, "this" will point to the newly created instance, // not to Dog itself. this.bark = function() { console.log('Guau!'); }; // That's called 'property shadowing' this.bornIn = new Date(this.bornIn()); }; ...
Notice the "this.bornIn = ..." line. At the time of creating a Dog instance, this.bornIn refers to Animal.bornIn but, after this line, it will be "hidden" with an own property with the same name. We can still have a reference to Animal.bornIn through Dog.prototype.bornIn.
Dog.prototype = Animal;
That's the key part. Here we are telling to JavaScript that, when creating a new Dog, our instance should have a reference to the properties and methods of Animal. But how?
By inspecting the prototype chain.
Proto... WHAAAT?
Let's continue with the example.
var myDog = new Dog();
Here we are creating an instance of a Dog. Let's dig inside the resulting object.
// console.dir(myDog); { bark: function () { ... } bornIn: Tue Jun 1... // A Date instance __proto__: Object }
Where is the eat function? In the __proto__ object.
Here, __proto__, is a reference to the Animal object, and JavaScript looks into it to search for inherited members, such as the eat function.
// console.dir(myDog); { bark: function () { ... }, bornIn: Tue Jun 1..., // Our own bornIn function __proto__: { bornIn: function now() { [native code] }, eat: function () { ... }, __proto__: Object } }
Because of this we can do something like myDog.eat().
And notice the two bornIn functions. The second one is the Animal.bornIn function.
And also notice the second __proto__. It's a reference to the native Object.prototype.
// console.dir(myDog); { bark: function () { ... }, bornIn: Tue Jun 1..., // Our own bornIn function __proto__: { bornIn: function now() { [native code] }, eat: function () { ... }, __proto__: Object } }
So, the prototype chain looks like this:
Dog --> Animal --> Object --> null
Yes, an instance of a native Object has no prototype because it's the first one in the chain. And, if you check for the __proto__ property, it exists, but you will get a null value.
Now that you know how inheritance works in JavaScript, you must take care of the prototype chain, because it's as powerful as dangerous.
Try to not make large chains. It will give your app a better performance.
I know it's hard, because almost all developers come from Class-based languages, but I guarantee you that using prototypes will give you more power and flexibility while developing.
Prototypes are dynamic, Classes are static. Prototypes can change during runtime but it could also be a problem if you don't develop carefully.
But the main reason to don't use classes is that JavaScript doesn't have Classes and trying to imitate it will cause a loss of performance and you will also lose the good parts of using prototypes.
In a short, Modular Patterns dictates how to structure our code, namely in modules that are reusable pieces and let us develop loose coupled applications.
In JavaScript there are, primarily, 3 modular patterns definitions:
It's important to note that, in the current iteration of JavaScript (ECMA-262), it's impossible to define modules without the need of a script loader.
Fortunately, the next version, JavaScript Harmony is coming and it's already available in some environments, such as Node.js.
In the meantime we can use nice script loader libraries like RequireJS and curl.js.
I always recommend to start thinking in modules as Lego pieces.
By themselves, they are so simple but, by combining them, you can create amazing things.
Remember the Facade pattern?
It defines the way the pieces connect each other.
And by controlling it we can maintain our application loose-coupled.
What are the benefits of loose-coupling?
You can easily reuse your modules everywhere in the application or even outside it.
If a module changes or crashes, the application could continue working and you only lose the features of these modules.
And testing! It's easier to test a simple module than testing the whole application.
How modular patterns help with this?
We remove unnecessary dependecies.
The truly needed dependencies are well defined and, if you don't define them, your code will not work.
We can use optimizers like r.js to maintain it clean and controlled.
We can have different versions of the same module running in our application. Say, i.e. some module needs jQuery 1.x and another one needs jQuery 2.x.
Remember the Observer pattern?
By subscribing and publishing messages we can have our application decoupled and well communicated.
Messages are like events, but the difference is that events are attached to objects while messages are not. They can live on their own.
Of course not.
Classical Inheritance, or its emulation in JS, has its own niche. Sometimes the application performance is not critical and you can benefit from the expertise about Class-based thinking of your team.
Are Modular Patterns and Classical Inheritance compatible?
Yes, they are!
Is this architecture the best way of structuring your app?
The answer is: "It depends".
Depends on your app needs.
Module Patterns define a way to organize your code but, it's important to note that inside a module you can create objects, or define multiple "classes" or just create a "class" inheriting from another one defined in another module.
What I want to emphasize is that it's not a battle between Classical Inheritance and Module Patterns. They don't play in the same league.
So, after all...