You are previewing Learning JavaScript Design Patterns.

Learning JavaScript Design Patterns

Cover of Learning JavaScript Design Patterns by Addy Osmani Published by O'Reilly Media, Inc.
  1. Learning JavaScript Design Patterns
  2. SPECIAL OFFER: Upgrade this ebook with O’Reilly
  3. Preface
    1. Target Audience
    2. Credits
    3. Reading
    4. Conventions Used in This Book
    5. Using Code Examples
    6. Safari® Books Online
    7. How to Contact Us
    8. Acknowledgments
  4. 1. Introduction
  5. 2. What Is a Pattern?
    1. We Already Use Patterns Every Day
  6. 3. “Pattern”-ity Testing, Proto-Patterns, and the Rule of Three
  7. 4. The Structure of a Design Pattern
  8. 5. Writing Design Patterns
  9. 6. Anti-Patterns
  10. 7. Categories of Design Patterns
    1. Creational Design Patterns
    2. Structural Design Patterns
    3. Behavioral Design Patterns
  11. 8. Design Pattern Categorization
    1. A Brief Note on Classes
  12. 9. JavaScript Design Patterns
    1. The Constructor Pattern
      1. Object Creation
      2. Basic Constructors
      3. Constructors with Prototypes
    2. The Module Pattern
      1. Object Literals
      2. The Module Pattern
      3. Module Pattern Variations
    3. The Revealing Module Pattern
      1. Advantages
      2. Disadvantages
    4. The Singleton Pattern
    5. The Observer Pattern
      1. Differences Between the Observer and Publish/Subscribe Pattern
      2. Advantages
      3. Disadvantages
      4. Publish/Subscribe Implementations
    6. The Mediator Pattern
      1. Basic Implementation
      2. Advanced Implementation
      3. Example
      4. Advantages and Disadvantages
      5. Mediator Versus Observer
      6. Mediator Versus Facade
    7. The Prototype Pattern
    8. The Command Pattern
    9. The Facade Pattern
      1. Notes on Abstraction
    10. The Factory Pattern
      1. When to Use the Factory Pattern
      2. When Not to Use the Factory Pattern
      3. Abstract Factories
    11. The Mixin Pattern
    12. Subclassing
    13. Mixins
      1. Advantages and Disadvantages
    14. The Decorator Pattern
    15. Pseudoclassical Decorators
      1. Interfaces
      2. Abstract Decorators
    16. Decorators with jQuery
    17. Advantages and Disadvantages
    18. Flyweight
      1. Using Flyweights
      2. Flyweights and Sharing Data
      3. Implementing Classical Flyweights
      4. Converting Code to Use the Flyweight Pattern
      5. A Basic Factory
      6. Managing the Extrinsic States
      7. The Flyweight Pattern and the DOM
  13. 10. JavaScript MV* Patterns
    1. MVC
      1. Smalltalk-80 MVC
    2. MVC for JavaScript Developers
      1. Models
      2. Views
      3. Controllers
      4. Controllers in Another Library (Spine.js) Versus Backbone.js
    3. What Does MVC Give Us?
    4. Smalltalk-80 MVC in JavaScript
      1. Delving Deeper
      2. Summary
    5. MVP
      1. Models, Views, and Presenters
      2. MVP or MVC?
      3. MVC, MVP, and Backbone.js
    6. MVVM
      1. History
      2. Model
      3. View
      4. ViewModel
      5. Recap: The View and the ViewModel
      6. Recap: The ViewModel and the Model
    7. Pros and Cons
      1. Advantages
      2. Disadvantages
    8. MVVM with Looser Data Bindings
    9. MVC Versus MVP Versus MVVM
    10. Backbone.js Versus KnockoutJS
  14. 11. Modern Modular JavaScript Design Patterns
    1. A Note on Script Loaders
    2. AMD
      1. Getting Started with Modules
      2. AMD Modules with Dojo
      3. AMD Module Design Patterns (Dojo)
      4. AMD Modules with jQuery
      5. AMD Conclusions
    3. CommonJS
      1. Getting Started
      2. Consuming Multiple Dependencies
      3. Loaders and Frameworks that Support CommonJS
      4. Is CommonJS Suitable for the Browser?
      5. Related Reading
    4. AMD and CommonJS: Competing, but Equally Valid Standards
      1. UMD: AMD and CommonJS-Compatible Modules for Plug-ins
    5. ES Harmony
      1. Modules with Imports and Exports
      2. Modules Loaded from Remote Sources
      3. Module Loader API
      4. CommonJS-like Modules for the Server
      5. Classes with Constructors, Getters, and Setters
      6. ES Harmony Conclusions
      7. Related Reading
    6. Conclusions
  15. 12. Design Patterns in jQuery
    1. The Composite Pattern
    2. The Adapter Pattern
    3. The Facade Pattern
    4. The Observer Pattern
    5. The Iterator Pattern
    6. Lazy Initialization
    7. The Proxy Pattern
    8. The Builder Pattern
  16. 13. jQuery Plug-in Design Patterns
    1. Patterns
    2. A Lightweight Start Pattern
      1. Further Reading
    3. Complete Widget Factory Pattern
      1. Further Reading
    4. Nested Namespacing Plug-in Pattern
      1. Further Reading
    5. Custom Events Plug-in Pattern (with the Widget Factory)
      1. Further Reading
    6. Prototypal Inheritance with the DOM-to-Object Bridge Pattern
      1. Further Reading
    7. jQuery UI Widget Factory Bridge Pattern
      1. Further Reading
    8. jQuery Mobile Widgets with the Widget Factory
    9. RequireJS and the jQuery UI Widget Factory
      1. Usage
      2. Further Reading
    10. Globally and Per-Call Overridable Options (Best Options Pattern)
      1. Further Reading
    11. A Highly Configurable and Mutable Plug-in Pattern
      1. Further Reading
    12. What Makes a Good Plug-in Beyond Patterns?
      1. Quality
      2. Code Style
      3. Compatibility
      4. Reliability
      5. Performance
      6. Documentation
      7. Likelihood of maintenance
    13. Conclusions
    14. Namespacing Patterns
    15. Namespacing Fundamentals
      1. Single Global Variables
      2. Prefix Namespacing
      3. Object Literal Notation
      4. Nested Namespacing
      5. Immediately Invoked Function Expressions (IIFE)s
      6. Namespace Injection
    16. Advanced Namespacing Patterns
      1. Automating Nested Namespacing
      2. Dependency Declaration Pattern
      3. Deep Object Extension
      4. Recommendation
  17. 14. Conclusions
  18. A. References
  19. Index
  20. About the Author
  21. Colophon
  22. SPECIAL OFFER: Upgrade this ebook with O’Reilly
  23. Copyright
O'Reilly logo

MVC for JavaScript Developers

We’ve reviewed the 1970s, but let us now return to the here and now. In modern times, the MVC pattern has been applied to a diverse range of programming languages, including of most relevance to us: JavaScript. JavaScript now has a number of frameworks boasting support for MVC (or variations on it, which we refer to as the MV* family), allowing developers to easily add structure to their applications without great effort.

These frameworks include the likes of Backbone, Ember.js, and AngularJS. Given the importance of avoiding “spaghetti” code, a term that describes code that is very difficult to read or maintain due to its lack of structure, it’s imperative that the modern JavaScript developer understand what this pattern provides. This allows us to effectively appreciate what these frameworks enable us to do differently (Figure 10-1).

MVC pattern

Figure 10-1. MVC pattern

We know that MVC is composed of three core components, described in the following sections.

Models

Models manage the data for an application. They are concerned with neither the user-interface nor presentation layers but instead represent unique forms of data that an application may require. When a model changes (e.g., when it is updated), it will typically notify its observers (e.g., views, a concept we will cover shortly) that a change has occurred so that they may react accordingly.

To understand models further, let us imagine we have a JavaScript photo gallery application. In a photo gallery, the concept of a photo would merit its own model, as it represents a unique kind of domain-specific data. Such a model may contain related attributes such as a caption, image source, and additional metadata. A specific photo would be stored in an instance of a model, and a model may also be reusable. Below, we can see an example of a very simplistic model implemented using Backbone.

var Photo = Backbone.Model.extend({

    // Default attributes for the photo
    defaults: {
      src: "placeholder.jpg",
      caption: "A default image",
      viewed: false
    },

    // Ensure that each photo created has an `src`.
    initialize: function() {
       this.set( { "src": this.defaults.src} );
    }

});

The built-in capabilities of models vary across frameworks, however it is quite common for them to support validation of attributes, where attributes represent the properties of the model, such as a model identifier. When using models in real-world applications we generally also desire model persistence. Persistence allows us to edit and update models with the knowledge that its most recent state will be saved in either memory, in a user’s localStorage data store, or synchronized with a database.

In addition, a model may have multiple views observing it. If, say, our photo model contained metadata, such as its location (longitude and latitude), friends who were present in the photo (a list of identifiers), and a list of tags, a developer may decide to provide a single view to display each of these three facets.

It is not uncommon for modern MVC/MV* frameworks to provide a means to group models together (e.g., in Backbone, these groups are referred to as “collections”). Managing models in groups allows us to write application logic based on notifications from the group should any model it contains be changed. This avoids the need to manually observe individual model instances.

A sample grouping of models into a simplified Backbone collection can be seen here.

var PhotoGallery = Backbone.Collection.extend({

    // Reference to this collection's model.
    model: Photo,

    // Filter down the list of all photos 
    // that have been viewed
    viewed: function() {
        return this.filter(function( photo ){ 
           return photo.get( "viewed" ); 
        });
    },

    // Filter down the list to only photos that 
    // have not yet been viewed
    unviewed: function() {
      return this.without.apply( this, this.viewed() );
    }
});

Older texts on MVC may also refer to a notion of models managing application state. In JavaScript applications, state has a different connotation, typically referring to the current “state”—i.e., view or subview (with specific data) on a users screen at a fixed point. State is regularly discussed when looking at single-page applications, where the concept of state needs to be simulated.

So to summarize, models are primarily concerned with business data.

Views

Views are a visual representation of models that present a filtered view of their current state. While Smalltalk views are about painting and maintaining a bitmap, JavaScript views are about building and maintaining a DOM element.

A view typically observes a model and is notified when the model changes, allowing the view to update itself accordingly. Design pattern literature commonly refers to views as “dumb,” given that their knowledge of models and controllers in an application is limited.

Users are able to interact with views, and this includes the ability to read and edit (i.e., get or set the attribute values in) models. As the view is the presentation layer, we generally present the ability to edit and update in a user-friendly fashion. For example, in the former photo gallery application we discussed earlier, model editing could be facilitated through an “edit” view where a user who has selected a specific photo could edit its metadata.

The actual task of updating the model falls to controllers (which we will be covering shortly).

Let’s explore views a little further using a vanilla JavaScript sample implementation. Below we can see a function that creates a single photo view, consuming both a model instance and a controller instance.

We define a render() utility within our view, which is responsible for rendering the contents of the photoModel using a JavaScript templating engine (Underscore templating) and updating the contents of our view, referenced by photoEl.

The photoModel then adds our render() callback as one of its subscribers so that through the Observer pattern we can trigger the view to update when the model changes.

One may wonder where user interaction comes into play here. When users click on any elements within the view, it’s not the view’s responsibility to know what to do next. It relies on a controller to make this decision for it. In our sample implementation, this is achieved by adding an event listener to photoEl, which will delegate handling the click behavior back to the controller, passing the model information along with it in case it’s needed.

The benefit of this architecture is that each component plays its own separate role in making the application function as needed.

var buildPhotoView = function ( photoModel, photoController ) {

  var base = document.createElement( "div" ),
      photoEl = document.createElement( "div" );

  base.appendChild(photoEl);

  var render = function () {
          // We use a templating library such as Underscore
          // templating which generates the HTML for our 
          // photo entry
          photoEl.innerHTML = _.template( "#photoTemplate" , {
              src: photoModel.getSrc()
          });
      };

  photoModel.addSubscriber( render );

  photoEl.addEventListener( "click", function () {
    photoController.handleEvent( "click", photoModel );
  });

  var show = function () {
    photoEl.style.display = "";
  };

  var hide = function () {
    photoEl.style.display = "none";
  };

  return {
    showView: show,
    hideView: hide
  };

};

Templating

In the context of JavaScript frameworks that support MVC/MV*, it is worth briefly discussing JavaScript templating and its relationship to views, as we briefly touched upon it in the last section.

It has long been considered (and proven) a performance bad practice to manually create large blocks of HTML markup in memory through string concatenation. Developers doing so have fallen prey to unperformant iterating through their data, wrapping it in nested divs, and using outdated techniques such as document.write to inject the “template” into the DOM. As this typically means keeping scripted markup inline with our standard markup, it can quickly become both difficult to read and, more importantly, maintain such disasters, especially when building nontrivially sized applications.

JavaScript templating solutions (such as Handlebars.js and Mustache) are often used to define templates for views as markup (either stored externally or within script tags with a custom type—e.g., text/template) containing template variables. Variables may be delimited using a variable syntax (e.g., {{name}}), and frameworks are typically smart enough to accept data in a JSON form (which model instances can be converted to), such that we only need be concerned with maintaining clean models and clean templates. Most of the grunt work to do with population is taken care of by the framework itself. This has a large number of benefits, particularly when opting to store templates externally, as this can give way to templates being dynamically loaded on an as-needed basis when it comes to building larger applications.

Here, we can see two examples of HTML templates (Examples 10-1 and 10-2). One implemented using the popular Handlebars.js framework and another using Underscore’s templates.

Example 10-1. Handlebars.js code

<li class="photo">
  <h2>{{caption}}</h2>
  <img class="source" src="{{src}}"/>
  <div class="meta-data"> 
    {{metadata}}
  </div>
</li>

Example 10-2. Underscore.js Microtemplates

<li class="photo">
  <h2><%= caption %></h2>
  <img class="source" src="<%= src %>"/>
  <div class="meta-data"> 
    <%= metadata %>
  </div>
</li>

Note that templates are not themselves views. Developers coming from a Struts Model 2 architecture may feel like a template is a view, but it isn’t. A view is an object that observes a model and keeps the visual representation up to date. A template might be a declarative way to specify part or even all of a view object so that it can be generated from the template specification.

It is also worth noting that in classical web development, navigating between independent views required the use of a page refresh. In single-page JavaScript applications, however, once data is fetched from a server via Ajax, it can simply be dynamically rendered in a new view within the same page without any such refresh being necessary. The role of navigation thus falls to a router, which assists in managing application state (e.g., allowing users to bookmark a particular view they have navigated to). As routers are however neither a part of MVC nor present in every MVC-like framework, I will not be going into them in greater detail in this section.

To summarize, views are a visual representation of our application data.

Controllers

Controllers are intermediaries between models and views, which are classically responsible for updating the model when the user manipulates the view.

In our photo gallery application, a controller would be responsible for handling changes the user made to the edit view for a particular photo, updating a specific photo model when a user has finished editing.

Remember that the controllers fulfill one role in MVC: the facilitation of the Strategy pattern for the view. In the Strategy pattern regard, the view delegates to the controller at the view’s discretion; that’s how the strategy pattern works. The view could delegate handling user events to the controller when the view sees fit. The view could delegate handling model change events to the controller if the view sees fit, but this is not the traditional role of the controller.

In terms of where most JavaScript MVC frameworks detract from what is conventionally considered “MVC” however, it is with controllers. The reasons for this vary, but in my honest opinion, it is that framework authors initially look at the server-side interpretation of MVC, realize that it doesn’t translate 1:1 on the client side, and reinterpret the C in MVC to mean something they feel makes more sense. The issue with this, however, is that it is subjective and increases the complexity in both understanding the classical MVC pattern and the role of controllers in modern frameworks.

As an example, let’s briefly review the architecture of the popular architectural framework Backbone.js. Backbone contains models and views (somewhat similar to what we reviewed earlier); however, it doesn’t actually have true controllers. Its views and routers act a little similar to a controller, but neither are actually controllers on their own.

In this respect, contrary to what might be mentioned in the official documentation or in blog posts, Backbone is neither a truly MVC/MVP nor MVVM framework. It’s in fact better to consider it a member of the MV* family that approaches architecture in its own way. There is of course nothing wrong with this, but it is important to distinguish between classical MVC and MV*, should we begin relying on advice from classical literature on the former to help with the latter.

Controllers in Another Library (Spine.js) Versus Backbone.js

Spine.js

We now know that controllers are traditionally responsible for updating the model when the user updates the view. It’s interesting to note that the most popular JavaScript MVC/MV* framework at the time of writing (Backbone) does not have its own explicit concept of controllers.

It can thus be useful for us to review the controller from another MVC framework to appreciate the difference in implementations and further demonstrate how nontraditionally frameworks approach the role of the controller. For this, let’s take a look at a sample controller from Spine.js.

In this example, we’re going to have a controller called PhotosController, which will be in charge of individual photos in the application. It will ensure that when the view updates (e.g., a user edited the photo metadata), the corresponding model does, too.

We won’t be delving heavily into Spine.js at all, but will just take a 10-foot view of what its controllers can do:

// Controllers in Spine are created by inheriting from Spine.Controller

var PhotosController = Spine.Controller.sub({  

  init: function () {
    this.item.bind( "update" , this.proxy( this.render ));
    this.item.bind( "destroy", this.proxy( this.remove ));
  },

  render: function () {
    // Handle templating
    this.replace( $( "#photoTemplate" ).tmpl( this.item ) );
    return this;
  },

  remove: function () {
    this.el.remove();
    this.release();
  }
});

In Spine, controllers are considered the glue for an application, adding and responding to DOM events, rendering templates, and ensuring that views and models are kept in sync (which makes sense in the context of what we know to be a controller).

What we’re doing in the above example is setting up listeners in the update and destroy events using render() and remove(). When a photo entry gets updated, we re-render the view to reflect the changes to the metadata. Similarly, if the photo gets deleted from the gallery, we remove it from the view. In the render() function, we’re using Underscore microtemplating (via _.template()) to render a JavaScript template with the ID #photoTemplate. This simply returns a compiled HTML string used to populate the contents of photoEl.

What this provides us with is a very lightweight, simple way to manage changes between the model and the view.

Backbone.js

Later in this section, we’re going to revisit the differences between Backbone and traditional MVC, but for now, let’s focus on controllers.

In Backbone, one shares the responsibility of a controller with both Backbone.View and Backbone.Router. Some time ago, Backbone did come with its own Backbone.Controller, but as the naming for this component didn’t make sense for the context in which it was being used, it was later renamed to Router.

Routers handle a little more of the controller responsibility, as it’s possible to bind the events there for models and have our view respond to DOM events and rendering. As Tim Branyen (another Bocoup-based Backbone contributor) has also previously pointed out, it’s possible to get away with not needing Backbone.Router at all for this, so a way to think about it using the Router paradigm is probably:

var PhotoRouter = Backbone.Router.extend({
  routes: { "photos/:id": "route" },

  route: function( id ) {
    var item = photoCollection.get( id );
    var view = new PhotoView( { model: item } );

    $('.content').html( view.render().el );
  }
});

To summarize, the takeaway from this section is that controllers manage the logic and coordination between models and views in an application.

The best content for your career. Discover unlimited learning on demand for around $1/day.