CHAPTER 9
Ensuring Correct Implementation of the Singleton Pattern

WHAT’S IN THIS CHAPTER?            

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

The wrox.com code downloads for this chapter are found at www.wrox.com/go/reliablejavascript on the Download Code tab. They are in the Chapter 9 download and individually named according to the filenames noted throughout this chapter.

The entirety of Chapter 3 is dedicated to creating object instances, and a good portion of it describes patterns for creating multiple instances of similar objects. The chapter also covers techniques for sharing behavior between object instances, such as prototypal inheritance and functional inheritance.

There are times, however, when it is unnecessary or even undesirable to create multiple instances of an object. The Singleton Pattern may be employed in these cases where one, and only one, instance of an object should ever exist.

This chapter will illustrate how object literals may be considered singleton objects. It will also revisit how to create singleton objects using the immediate-execution modules, introduced in Chapter 3.

If you have experience in a language that supports multi-threading, such as C# or Java, you may notice that there’s a topic not covered in this chapter: thread safety. Because JavaScript is a single-threaded language, complications that arise when an object is accessed from multiple threads need not be considered when creating reliable JavaScript singletons.

UNDERSTANDING THE PATTERN THROUGH UNIT TESTS

In Chapter 8, the restaurantApi.getRestaurantsWithinRadius function was memoized in order to reduce the number of calls to the third-party API used by the JavaScript conference website. The memoization functionality was nicely encapsulated into an aspect that may be applied to any other function in the website.

What happens, however, if two instances of the restaurantApi are created and the getRestaurantsWithinRadius function of each object is called with the same arguments? Does the call to the second object use the results that were cached when the call to the first object’s function was made?

In this example, and many other cases when memoization has been applied to a function, the desired answer to the second question is: “Yes, it makes use of the results cached by the call to the first object’s function.” The next question, of course, is: “Do functions decorated with the returnValueCache behave that way?”

Unfortunately, the answer is no. Instances of returnValueCache each have their own internal cache object. This means that when an instance of the restaurantApi is created and has its getRestaurantsWithinRadius function decorated with the returnValueCache, that instance of restaurantApi has access only to the results of previous calls to its own getRestaurantsWithinRadius. It will not benefit from values cached within other instances of restaurantApi.

It would be nice if instances of returnValueCache could share a single cache rather than each having its own. If the returnValueCache had this capability, instances of restaurantApi could share the saved results of all getRestaurantsWithinRadius calls, regardless of the restaurantApi instance that held the function that was called.

Dependency injection, covered in Chapter 2, is a great mechanism to facilitate sharing of cache objects between returnValueCache instances. Modifying the returnValueCache module’s function to optionally accept a cache object instance will move the code in the direction desired.

Implementing a Singleton Shared Cache with an Object Literal

The object literal is the simplest implementation of the Singleton Pattern in JavaScript. Unlike the other object creation patterns, there’s no function to call to create another one, nor may the new keyword be used to create more.

The current implementation of the returnValueCache already uses an object literal as a cache, so injecting an optional object literal as the shared cache is the easiest way to add the desired functionality.

The unit tests shown in Listing 9-1 build upon those from Listing 8-3 in Chapter 8. By making the injected cache object optional, the original tests don’t require any modification (beyond updates to account for some test refactoring). By extension, any code that utilizes the returnValueCache in its current state—without being provided a shared cache—also doesn’t require any change.

As mentioned, a minor refactor of the unit tests was made to reduce duplication: The utility method createATestObject was added to encapsulate—you guessed it—the creation of test objects.

Figure 9.1 shows that all the pre-existing tests pass, but the new test exercising the ability to share a cache fails.

images

Figure 9.1

Listing 9-2 illustrates the modified returnValueCache accepting an object literal, and Figure 9.2 shows the tests all pass.

images

Figure 9.2

Now that the returnValueCache accepts a shared cache, all that’s left is to slightly modify the application of the aspect to restaurantApi.getRestaurantsWithinRadius, as Listing 9-3 (adapted from Listing 8-5) illustrates.

While the object literal is literally the simplest implementation of the Singleton Pattern, it lacks the ability to hide data and other desirable qualities that are provided by other object creation patterns, such as the Module Pattern.

Implementing a Singleton Shared Cache with a Module

In Listing 9-3, a shared cache was created within the Conference.caches namespace by declaring an object literal, restaurantsWithinRadiusCache. In many cases, an object literal—a key/value collection—is sufficient for use as a cache. There are cases, however, when a more capable cache is useful. It may be desirable to create a least recently used (LRU) cache that only stores a fixed number of values, replacing the oldest cached value with the newest. In other cases, it may be appropriate to only retain a value in the cache for a fixed period of time. Neither of these scenarios is easily implemented when an object literal is used as a cache.

Your ever-capable colleague, Charlotte, realized the usefulness of a more capable cache and provided Conference.simpleCache, a module that provides the functionality of the object literal-based cache, but via an API rather than direct property access. Charlotte’s Conference.simpleCache is shown in Listing 9-4.

Even though Conference.simpleCache does nothing more than provide an API for interacting with an object literal, Charlotte has created an interface that the returnValueCache may use to provide memoization functionality. In the future, new caching objects may be created that expose the same interface but exhibit different behavior, such as LRU or cache item timeout.

The returnValueCache does require a little bit of modification to use the simpleCache. Instead of creating an object literal if a shared cache is not provided, a new simpleCache will be created. Also, code in the advice will use the methods exposed by the simpleCache API rather than directly modifying properties of the cache object.

Only a small change is required in the unit tests from Listing 9-1 to account for the use of simpleCache. Listing 9-5 highlights the change.

The updated version of returnValueCache can be found in the code sample file Singleton \returnValueCache_02.js, and the passing test results are shown in Figure 9.3.

images

Figure 9.3

Now that simpleCache has been created and returnValueCache has been updated to use it, the next step is to create the singleton cache object for use with the restaurantApi .getRestaurantsWithinRadius function.

When the shared cache was an object literal, ensuring that only a single instance of the cache existed was a simple matter because object literals are singletons. The scenario is a bit different now that a module is being used as the cache: Each execution of the module function creates a new object instance.

To make sure that all instances of restaurantApi get the same instance of simpleCache, an implementation of the Singleton Pattern will be created in Listing 9-7 utilizing an immediate-execution module that was introduced in Chapter 3. The RestaurantsWithinRadiusCache module will expose a single function, getInstance, which will return the same instance of the simpleCache each time it is called.

The single test in Listing 9-6 verifies the behavior of getInstance.

And the implementation of RestaurantsWithinRadiusCache follows in Listing 9-7.

In Listing 9-7, RestaurantsWithinRadiusCache is assigned the value returned from the immediately executed function, establishing it as a singleton object exposing the getInstance function. The first time getInstance is invoked, the hidden instance variable is populated with a simpleCache. Each additional call to getInstance returns that same instance.

The passing unit test is shown in Figure 9.4.

images

Figure 9.4

Last, but certainly not least, application of the returnValueCache aspect to the restaurantApi.getRestaurantsWithinRadius function must be updated to use the new RestaurantsWithinRadiusCache singleton. This is shown in Listing 9-8.

SUMMARY

The Singleton Pattern is widely used in JavaScript. It’s useful for creating namespaces so that the global namespace isn’t polluted with your application’s functions and variables. Additionally, it’s fantastic for sharing data, like caches, between modules.

Implementations of the Singleton Pattern in JavaScript, such as object literals and immediate-execution modules, are easier to make reliable than in other languages because JavaScript is single-threaded. You need not worry about your singleton objects being accessed from multiple threads at the same time.

When implementing the Singleton Pattern, the primary concern that should be covered by unit tests is that only a single instance of the singleton object exists.

The next chapter covers the Factory Pattern, a technique for creating objects that provides additional abstraction and control over the object creational patterns that have already been discussed.