Bringing classical OOP into JavaScript

Post on 06-May-2015

1.627 views 0 download

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 of Bringing classical OOP into JavaScript

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