You are viewing t3knomanser

t3knomanser's Fustian Deposits

JavaScript Design Patterns (Gang of Remy)

How Random Babbling Becomes Corporate Policy

IOCCC Original Winner

Mad science gone horribly, horribly wrong(or right).

JavaScript Design Patterns (Gang of Remy)

Previous Entry Share Next Entry
IOCCC Original Winner

Javascript Design Patterns


I'm a huge fan of Design Patterns. For those that might not be familiar with Patterns, the summary is this: Design Patterns are reusable fragments of application designs that can be used to solve a variety of problems. Pre-designed solutions that you can plug into your own application, and thus minimize development time and reduce program complexity. If you do any programming and don't know these learn them!

Now, the GoF patterns were designed around traditional OOP languages. In their book, they used C++ and Smalltalk- the latter being one of the original OOP languages, the former one of the most well respected and recognized. Patterns became extremely popular in response to Java development- another traditional OOP languages.

JavaScript is not a traditional language. In fact, it has some pretty important things that make it different.

JavaScript's Differences


If we look at languages like C++ and Java, we notice some things right away. First, the languages are strongly typed. Every variable holds a specific kind of data. While JavaScript is not unique as a weakly typed language- nearly every scripting language is weakly typed- type management means some pretty significant differences in how things behave. Polymorphism, a key component of traditional OOP languages isn't nearly as important in JavaScript.

The biggest difference- one that makes the biggest changes in how things work- is Prototypes. Most OOP languages don't have a concept like this. In C++ or Java, when you define a class, you've defined the class. Its implementation is static in your application. JavaScript makes no such promises. when you define a class, what you're really doing is establishing the prototype for all instances of that class. At runtime, I can add and remove methods and properties via that prototype. This renders certain design patterns needless and opens up the opportunity for new patterns.

Speaking of things that are volatile- not only can class definitions be revised at runtime via the Prototype, functions can also be revised. Functions are just data, and can be manipulated at runtime. This makes the passing of callbacks trivial, but there are issues that develop around closures. Once again, in traditional OOP languages, class methods are defined as part of the class and are static- it takes some OOP wizardry to get anything approaching this kind of flexibility.

Don't Confuse Flexibility with "Good"


Flexibility is like oxygen. Too little oxygen, and a living thing becomes torpid and eventually suffocates. Too much oxygen, and a very small spark can cause an explosion. A language that isn't flexible enough isn't useful. A language that is too flexible is a bomb waiting to go off. JavaScript is extremely flexible- and this makes it important that we as programmers learn to use that flexibility wisely. This is where design patterns come into play- they allow us to approach flexibility in a structured manner to control complexity.

Patterns in JavaScript


I'm not going to attempt to discuss how to port every concievable pattern into a JavaScript implementation. What I want to do is discuss a few patterns that are specific to Prototypes, and then take a look at how these patterns boil can be used to streamline other, more traditional patterns.

Prototypes and Patterns


Mixin


The Mixin pattern is an incredibly powerful pattern. We will, later, use it to implement many other, more traditional patterns. It has no analogue in traditional OOP patterns, although some- like the Decorator- imitate it.

The problem is this- we have generic functionality that we want to easily apply to a variety of classes. In a traditional OOP language, we might look at using Inheritance, or patterns like the Delegate and Decorator- any way we can generalize and extract certain functionality without having to duplicate it.

Thanks to Prototypes in JavaScript, we have a simple, safe way of achieving this functionality. Let's say I have a function foo and a property bar. There are ten classes that all need to have foo and bar. In a traditional OOP language, I would probably apply the Delegate pattern- I would implement another class that contains foo and bar, then make it a member of those ten classes. In JavaScript, however, I could simply do this:
  className.prototype.foo = foo;
  className.prototype.bar = bar;

Repeat as needed for each class. Of course, if I want to make it clean and reusable, I might push that work down into a function- something like this:
  function MixInFoo(classType)
  {
    classType.prototype.foo = function () {...}
    classType.prototype.bar = someProperty;
  }

Inside the function foo, I can take advantage of the fact that "this" in JavaScript always represents the caller. In function foo, "this" always represents the class instance that foo is attached to.

By writing functions that manipulate the Prototype, it's trivial to append and reuse functionality between a variety of classes. This makes implementing certain design patterns far easier and more reusable in JavaScript than in other languages.

Mixin - Observer


The Observer pattern is the general model for implementing an "event" architecture in most languages. The general scenario is that we have a two classes: the first class is going to mutate somehow, and the second class (the observer) wants to be notified of that event. We use events all the time when programming JavaScript- an Anchor offers a "click" event- we can write an observer class (or just a single callback function) that should be notified of that event. Unfortunately, we can't use the built in DOM event model for our own custom event handling. So we want to implement the Observer pattern.

We'll distinguish two roles- the Observer and the Notifier. The Notifier class is going to fire off the event, which means it needs to have some mechanism for tracking all of the Observers, and some mechanism for notifying all of the Observers. Generally, we'd track an array of callback functions and iterate across that array to send the notifications. Something like this:
  function NotifierClass()
  {
    this.listeners = new Array();
    this.addListener = function(callback)
    {
      this.listeners.push(callback);
    }
    this.fireEvent = function()
    {
      for (var i = 0; i < this.listeners.length; i++)
      {
        this.listeners[i]();
      }
    }
    this.doStuff = function()
    {
      //... do stuff ...
      this.fireEvent(); //notify the listeners.
    }
  }
  function ObserverClass()
  {
    this.handleEvent = function()
    {
      //... respond to the event ...
    }
  }
  var n = new NotifierClass();
  var o = new ObserverClass();
  n.addListener(o.handleEvent);
  n.doStuff();

This works, but it's not really reusable. We'd need to replicate this for every class that needs to fire off events, as well as every other event we might want to fire from the same class. This can be generalized a bit by using some of the alternatives for ECMAScript property access:
  function NotifierClass()
  {
    this.addListener = function(eventName, callback)
    {
      if (!this[eventName]) this[eventName] = new Array();
      this[eventName].push(callback);
    }
    this.fireEvent = function(eventName, args)
    {
      for (var i = 0; i < this[eventName]s.length; i++)
      {
        this[eventName][i](args);
      }
    }
    this.doStuff = function()
    {
      //... do stuff ...
      this.fireEvent("didStuff", {some:"object"}); //notify the listeners.
    }
  }
  function ObserverClass()
  {
    this.handleEvent = function(args)
    {
      //... respond to the event ...
      window.alert(args.some);
    }
  }
  var n = new NotifierClass();
  var o = new ObserverClass();
  n.addListener("didStuff", o.handleEvent);
  n.doStuff();

That doesn't remove the need to re-implement these methods in other classes, but this does let our notifier class have as many events as it needs. Notice how I also added an argument to the fireEvent function- that allows us to pass information about the event to the handlers.

Notice also, that the event notifying code doesn't have any strong relationship to the class itself. It's not accessing any private class members, and it doesn't need to- especially now that I've added a parameter to the fireEvent method. We have code that's generic, that we want to reuse between classes, and that is not strongly-coupled to the details of any specific class. This is the perfect situation to use the MixIn pattern.
  function Notifier(ClassType)
  {
    ClassType.prototype.addListener = function(eventName, callback)
    {
      if (!this[eventName]) this[eventName] = new Array();
      this[eventName].push(callback);
    }
    ClassType.prototype.fireEvent = function(eventName, args)
    {
      for (var i = 0; i < this[eventName]s.length; i++)
      {
        this[eventName][i](args);
      }
    }
  }
  function NotifierClass()
  {   
    this.doStuff = function()
    {
      //... do stuff ...
      this.fireEvent("didStuff", {some:"object"}); //notify the listeners.
    }
  }
  Notifier(NotifierClass) //Here's where the real magic happens!
  function ObserverClass()
  {
    this.handleEvent = function(args)
    {
      //... respond to the event ...
      window.alert(args.some);
    }
  }
  var n = new NotifierClass();
  var o = new ObserverClass();
  n.addListener("didStuff", o.handleEvent);
  n.doStuff();

The line Notifier(NotifierClass) executes the Notifier method, which appends methods to the NotifierClass prototype using the MixIn pattern. Voila! I can now call Notifier(anyclass) to turn that class into an event-firing machine.

Decorator


The Decorator pattern is an attempt to synchronize interfaces but change functionality in a predictable fashion. An underlying class is manipulated by the Decorator object- which happens to have the same interface. When I invoke doStuff on the Decorator object, it does soemthing and invokes doStuff on the underlying object. Something like this:
function ConcreteClass()
{
	this.doStuff = function() {window.alert("Concrete DoingStuff");}
}

function AbstractDecorator(decorated)
{
	this.doStuff = function()
	{
		this.preCall();
		decorated.doStuff();
		this.postCall();
	}
}

function ConcreteDecorator(decorated)
{
	this.base = AbstractDecorator;
	this.base(decorated);
	
	this.preCall = function()
	{
		window.alert("precalling...");
	}
	
	this.postCall = function()
	{
		window.alert("postcalling...");
	}
}
var conc = new ConcreteClass();
var dec1 = new ConcreteDecorator(conc);
var dec2 = new ConcreteDecorator(dec1);
dec2.doStuff();

The output of the above: "precalling...", "precalling...", "Concrete DoingStuff", "postcalling", "postcalling"
Unlike the traditional OOP version, there's no need to inherit from a common interface- JavaScript doesn't support polymorphism, because it's weakly typed. That's actually a bad thing- there's no compile-time checking of the interface. I could write some cumbersome "typeof" conditions to ensure that they match up, or I could take advantage of JavaScript's flexibility to create an "AutoDecorator" class.

AutoDecorator


The following example uses some JavaScript wizardry to dynamically discover and generate an interface. This has the advantage of being a single class that can be used to decorate any object- promoting code reuse. However, it carries a pretty significant performance overhead, and should be used very carefully. My goal in including it is to demonstrate some of the things that can be done with JavaScript- but remember my warning about flexibility. We must be careful about how we apply it. This generic class offers some advantages- especially in that we don't need to define the interface- but how often do we really need to use something this flexible? Rarely, if ever.
function AutoDecorator(decorated)
{
	//By delegating the build to here, we actually benefit from closures.
	function buildFunction(key) //You see, KEY here is going to retain its state
	{ 							//The KEY below won't.
		return function()		//This does have GC implications.
		{
			//if the precalls array exists
			if (this[key].precalls)
			{
				//invoke each pre-call.
				for (var i = 0; i < this[key].precalls.length; i++)
				{
					this[key].precalls[i].apply(decorated, arguments);
				}
			}
			//Call the underlying method
			decorated[key].apply(decorated, arguments);
			if (this[key].postcalls)
			{
				//Do all the postcalls.
				for (var i = 0; i < this[key].postcalls.length; i++)
				{
					this[key].postcalls[i].apply(decorated, arguments);
				}
			}
		};
	}
	//start by looping across all the members of decorated
	for (var key in decorated)
	{
		//functions are a special case
		if (typeof(decorated[key]) == "function")
		{
			//declare a function
			this[key] = buildFunction(key); //We're going to delegate the act of building the decorating function
			this[key].precalls = new Array(); //init the call-arrays
			this[key].postcalls = new Array();
		} else { //all other properties are read-only
			this[key] = function() { return decorated[key]; }
		}
	}
}

This class detects the interface of the decorated class, and based on that interrogation, it creates its own interface that is similar to that of the class being decorated. Each decorated method has two new properties- precalls and postcalls. These are arrays- allowing the decorator to append a large number of calls to the decorated class methods without altering the decorated class in the least.

This particular example does an extremely good job demonstrating the core differences between JavaScript and traditional OOP. In a traditional language, we would have specified a distinct Interface for this pattern- the Decorator and the Decorated would both implement the same interface for polymorphisms sake. In JavaScript, we can achieve the same goal by simply defining classes with similar prototypes. However, since we don't have polymorphism, we have the ability to generalize this pattern to the point where there is a single Decorator for every decoration we want to provide. We can also use the MixIn pattern to append functionality to the Decorator, allowing us to completely separate the details of implementing the Observer pattern from the class to be observed:
var c = new ConcreteClass();
var ad = new AutoDecorator(c);
Notifier(ad);
ad.doStuff.postcall = function() {ad.fireEvent("didStuff");};

Conditional logic could of course be added that inspects the states of the objects involved before firing any events. Again, this sort of flexibility is probably overkill for most practical applications.

Strategy


The Strategy pattern is for solving a problem when the exact solution isn't known until run-time. In this pattern, a class would have a method "solveProblem"- which merely calls out to a supporting class that actually does the work of solving the problem. This allows run-time binding of code to calls.

This pattern can be implemented straight in JavaScript, and there are plenty of reasons to do so. But do remember, JavaScript allows binding of functions at runtime without the overhead of creating an entire class. For example:
function add(a, b) { return a + b; }
function subtract(a, b) { return a - b; }
function Operation()
{
  this.solveProblem = function(a, b) { window.alert(this.solution(a, b)); }
}

var o = new Operation();
o.solution = add;
o.solveProblem(10, 12);

MVC


A discussion of design patterns and JavaScript is not going to be complete without discussing the MVC pattern- Model-View-Controller. This is the main pattern for use in front-end layers, and this includes your AJAX application. The Model class represents your data-layer, the information you want to represent. This would be your JSON or XML response in an AJAX application. The View is the display- in the case of most web apps, we're talking about the DOM here. Generally, we'll wrap a class around the DOM to narrow the field of operations to specifically what we need. Finally, the Controller is the logic that moves data between the View and the Model.

While this will be the most oft-used pattern, there's nothing really exciting or shocking about applying it in JavaScript. It's a very standard pattern and doesn't need to vary much across platforms. Further- it's been covered to death in much of the AJAX literature. I don't have anything to really add to what's been said. In the realm of JavaScript- targeted for client display- the MVC is the standard pattern.

FHM


The FHM pattern is a special case of the MVC pattern. It is especially well suited to adapting complex server-side legacy web applications to AJAX. Really, it's just an overglorified use of IFrames.

FHM stands for Fragment-Host-Manager. In this pattern, a server-side page is sent to the client- the HOST. The HOST references the MANAGER, which retrieves the FRAGMENT from the server- literally, a page fragment- and embeds it into the output DOM. As I said- this is really just using JavaScript to do what IFrames can. It offers some advantages, but is hardly formalized into a true design pattern. Unlike normal AJAX models, it doesn't require XML parsing and offers lower overhead in exchange for surrendering flexibility.

Conclusion



These are a few Design Patterns adapted and developed for JavaScript. The important thing to keep in mind is not the details of individual patterns, but the goal of good software design. Patterns are a tool for designing good software, but they do not guarantee that your software well be well designed. They are easily misapplied, misunderstood and misused. Your end goal is to develop easy to maintain and usable applications. Apply patterns insofar as they help you reach that goal, but don't get enslaved to them.

I highly reccomend the MixIn pattern for JavaScript. It's a very powerful way for adding functionality to classes. Used in conjunction with the Decorator pattern, it allows features to be added without modifying the underlying classes- which is a great way to adapt complex code libraries to the specific task at hand. For many JavaScript tasks, however, that sort of flexibility might be more than is really required. JavaScript's functions-as-variables concept allows for lightweight implementations of things like the Strategy pattern, and are worth considering. I all but guarantee that you'll be using the MVC pattern in your JavaScript applications, even if you don't formally discuss it.

EDIT: Upon further testing, the AutoDecorator doesn't nest properly. That is to say, an AutoDecorator should be able to decorate another AutoDecorator. It doesn't, currently. I'll have to poke around in the code some more. This sort of problem is harder than normal to diagnose, because of the use of "eval"- which proves my point- you shouldn't use them.

EDIT AGAIN: I now have a working version of the AutoDecorator. It involves the use of closures, which are complex enough to require their own essay. I'll come back to that later. Closures are actually very important when one starts attempting to do code generation in JavaScript- I'll discuss them in a later essay to give a picture of the details.
Powered by LiveJournal.com