bringing classical oop into javascript
DESCRIPTION
Any diligent developer is constantly working on improving his or her code. There are plenty of books telling how to make your code better. However most of them use the language of class-based OOP. Reader must have enough of experience to reflect those classical design patterns on JavaScript. Thus, many and many developers give up on the language before mastering its true power. On the other hand, JavaScript is an incredible impressive language. I wonder if JavaScript was originally designed as a foundation on which you build your own language. About 2 years ago I had worked out a solution which developed by now into a light-weight library which brings into JavaScript everything I miss in the language as a follower of class-based OOP. All about how it is implemented and how can be used is on the slides.TRANSCRIPT
Bringing classical OOP into JavaScript
By Dmitry Sheiko
Who's the dude?
I'm Dmitry Sheiko, a web developer, blogger, open source contributor.
http://dsheiko.com
@sheiko
https://github.com/dsheiko
Eager to be a better coder?
Reading helps…
Reflecting learned patterns on JavaScript
What the hell?! Where are all the classes, interfaces, members visibility, namespaces, mixins?!
JavaScript is a class-free language
“JavaScript: The World's Most Misunderstood Programming Language”
Yet JavaScript is incredibly expressive
“The expressiveness of JavaScript
provides an enormous amount of power.
Even though the language lacks certain
useful built-in features, its flexibility
allows you to add them yourself…”
Ross Harmes, Dustin Diaz. Pro JavaScript Design Patterns
Make of JavaScript the language you need
As a class-based OOP programmer I would bring to JavaScript following : •classes with private/public members•classical inheritance (abstract class -> class -> .. -> final class) •interfaces•mixins (traits) •type hinting •entry point validators
What look like objects in JavaScript
Somehow clumsy, isn’t it?
var o, SuperTypeConstruct = function() {}, ConstructorFunc = function() { var _privateMember = "private member"; this.publicMember = "public member"; this.privilegedMethod = function() { return _privateMember; }}ConstructorFunc.prototype = new ConstructorFunc();o = new ConstructorFunc();
What I want it to look like
var ConcreteClass = function() { // Constructor's job var _privateMember = "private member"; return { __extends__: AbstractClass, __implements__: [Countable, Traversable], __mixin__: [Trait], publicMember: "public member", privilegedMethod: function() { return _privateMember; } }}
Can it inherit?
With a factory it can:Function.prototype.createInstance = function () { var key, module = this, members = module.apply(this, arguments), Fn = function () {}; members.hasOwnProperty( "__extends__" ) && members[ "__extends__" ] && (module.prototype = members[ "__extends__" ].createInstance()); Fn.prototype = module.prototype; // Link to the supertype for (key in members) { // Mix in members if (members.hasOwnProperty(key)) { Fn.prototype[key] = members[key]; } } return new Fn(); };
What about interfaces, mixins and so on?
We add with a hook any object creation control flow that we wish. Let’s just change a bit the factory:
Function.prototype.createInstance = function () { ... instance = new Fn(); jsa.Hook.invokeAll( instance, arguments ); return instance;};
JSA Way
JSA is a light-weight library comprising factory plugins to “make of JavaScript the language I want”. Let’s see what they are.
Widget foundation class
As good programmers we learn from the great ones, don’t we? So, let’s borrow from YUI guys some ideas of abstract widget (http://yuilibrary.com/yui/docs/widget/) . Videlicet:• Common bootstrap interface• Consistent node referencing • Built-in progressive enhancement support
Widget abstract layer in JSA
Widget plugin declares BaseAbstract class, which makes the factory (via a hook) to auto-call bootstap methods (init, renderUI, syncUI) of every class extending this one.
The plugin also declares WidgetAbstract, which makes the factory to populate node property with node references given in HTML_PARSER property of extending class
+syncUI()
-__extends__-HTML_PARSER-settings
WidgetAbstract
+init()+renderUI()+syncUI()
BaseAbstract
+init()+renderUI()+syncUI()
-__extends__-node-settings
ConcreteModule
Usage example(function( $, jsa ) {Widget = function( settings ) { // event handlers _handler = { onclick : function(e) { // do something } }; return { __extends__ : jsa.WidgetAbstract, HTML_PARSER : { toolbar : 'div.toolbar' }, syncUI : function() { this.node.toolbar.find( 'li' ).bind( 'click.intro', this, _handler.onclick ); } }; }; $(document).bind( 'ready.app', function () { // Document is ready Widget.createInstance({ boundingBox: $('div.intro') }); });})( jQuery, jsa );
Mixins
Mixins provide aggregation (has-a) relation between objects, which is easy to implement especially is JavaScript. Mixin plugin only assigns a hook, which makes factory mix public members of objects given in mixin property
Mixin
+init()+renderUI()+syncUI()
-__extends__-__mixin__
ConcreteClass
1
*
Usage example
var MixinA = { propertyA: "propertyA" }, MixinB = { propertyB: "propertyB" }, Silo = function() { return { __mixin__: [MixinA, MixinB ], ownPropery: "Own property" } },o = Silo.createInstance();console.log(o.ownPropery !== undefined ); // trueconsole.log(o. propertyA !== undefined ); // trueconsole.log(o. propertyB !== undefined ); // true
Interfaces
Interface plugin assigns a hook, which checks if the newly born object meets the requirements of the interfaces enlisted in implement property. In order to make sure arguments match type hints, the plugin wrap the method with cross-cutting functionality, which does the check on entry point.
-__implements__
ConcreteClass
Interface
*
Usage example
var ConcreteInterface = { requeriedMethod : ["string”] }, StrictModule = function() { return { __implements__: ConcreteInterface, requeriedMethod : function() { } } }, o = StrictModule.createInstance();// Test Module.requeriedMethod('a string'); // OKModule.requeriedMethod(555); // throws TypeError exception
Design by Contract
Design by Contract approach provides another and more sophisticated solution of defining requirements for the objects of a type. By a contract we can define entry/exit point conditions.
-__contract__
ConcereClass
Contract
*
var Contract = { methodName: { onEntry: [ "number", aClass ], // type hints validators: [ function( arg ) { return arg > 10; }, secondArgValidator ], onExit: "string" }}
Usage example
var ConcreteContract = { methodA : { onEntry: [ "number" ], validators: [ function(arg){ return arg > 10; } ], onExit: "string" }},EmployedModule = function() { return { __contract__: ConcreteContract, aMethod : function() { return "a string"; } }}, o = EmployedModule.createInstance();o.aMethod( 50 ); // OKo.aMethod( 1 ); // Validator fails, RangeError exception is thrown
Fork me!
JSA project page:
Articles relevant to JSAhttp://dsheiko.com/weblog/js-application-designhttp://dsheiko.com/weblog/prototypal-inheritance-in-javascript-for-moduleshttp://dsheiko.com/weblog/design-by-contract-and-js