CHAPTER 5

image

Design Patterns: Creational

In this and the following three chapters, I’m going to explain the priniples of design patterns and code architecture pattern for large-scale JavaScript applications that will keep your code sensibly organized and easy to understand, making the job of maintaining and adding to your code a lot simpler. By implementing one or more of the patterns and techniques found in these chapters, you’ll find much of your code files look, at a glance, very similar to each other, which breeds instant familiarity for a number of developers working together on the same project. In fact, if you choose to adopt any of these techniques across multiple projects, you’ll likely find the same patterns and architecture idioms work across them all, making it easier to get new developers up to speed on different projects and freeing up those developers to concentrate on writing excellent code.

The secret to using design patterns is to consider them like tools in your programming toolbox where each has a specific use. First familiarize yourself with the patterns available and when to use each, and these chapters will help do just that, before you attempt to apply them to your code—applying the wrong tool will cause undue headaches and wasted time. Unless you are a particularly seasoned JavaScript developer, you will start writing the code for your application without a specific design pattern in mind to begin with and, as your code grows, you will find you need to make changes to it in order to make further development more manageable and to give some structure and familiarity to the files in your code base. This process is typically known as refactoring and it is often at this stage in development that you will consider applying a particular design or architectural pattern to your code to simplify development going forward. Be wary of anyone who insists starting a new project with a specific pattern in mind or who insists on using a particular prebuilt JavaScript framework at the outset, as, unless they are significantly experienced professionals, this is the equivalent of selecting a new, shiny tool before identifying the problem the tool is needed for.

Each chapter covers design patterns and architectural patterns with which you should familiarize yourself. Study each pattern and understand how it is used, then over time you will start identifying specific patterns in your code that need to be applied to improve the maintainability, and in some cases efficiency, of your code.

What Are Design Patterns?

Design patterns are tried and tested, proven ways of programming and structuring code so that it is easy to understand, easy to maintain, and easy to scale by favoring clarity, removing unnecessary complication for the developer, and by decoupling connections between different parts of a large code base. They are the tools in your programming toolbox.

Design patterns were first introduced in a book titled Design Patterns: Elements of Reusable Object-Oriented Software, written by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, collectively known as the Gang Of Four, and published in 1994. Their original examples were written in C++ and Smalltalk programming languages, but the principles of most patterns they describe apply to any language, including JavaScript. The authors describe twenty-three different design patterns, split into three distinct categories: Creational, Structural, and Behavioral. In this and the following chapters, we’ll cover those that best apply to JavaScript programming, skipping those that aren’t relevant, and including some modern patterns missing from the original book that apply equally well to JavaScript. Although many tutorials on design patterns reuse a lot of the same examples over and over again, I have created original examples for each of the patterns featured here that often correlate better to solving real-world coding issues than others that you might find.

Creational Design Patterns

A creational design pattern describes a “class” or method that creates objects for you rather than you creating them yourself directly. This layer of abstraction gives you and your code more flexibility in deciding which object or type of object would be most relevant for the specific situation and need you have. Here I will introduce you to five creational patterns that you may find useful in your code, with examples for each.

The Factory Pattern

The factory design pattern allows you to create an object without specifying the “class” to use to create it with. When we’ve covered “classes” in previous chapters, we’ve used the new JavaScript keyword directly to create an instance of a specific “class” or subclass; with the factory pattern, the object creation process is abstracted, allowing for relatively complex object creation procedures to be masked behind a simple interface that does not require the new keyword. This abstraction means that the underlying “class” types and methods to create them can be completely replaced at any time without changing the interface for “class” creation for other developers—ideal if you know lots of changes may need to be made in future but you don’t want to have to rewrite your “class” instantiaton code in a large number of code files.

Listing 5-1 shows an example of the factory pattern for instantiating objects based on a number of different “classes” dependent on the factory method’s input parameters.

Listing 5-1. The factory design pattern

// Define the factory that will make form field objects for us using the most appropriate
// "class" depending on the inputs
var FormFieldFactory = {

        // The makeField method takes two options:
        // - type, which defines the type of form field object to create, e.g. text, email,
        //    or button
        // - displayText, which defines either the placeholder text for the form field, or the
        //    text to display on the button, depending on the type
        makeField: function(options) {
            var options = options || {},
                type = options.type || "text",
                displayText = options.displayText || "",
                field;

            // Create an object instance using the most appropriate "class" based on the
            // supplied input type
            switch (type) {
            case "text":
                field = new TextField(displayText);
                break;
            case "email":
                field = new EmailField(displayText);
                break;
            case "button":
                field = new ButtonField(displayText);
                break;

            // If in doubt, use the TextField "class"
            default:
                field = new TextField(displayText);
                break;
            }

            return field;
        }
    };

// Define the TextField "class" to be used for creating <input type="text"> form elements
function TextField(displayText) {
    this.displayText = displayText;
}

// The getElement method will create a DOM element using the supplied placeholder text value
TextField.prototype.getElement = function() {
    var textField = document.createElement("input");
    textField.setAttribute("type", "text");
    textField.setAttribute("placeholder", this.displayText);

    return textField;
};

// Define the EmailField "class" to be used for creating <input type="email"> form elements
function EmailField(displayText) {
    this.displayText = displayText;
}

// The getElement method will create a DOM element using the supplied placeholder text value
EmailField.prototype.getElement = function() {
    var emailField = document.createElement("input");
    emailField.setAttribute("type", "email");
    emailField.setAttribute("placeholder", this.displayText);

    return emailField;
};

// Define the ButtonField "class" to be used for creating <button> form elements
function ButtonField(displayText) {
    this.displayText = displayText;
}

// The getElement method will create a DOM element using the supplied button text value
ButtonField.prototype.getElement = function() {
    var button = document.createElement("button");
    button.setAttribute("type", "submit");
    button.innerHTML = this.displayText;

    return button;
};

Listing 5-2 demonstrates how the factory created in Listing 5-1 could then be used within an application.

Listing 5-2. The factory design pattern in use

// Use the factory to create a text input form field, an email form field, and a submit button.
// Note how we do not need to know about the underlying "classes" or their specific inputs to
// create the form fields - the FormFieldFactory abstracts this away
var textField = FormFieldFactory.makeField({
        type: "text",
        displayText: "Enter the first line of your address"
    }),
    emailField = FormFieldFactory.makeField({
        type: "email",
        displayText: "Enter your email address"
    }),
    buttonField = FormFieldFactory.makeField({
        type: "button",
        displayText: "Submit"
    });

// Wait for the browser's "load" event to fire, then add the DOM elements represented by the
// three newly created objects to the current page
window.addEventListener("load", function() {
    var bodyElement = document.body;

    // Use the getElement() method of each object to get a reference to its DOM element for
    // adding to the page
    bodyElement.appendChild(textField.getElement());
    bodyElement.appendChild(emailField.getElement());
    bodyElement.appendChild(buttonField.getElement());
}, false);

The factory pattern is best used when you want to simplify the creation of specific objects throughout the rest of your code by masking a more complex operation to create those objects. To read about the factory pattern in more detail online, take a look at the following resources:

The Abstract Factory Pattern

The abstract factory pattern takes the factory pattern we just looked at one step further, allowing multiple factories to be created together according to a common use or theme, creating an extra layer of abstraction, if needed by your application. The code in Listing 5-3 demonstrates this pattern, extending the example from Listing 5-1 by treating two factories as instances of a new factory type from which they shame similar behaviors.

Listing 5-3. The abstract factory design pattern

// Define a base factory "class" for creating form fields, from which other, more specialised
// form field creation factory "classes" will be inherited.
function FormFieldFactory() {

    // Define a list of supported field types to be applied to all inherited form field
    // factory classes
    this.availableTypes = {
        TEXT: "text",
        EMAIL: "email",
        BUTTON: "button"
    };
}
FormFieldFactory.prototype = {

    // Define a makeField() method which will be overwritten by sub classes using polymorphism.
    // This method should therefore not be called directly from within this parent "class" so
    // we'll throw an error if it is
    makeField: function() {
        throw new Error("This method should not be called directly.");
    }
};

// Define a factory "class", inherited from the base factory, for creating HTML5 form fields.
// Read more about the differences in these form fields from HTML4 at
// http://bit.ly/html5_webforms
function Html5FormFieldFactory() {}
Html5FormFieldFactory.prototype = new FormFieldFactory();

// Override the makeField() method with code specific for this factory
Html5FormFieldFactory.prototype.makeField = function(options) {
    var options = options || {},
        type = options.type || this.availableTypes.TEXT,
        displayText = options.displayText || "",
        field;

    // Select the most appropriate field type based on the provided options
    switch (type) {
    case this.availableTypes.TEXT:
        field = new Html5TextField(displayText);
        break;
    case this.availableTypes.EMAIL:
        field = new Html5EmailField(displayText);
        break;
    case this.availableTypes.BUTTON:
        field = new ButtonField(displayText);
        break;
    default:
        throw new Error("Invalid field type specified: " + type);
    }

    return field;
};

// Define a factory "class", also inherited from the same base factory, for creating
// older-style HTML4 form fields
function Html4FormFieldFactory() {}
Html4FormFieldFactory.prototype = new FormFieldFactory();

// Override the makeField() method with code specific for this factory
Html4FormFieldFactory.prototype.makeField = function(options) {
    var options = options || {},
        type = options.type || this.availableTypes.TEXT,
        displayText = options.displayText || "",
        field;

    switch (type) {
    case this.availableTypes.TEXT:
    case this.availableTypes.EMAIL:
        field = new Html4TextField(displayText);
        break;
    case this.availableTypes.BUTTON:
        field = new ButtonField(displayText);
        break;
    default:
        throw new Error("Invalid field type specified: " + type);
    }

    return field;
};

// Define the form field "classes" to be used for creating HTML5 and HTML4 form elements
function Html5TextField(displayText) {
    this.displayText = displayText || "";
}
Html5TextField.prototype.getElement = function() {
    var textField = document.createElement("input");
    textField.setAttribute("type", "text");
    textField.setAttribute("placeholder", this.displayText);

    return textField;
};

// Since the placeholder attribute isn't supported in HTML4, we'll instead create and return a
// <div> element containing the text field and an associated <label> containing the
// placeholder text
function Html4TextField(displayText) {
    this.displayText = displayText || "";
}
Html4TextField.prototype.getElement = function() {
    var wrapper = document.createElement("div"),
        textField = document.createElement("input"),
        textFieldId = "text-field-" + Math.floor(Math.random() * 999),
        label = document.createElement("label"),
        labelText = document.createTextNode(this.displayText);

    textField.setAttribute("type", "text");
    textField.setAttribute("id", textFieldId);

    // Associate the <label> with the <input> using the label 'for' attribute and the input 'id'
    label.setAttribute("for", textFieldId);
    label.appendChild(labelText);

    wrapper.appendChild(textField);
    wrapper.appendChild(label);

    return wrapper;
};

function Html5EmailField(displayText) {
    this.displayText = displayText;
}
Html5EmailField.prototype.getElement = function() {
    var emailField = document.createElement("input");
    emailField.setAttribute("type", "email");
    emailField.setAttribute("placeholder", this.displayText);

    return emailField;
};

// We define the button form element to be identical for both HTML5 and HTML4 form field types,
// so no need for two separate "classes". If we ever needed to create a different HTML5 version
// in future, we'd only need to update the relevant factory "class" with the change, and the
// rest of the code in our full application will adapt accordingly
function ButtonField(displayText) {
    this.displayText = displayText;
}
ButtonField.prototype.getElement = function() {
    var button = document.createElement("button");
    button.setAttribute("type", "submit");
    button.innerHTML = this.displayText;

    return button;
};

We can use the abstract factory from Listing 5-3 as shown in Listing 5-4 to produce the correct type of form fields based on the support of the browser the code is being run within.

Listing 5-4. The abstract factory design pattern in use

// Establish if the browser supports HTML5, and select the appropriate form field factory
var supportsHtml5FormFields = (function() {

        // This self-executing function attempts to create a HTML5 form field type:
        // <input type="email">
        var field = document.createElement("input");
        field.setAttribute("type", "email");

        // If the new form field returns the corrent field type then it was created correctly
        // and is a browser that supports HTML5. If not, the browser is HTML4-only
        return field.type === "email";
    }()),

    // Use the value returned previously to select the appropriate field field creation factory
    // "class" and create an instance of it
    formFieldFactory = supportsHtml5FormFields ? new Html5FormFieldFactory() : new Html4FormFieldFactory(),

    // Use the factory to create a text input form field, an email form field, and a submit
    // button, which will now use the most appropriate field type and attributes for the
    // current browser
    textField = formFieldFactory.makeField({
        type: "text",
        displayText: "Enter the first line of your address"
    }),
    emailField = formFieldFactory.makeField({
        type: "email",
        displayText: "Enter your email address"
    }),

    // Notice how we can harness the availableTypes property containing the list of supported
    // field types from the factory "class" instead of using a hard-coded text string for the
    // form field type. This is preferred, just as variables are preferable over
    // hard-coded values.
    buttonField = formFieldFactory.makeField({
        type: formFieldFactory.availableTypes.BUTTON,
        displayText: "Submit"
    });

// Wait for the browser's "load" event to fire, then add the DOM elements represented by the
// three newly created objects to the current page
window.addEventListener("load", function() {
    var bodyElement = document.body;

    // Use the getElement() method of each object to get a reference to its DOM element for.
    // adding to the page
    bodyElement.appendChild(textField.getElement());
    bodyElement.appendChild(emailField.getElement());
    bodyElement.appendChild(buttonField.getElement());
}, false);

The abstract factory pattern is best used when you need to create an extra layer of abstraction away from more than one “class” in your existing code according to a shared purpose or common theme between them, so as to make development in the rest of your application less complex. To read about the abstract factory pattern in more detail online, take a look at the following resources:

The Builder Pattern

Like the factory and abstract factory patterns we’ve seen so far, the builder pattern abstracts the creation of objects. In this pattern, we only need supply the content and type of the object we wish to create, with the process of deciding which “class” to use to create it abstracted away by the builder. We effectively construct a complete object by splitting its creation down into a series of smaller steps, finally calling an operation that “builds” the resulting object, returning it to the calling code. A builder can potentially contain a fair amount of code, all with the express intention of making object creation as pain-free as possible for developers.

The builder pattern is demonstrated in Listing 5-5, which defines a builder for creating simple HTML forms containing any number and type of form fields in any order and added at any time; the <form> element is only created and returned at such point as it is needed, using the getForm() “build” method once all of the fields have been added.

Listing 5-5. The builder pattern

// Define a builder "class" for constructing simple forms which can be configured according to
// the end developer's needs. The end developer will instantiate the builder and add fields to
// the form as needed throughout the course of their application, finally calling a method to
// return a <form> element containing all the fields added
function FormBuilder() {}
FormBuilder.prototype = {

    // Define a property for storing fields created
    fields: [],

    // Define a method for adding fields to the form instance
    addField: function(type, displayText) {
        var field;

        // Use the supplied form field type and display text to instantiate the relevant form
        // field "class"
        switch (type) {
        case "text":
            field = new TextField(displayText);
            break;
        case "email":
            field = new EmailField(displayText);
            break;
        case "button":
            field = new ButtonField(displayText);
            break;
        default:
            throw new Error("Invalid field type specified: " + type);
        }

        // Add the created field object to the storage array
        this.fields.push(field);
    },

    // Define a method for returning the resulting <form> element, containing the fields added
    // using the addField method
    getForm: function() {

        // Create a new <form> element
        var form = document.createElement("form"),
            index = 0,
            numFields = this.fields.length,
            field;

        // Loop through each field in the fields property, getting the DOM element from each and
        // adding it to the <form> element
        for (; index < numFields; index++) {
            field = this.fields[index];
            form.appendChild(field.getElement());
        }

        // Return the populated <form> element
        return form;
    }
};

// Define the underlying form field "classes", as in Listing 5-1
function TextField(displayText) {
    this.displayText = displayText || "";
}
TextField.prototype.getElement = function() {
    var textField = document.createElement("input");
    textField.setAttribute("type", "text");
    textField.setAttribute("placeholder", this.displayText);

    return textField;
};

function EmailField(displayText) {
    this.displayText = displayText || "";
}
EmailField.prototype.getElement = function() {
    var emailField = document.createElement("input");
    emailField.setAttribute("type", "email");
    emailField.setAttribute("placeholder", this.displayText);

    return emailField;
};

function ButtonField(displayText) {
    this.displayText = displayText || "";
}
ButtonField.prototype.getElement = function() {
    var button = document.createElement("button");
    button.setAttribute("type", "submit");
    button.innerHTML = this.displayText;

    return button;
};

The form builder in Listing 5-5 can then be used in an application as shown in Listing 5-6, in which a number of fields are added to a form without having to directly instantiate any form or field “classes,” nor having to manually create any DOM elements. The final object is then “built” using the getForm() method, returning it for use in the calling code.

Listing 5-6. The builder pattern in use

// Instantiate the form builder
var formBuilder = new FormBuilder(),
    form;

// Add fields in any order and at any time required in the application - only the type and
// content is required, the actual object creation is abstracted away in the builder
formBuilder.addField("text", "Enter the first line of your address");
formBuilder.addField("email", "Enter your email address");
formBuilder.addField("button", "Submit");

// When the final form is required, call the builder's getForm method to return a <form> element
// containing all the fields
form = formBuilder.getForm();

 window.addEventListener("load", function() {
    document.body.appendChild(form);
}, false);

The builder pattern is best used when you need to create a large object in your code through a series of smaller steps, returning the created object at a specific point as required by your application. To read about the builder pattern in more detail online, check out the following resources:

The Prototype Pattern

The prototype pattern creates new objects by cloning existing objects using prototypal inheritance. This will be familiar to you after reading Chapter 1 as prototypal inheritance is the type of inheritance JavaScript was created around, and can be enforced using either the prototype property of an existing object, as we have seen when creating “classes” in JavaScript, or by using ECMAScript 5’s Object.create() method, which is the preferred method but still requires better web browser support to be used exclusively. Listing 5-7 shows the prototype pattern in action using the first of these techniques, whereas Listing 5-8 demonstrates the pattern using the latter.

Listing 5-7. The prototype pattern using the prototype keyword

var textField,
    emailField;

// Define a Field "class" to be used for creating <input> form elements
function Field(type, displayText) {
    this.type = type || "";
    this.displayText = displayText || "";
}

// Use the prototype property to adopt the Prototype pattern of defining methods that will be
// applied to any object instantiated from this "class"
Field.prototype = {
    getElement: function() {
        var field = document.createElement("input");
        field.setAttribute("type", this.type);
        field.setAttribute("placeholder", this.displayText);

        return field;
    }
};

// Create two object instances, both of which receive the getElement method from the prototype
textField = new Field("text", "Enter the first line of your address");
emailField = new Field("email", "Enter your email address");

// Add the elements stored in these objects to the current page once loaded
window.addEventListener("load", function() {
    var bodyElement = document.body;

    bodyElement.appendChild(textField.getElement());
    bodyElement.appendChild(emailField.getElement());
}, false);

Listing 5-8. The prototype pattern using ECMAScript 5

// Define a base object with two properties, type and displayText, and a getElement() method 
// which creates a HTML <input> element, configuring it using the values from the two properties
var field = {
        type: "",
        displayText: "",

        getElement: function() {
            var field = document.createElement("input");
            field.setAttribute("type", this.type);
            field.setAttribute("placeholder", this.displayText);

            return field;
        }
    },

    // Create a new object based upon the base object, using ECMAScript 5's Object.create()
    // method to clone the original object and apply values to the two properties type and
    // displayText, in order to create an object capable of creating a <input type="text">
    // element when the object's getElement() method is called
    textField = Object.create(field, {

        // The second parameter of Object.create() allows values from the first parameter to be
        // overwritten using the format described in Chapter 1
        'type': {
            value: "text",
            enumerable: true
        },
        'displayText':{
            value: 'Enter the first line of your address',
            enumerable: true
        }
    }),

    // Create another new object based upon the base object, using different property values in
    // order to allow the creation of a <input type="email"> element when the object's
    // getElement() method is called
    emailField = Object.create(field, {
        'type': {
            value: "email",
            enumerable: true
        },
        'displayText':{
            value: 'Enter your email address',
            enumerable: true
        }
    });

// Call the getElement() method of both objects, appending the created <input> DOM elements to
// the current page once loaded
window.addEventListener("load", function() {
    var bodyElement = document.body;

    bodyElement.appendChild(textField.getElement());
    bodyElement.appendChild(emailField.getElement());
}, false);

The prototype pattern is best used when you want to create new objects on the fly as clones of existing objects, alternatively to create objects based on “class” templates. To read about the prototype pattern in more detail online, take a look at the following resources:

The Singleton Pattern

The singleton pattern, when applied to JavaScript, defines the creation of an object that has only a single instance. In its simplest form, a singleton could therefore be a simple object literal encapsulating specific, related behaviors, as shown in Listing 5-9.

Listing 5-9. The singleton pattern

// Group related properties and methods together into a single object literal, which 
// we call a Singleton
var element = {

        // Create an array for storage of page element references
        allElements: [],

        // Get an element reference by its ID and store it
        get: function(id) {
            var elem = document.getElementById(id);
            this.allElements.push(elem);
            return elem;
        },

        // Create a new element of a given type, and store it
        create: function(type) {
            var elem = document.createElement(type);
            this.allElements.push(elem);
            return elem;
        },

        // Return all stored elements
        getAllElements: function() {
            return this.allElements;
        }
    },

    // Get and store a reference to a page element with ID of "header"
    header = element.get("header"),

    // Create a new <input> element
    input = element.create("input"),

    // Contains id="header", and new <input> elements
    allElements = element.getAllElements();

// Check to see how many elements are stored
alert(allElements.length); // 2

There are cases, however, in which you may wish to execute some initialization code as part of the creation of a singleton. For these, use a self-executing function, as shown in Listing 5-10, and use the return keyword to surface the object structure that you wish to make available to the rest of your code. I’ll look further at the use of self-executing functions in this way when I cover the module pattern in the next chapter.

Listing 5-10. The singleton pattern with a self-executing function

// Define a singleton containing cookie-related methods. Initialization code is achieved by
// using a self-executing function closure, which allows code to be executed at creation which
// is then unavailable publicly to the rest of the application
var cookie = (function() {

    // Cookies are stored in the document.cookie string, separated by semi-colons (;)
    var allCookies = document.cookie.split(";"),
        cookies = {},
        cookiesIndex = 0,
        cookiesLength = allCookies.length,
        cookie;

    // Loop through all cookies, adding them to the "cookies" object, using the cookie names
    // as the property names
    for (; cookiesIndex < cookiesLength; cookiesIndex++) {
        cookie = allCookies[cookiesIndex].split("=");

        cookies[unescape(cookie[0])] = unescape(cookie[1]);
    }

    // Returning methods here will make them available to the global "cookie" variable defined
    // at the top of this code listing
    return {

        // Create a function to get a cookie value by name
        get: function(name) {
            return cookies[name] || "";
        },

        // Create a function to add a new session cookie
        set: function(name, value) {

            // Add the new cookie to the "cookies" object as well as the document.cookie string
            cookies[name] = value;
            document.cookie = escape(name) + "=" + escape(value);
        }
    };
}());

// Set a cookie using the "set" method exposed through the "cookie" singleton
cookie.set("userID", "1234567890");

// Check that the cookie was set correctly
alert(cookie.get("userID")); // 1234567890

Many developers use the singleton patten like this to encapsulate and group together related code into a hierarchical structure, known as namespacing, which is popular in other programming languages such as Java. By keeping everything together within a single global variable like this, you reduce the risk of conflicting with any third-party code used within your application. Take a look at Listing 5-11, which shows a basic namespaced structure for keeping relating code together in named sections to reduce developer confusion and to simplify maintenance and development, making code easier to read and understand.

Listing 5-11. Namespacing using the singleton pattern

// Use an object literal to create a hierarchy of grouped properties and methods,
// known as a "namespace"
var myProject = {
    data: {

        // Each nested property represents a new, deeper level in the namespace hierarchy
        ajax: {

            // Create a method to send an Ajax GET request
            get: function(url, callback) {
                var xhr = new XMLHttpRequest(),
                    STATE_LOADED = 4,
                    STATUS_OK = 200;

                xhr.onreadystatechange = function() {
                    if (xhr.readyState !== STATE_LOADED) {
                        return;
                    }

                    if (xhr.status === STATUS_OK) {
                        callback(xhr.responseText);
                    }
                };

                xhr.open("GET", url);
                xhr.send();
            }
        }
    }
};

// Add to the namespace after creation using dot notation
myProject.data.cookies = {

    // Create a method for reading a cookie value by name
    get: function(name) {
        var output = "",
            escapedName = escape(name),
            start = document.cookie.indexOf(escapedName + "="),
            end = document.cookie.indexOf(";", start);

        end = end === -1 ? (document.cookie.length - 1) : end;

        if (start > = 0) {
            output = document.cookie.substring(start + escapedName.length + 1, end);
        }

        return unescape(output);
    },

    // Create a method for setting a cookie name/value pair
    set: function(name, value) {
        document.cookie = escape(name) + "=" + escape(value);
    }
};

// Execute methods directly through the "namespace" hierarchy using dot notation
myProject.data.ajax.get("/", function(response) {
    alert("Received the following response: " + response);
});

// Note how using the hierarchy adds clarity to the final method call
myProject.data.cookies.set("userID", "1234567890");
myProject.data.cookies.set("name", "Den Odell");

// Read back the cookie valus set previously
alert(myProject.data.cookies.get("userID")); // 1234567890
alert(myProject.data.cookies.get("name")); // Den Odell

The singleton pattern is best used when you need to create a single instance of an object for use throughout your code, or to namespace your code, dividing it into named sections with a hierarchy defined under a single global object. To read about the singleton pattern in more detail online, check out the following resources:

Summary

In this chapter, I have introduced the concept of design patterns and shown how to use creational design patterns to abstract away object creation within your own JavaScript applications. Design patterns are tools in your Swiss Army knife of JavaScript development but, like all tools, you need to know when and where to use them best. Familiarize yourself with the patterns in this chapter, and ensure that you don’t use a design pattern before you recognize the need for it in your code.

In the following chapter, I will continue to look at design patterns with a focus on structural design patterns that you can use in your JavaScript code to combine objects together into a larger, more structured form.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.17.40