JavaScript OOP: encapsulation, durable objects, parasitic inheritance and the decorator pattern
I've been working to eliminate this
and its associated complexity from my JavaScript vocabulary. I haven't written an articulate blog post about the difficulties this
causes but there are many and perhaps I will write that article some day. I've gone on and off with my quest over time and made another push this past week to rid my life of this
. I think that after just over two years of experimenting and blogging about JavaScript OOP, I may have finally seen the light.
In my search for the great OOP solution, I was really looking for something that allowed for proper encapsulation and structured code reuse without any hint of type declarations. I wasn't necessary searching for a message passing OOP system or to simulate class
. I was searching for whatever it is that JavaScript does most naturally. JavaScript's prototypal inheritance system is interesting and more flexible than classical inheritance schemes; however, even the prototypal system is difficult going most of the time. I imagine all JavaScript programmers have struggled to make the prototypal system work for them. The prize waiting for me was, of seems to be usual, rooted in the wonders of closures (a.k.a lambdas or just function
in JavaScript).
Douglas Crockford has written about durable objects and parasitic inheritance which concern encapsulation and code reuse, respectively. Along my exploratory path I had read his articles and watched his talks on the web about these and other topics. I knew they were all good concepts for one thing or another but I didn't see how the durable object and parasitic inheritance concepts in particular comprised most of the solution I sought. I pursed various OOP styles. When I arrived at what feels like the end of my search, I realized I had essentially combined these two concepts which Douglas has evangelized.
I now also see that parasitic inheritance is really The Decorator Pattern in disguise. The decorator pattern gives the benefits of code reuse supplied by extends
-type inheritance without the drawbacks of the template pattern also supplied by extends
-type inheritance. Adding the decorator pattern explicitly into the mix with durable objects and parasitic inheritance is a powerful and natural fit.
Below is a small code snip which shows the essential concepts. I modified an example from the decorator pattern chapter of Head First Design Patterns by Freeman, Freeman, Sierra and Bates (which, by the way, is a great book and more accessible than the more in depth and classic Gang of Four Design Patterns by Gamma, Helm, Johnson and Vlisides.) The Head First book has its examples in Java which is a class-based language. Most books on design patterns use class-based languages which makes it more difficult to find the natural way to write JavaScript programs. Where are all the books on design patterns for closure-based languages? Ok, on to the example...
// makes base item
var makeHouseBlend = function() {
return {
toString: function() {
return 'House Blend';
},
toDollars: function() {
return 0.89;
}
};
};
// makes different base item
var makeEspresso = function() {
return {
toString: function() {
return 'Espresso';
},
toDollars: function() {
return 1.99;
}
};
};
// Decorate (actually modifies by wrapping certain properties)
// a base item or a base item that has already been decorated.
// Cream costs extra.
var decorateWithMilk = function(beverage) {
var originalToString = beverage.toString;
beverage.toString = function() {
return originalToString() + ', Milk';
};
var originalToDollars = beverage.toDollars;
beverage.toDollars = function() {
return originalToDollars() + 0.20;
};
return beverage;
};
// Decorate a beverage with a different condiment.
// Sugar is free.
var decorateWithSugar = function(beverage) {
var originalToString = beverage.toString;
beverage.toString = function() {
return originalToString() + ', Sugar';
};
return beverage;
};
// Watch the changes as my coffee is made in stages.
// House blend with milk and sugar
var myCoffee = makeHouseBlend();
myCoffee.toString(); // 'House Blend'
myCoffee.toDollars(); // 0.89
myCoffee = decorateWithMilk(myCoffee);
myCoffee.toString(); // 'House Blend, Milk'
myCoffee.toDollars(); // 1.09
myCoffee = decorateWithSugar(myCoffee);
myCoffee.toString(); // 'House Blend, Milk, Sugar'
myCoffee.toDollars(); // 1.09
// double milk espresso
var yourCoffee =
decorateWithMilk(decorateWithMilk(makeEspresso()));
yourCoffee.toString(); // 'Espresso, Milk, Milk'
yourCoffee.toDollars(); // 2.39
Suppose a house blend coffee with milk and sugar is a very common order. writing decorateWithSugar(decorateWithMilk(makeHouseBlend())) or pressing three buttons on the cash register for each order is a bit tedious. It may be worth our while to write a special maker function for this particularly common drink and map it to a key on the cash register.
var makeSugarMilkHouseBlend = function() {
decorateWithSugar(decorateWithMilk(makeHouseBlend()));
};
var myCoffee = makeSugarMilkHouseBlend();
myCoffee.toString(); // 'House Blend, Milk, Sugar'
myCoffee.toDollars(); // 1.09
The above maker function does the same job that one of Douglas' parasitic inheritance maker functions does. I haven't seen an example before of parasitic inheritance where its decorator pattern nature has been explicitly extracted. The examples I have seen would have the decorating happening directly in the makeSugarMilkHouseBlend
function which reduces the reusability of the decorating logic. The decorator pattern used this way also hints towards Ruby's mixin. It also displays the characteristics of the bottom-up style of programming.
I'm working on an article which shows how all of these concepts fit together in a browser scripting environment with the whole works from XMLHttpRequest
up through DOM manipulation. If I was writing a book on browser scripting, the article would be the last chapter: the chapter which uses all the concepts from all the previous chapters to write a full client-side application with some sort of MVC-equivalent architecture. My JavaScript Widgets without this
article is peak at some of the view-related concepts.
This seems like a very comfortable JavaScript OOP solution that will work well for the majority of browser scripting task. I certainly feel like the major struggle is behind me and I am now at peace with JavaScript OOP.
Comments
Have something to write? Comment on this article.
Andy B,
I just purchased Douglas Crockford's book today thanks to your prompting. I've been meaning to get it for a while. I think because I have studied many of Douglas' articles already, the book was a very quick read. The book is a great idea and I wish I had it a few years ago. I don't agree with all of Douglas' practices (e.g. augmented built-in objects!!), but reading a thoughtful programmer's discussion of good and bad parts in the language provokes more thought on my part. Unfortunately, I think the seven pages of the book which deal with the type of OOP I've discussed in this article are far too minimal. It is an extremely rich topic, about which Douglas' appears to have spent many hours thinking, and has large impact on day-to-day work as a programmer. This section of the book could have been much longer to the benefit of the reader's programming and enjoyment of the book.
I'll have to check out Pro JS Patterns at some point. I've been meaning to do that for a while as well.
What I've really been seeking is a book on design patterns in a different functional language (e.g. Scheme) with a different community of developers their set of tricks the JavaScript community hasn't discovered yet.
Thanks for the prod.
Yes I can definitely see why you would have burned through Crockford's book so fast! I agree, it could have been treated in a lot more depth, but perhaps his intention with the book was to provide a good platform/reference for developers to experiment and educate themselves, rather than lay out a complete solution.
I agree that a patterns book in relation to functional programming would be an interesting read, and seeing new techniques from outside the JavaScript community is always welcome! Resig and Oliver Steele are particularly active in these areas.
Have something to write? Comment on this article.
Great stuff Peter, very interesting series of articles in the last few days, particularly concerning the removal of
this
from your objects.Have you read Crockford's book? He shares the same view and advocates a pattern of inheritance dubbed 'Functional' which I belive relates to his parasitic inheritance model, and utilises closures for all the private stuff.
Regarding patterns, I found Pro JS Patterns by Diaz & Harmes a good read, and understandable (they cover a mixture of organisational, creation, and optimisation patterns) and also cover the decorator pattern in a similar manner to yourself (except they impose greater constraints by emulating interfaces between Classes.
Anyway, just wanted to say thanks for the interesting and insightful reads, and looking forward to more guidance on pragmatic JS authoring :-)