The Strategy Pattern in JavaScript

I was recently re-factoring some code under Rick’s guidance, and we implemented what I would later recognize as the Strategy pattern. JavaScript’s objects and first-class functions make this pattern extremely simple to implement, although you can optionally take some additional steps to add robustness.

Background: Design Patterns

A lot of my troubles with design patterns come from understanding their goal and recognizing appropriate situations to use them. In fact, many people begin practicing them without any formal education into design patterns. This leads many seasoned programmers to respond with, “well, duh” when first confronted. Design patterns are derived from scrutiny of best practices in the real world (not your old CS prof’s black cauldron). They can seem artificial because they have been abstracted to describe general programming paradigms. That means any discussion of a specific pattern should really begin with an explanation of use cases–keep reading!

Motivation: Why Strategy?

Abstractly speaking, the Strategy pattern is relevant whenever you have a number of algorithms (or some combination of functions and inputs) that share some common behavior. Put another way, try using this pattern whenever you have a single Goal to accomplish with a number of Approaches. Here are some concrete examples, with the Goal and Approaches highlighted:

Implementation

That last example is exactly what Boaz discussed in his most recent blog post. Let’s use a stripped-down version of his code to see how simple this pattern can be:

(You can see his inspiration in this Gist from Rick, where the handlers object holds dummy Approaches.) Each property of the buttons object represents a unique button. This code recognizes the common aspects (the Goals: label the button and perform some action) that are shared by each unique button (the Approaches: log in, log out, get contacts). Now that this relationship has been set up, we can leverage its representational power:

This code leverages the common interface (Goals) we teased out of each button (Approaches). We simply iterate over the controls object, confident that each member has some label and action. In this way, we’ve saved ourselves from having to write blocks of redundant code (you can see what I mean here). Adding new buttons is also much easier because you only need to define the unique aspect of each—no need to retrace through the code adding logic for binding functions to buttons, etc.

Making it Robust

Although this is perfectly serviceable, there are steps we can take to make sure every Approach conforms to the same standard. Simply define a generic approach for the others to inherit from:

Using this Button object admittedly adds a small amount of code to the buttons definition, for example:

getContacts: { /* ... */ } becomes getContacts: new Button({ /* ... */ })

(See here for the complete definition.) In return, we’ve built a clear contract of what each Button provides.

Runtime

So far, I have motivated the use of this pattern for object instantiation. While this makes code more readable and maintainable, it still might not be clear how this increases the power of the code. Consider another example (as mentioned earlier, working with audio tones):

Once again, the data structures and methods unique to each approach have been teased out into dedicated objects. By defining the wave object in this way, the rest of the code can be written without any regard for the unique implementation details of continuous and discrete waves. More importantly, we can switch implementations at any time with one simple line of code: wave = waveImplementations.continuous;

This entry was posted by Mike Pennisi (@jugglinmike) on June 20, 2011 in JavaScript, OOP and Open Web Apps.

Comments

Training

We offer comprehensive beginner and advanced jQuery, JavaScript and HTML5 training onsite and at The Bocoup Loft in Boston, MA.

Check out our upcoming courses:

More about training →

Categories

AJAX, Apache, Arduino, Audio, audio data api, Augmented Reality, Backbone, Bash, Bocoup, boston, Build Tool, Burst, burst-engine, Canvas, Chrome, Chromium, conflict, CouchDB, CSS, CSS3, Data, Data Visualization, EventSource, facebook, Feature Detection, Fieldrunners, Firebug, Firefox, fx4, Games, Gecko, Git, Google, Google Analytics, Grunt, hardware, heart, hole, html, HTML5, inset, Internet Explorer, Inventions, JavaScript, Johnny-Five, jQuery, jQuery Plugins, jQuery.Hive, JSARToolkit, Linux, list, Media, merge, Minefield, Mobile, moz10, mozilla, Node.js, OOP, Open Source, Open Web Apps, Opera, opposite-winding, Performance, PHP, PollenJS, Popcorn, Popcorn.js, primatives, Processing.js, RaphaëlJS, Regular Expressions, Ringmark, Ringmark Gallery, Safari, Security, shadow, social, Social Networking, specs, SpiderMonkey, sprite-viking, Standards, SVG, Sys Admin, terminal, Testing, Tools, Training, Tutorial, ubuntu, Uncategorized, Uselessware, V8, Video, Visualization, VML, W3C, Web Project Management, Web Workers, webgl, WebKit, WebRTC, wget, Workflow, wtfjs and xml.

Twitter