Chapter 12. Making Sure There Is Only One with the Singleton

Pity the poor Singleton pattern. Even coders who do not know very much about patterns know about the singleton. Mainly they know one thing: Singletons are Bad, with a capital "B". And yet we cannot seem to live without the things. Singletons are everywhere. In the Java world, singletons show up in some of the most widely used software around—you will find them in tomcat, in ant, and in JDOM. On the Ruby side, we can find singletons lurking in Webrick, in rake, and even in Rails, just to name a few.

What is it about the Singleton pattern that makes it so indispensable and yet so widely detested? In the pages that follow we will look at why you might need a singleton, how you would go about building singletons and singleton-like things in Ruby, why singletons cause trouble, and what you can do to ease some of this pain.

One Object, Global Access

The motivation behind the Singleton pattern is very simple: There are some things that are unique. Programs frequently have a single configuration file. It is not unusual for a program to let you know how it is doing via a single log file. GUI applications frequently have a one main window, and they typically take input from exactly one keyboard. Many applications need to talk to exactly one database. If you only ever have one instance of a class and a lot of code that needs access to that instance, it seems silly to pass the object from one method to another. In this kind of situation, the GoF suggest that you build a singleton—a class that can have only one instance and that provides global access to that one instance.

There are a number of different ways that you can get some or all of the singleton behavior in Ruby, but we will start with the method that is closest to the one recommended by the GoF: Let the class of the singleton object manage the creation and access to its sole instance. To do so, we need to look first at class variables and class methods in Ruby.

Class Variables and Methods

So far, all of the code that we have written in this book has involved instance methods and variables—that is, code and data that are attached to individual instances of a class. Ruby, like most object-oriented languages, also supports class variables and methods, which are attached to a class.[1]

Class Variables

As we have seen, a class variable is a variable that is attached to a class[2] instead of to an instance of the class. Creating a class variable is very straightforward: You simply add another at sign (@) to the variable name. Here, for example, is a class that counts the number of times the increment method is called in two different variables—once in an instance variable and once in a class variable:


   class ClassVariableTester
     @@class_count = 0
  
     def initialize
       @instance_count = 0
     end
  
     def increment
       @@class_count = @@class_count + 1
       @instance_count = @instance_count + 1
     end
  
     def to_s
       "class_count: #{@@class_count} instance_count: #{@instance_count}"
     end
   end


Now let’s create an instance named ClassVariableTester and call its increment method a couple of times:


   c1 = ClassVariableTester.new
   c1.increment
   c1.increment
   puts("c1: #{c1}")


Not surprisingly, both counts end up being 2:

   c1: class_count: 2 instance_count: 2

Things get more interesting when you create a second instance of the class:


   c2 = ClassVariableTester.new
   puts("c2: #{c2}")


This produces

   c2: class_count: 2 instance_count: 0

What is happening here is that the instance counter was reset to zero for the second ClassVariableTester instance, whereas the class counter, which is shared by both instances, keeps right on counting.

Class Methods

Creating class-level methods in Ruby is a bit more challenging, but only a bit. We cannot just open a class and define a method:


   class SomeClass
     def a_method
       puts('hello from a method')
     end
   end


As we have already seen, if we do that we end up with an instance method:


   SomeClass.a_method
   instance.rb:11: undefined method 'a_method' for SomeClass:Class


The secret to creating a class method is knowing that when you are inside a class definition—but outside a method definition—the self variable is the class you are defining. You do not have to take my word for it, however. Suppose you run this class definition:


   class SomeClass
     puts("Inside a class def, self is #{self}")
   end


You will see the following output:

   Inside a class def, self is SomeClass

With this useful bit of information in hand, we can define a method on the class:


   class SomeClass
     def self.class_level_method
       puts('hello from the class method')
     end
   end


We can now call class_level_method exactly as its name suggests, at the class level:

   SomeClass.class_level_method

If you do not like the self.method_name syntax, Ruby offers another option. You can just define the class method by calling out the name explicitly:


   class SomeClass
     def SomeClass.class_level_method
       puts('hello from the class method')
     end
   end


Ruby programmers seem evenly split on this syntactical conundrum: Some like self, and some like the explicit class name. Personally, I like the self format, because you have less to change if you rename the class or transplant code to another class.

A First Try at a Ruby Singleton

Now that we know how to create class variables and methods, we have all the tools we need to create a singleton. Let’s start with an ordinary, non-singleton class (a multiton?) and transform it into a singleton. Perhaps you have a logging class, a little facility for keeping track of the comings and goings of your program. Your ordinary, non-singleton version of the logging class might look something like this:


   class SimpleLogger
     attr_accessor :level
  
     ERROR = 1
     WARNING = 2
     INFO = 3
  
     def initialize
       @log = File.open("log.txt", "w")
       @level = WARNING
     end
  
     def error(msg)
       @log.puts(msg)
       @log.flush
     end
  
     def warning(msg)
       @log.puts(msg) if @level >= WARNING
       @log.flush
     end
  
     def info(msg)
       @log.puts(msg) if @level >= INFO
       @log.flush
     end
   end


You might use this version of the logger by creating a new one and passing it around:


   logger = SimpleLogger.new
   logger.level = SimpleLogger::INFO
  
   logger.info('Doing the first thing')
   # Do the first thing...
   logger.info('Now doing the second thing')
   # Do the second thing...


Managing the Single Instance

The whole point of the Singleton pattern is to avoid passing an object like the logger all over the place. Instead, you want to make the SimpleLogger class responsible for managing its single instance. So how would you turn SimpleLogger into a singleton?

First, you add a class variable to hold the one and only instance of your class. You will also need a class method to return the singleton instance.


   class SimpleLogger
  
     # Lots of code deleted...
  
     @@instance = SimpleLogger.new
  
     def self.instance
       return @@instance
     end
   end


We can now call the instance method of the SimpleLogger class any number of times and always get back the same logger object:


   logger1 = SimpleLogger.instance # Returns the logger
   logger2 = SimpleLogger.instance # Returns exactly the same logger


More practically, we can get at the singleton logger from anywhere in our code and use it to write out messages:


   SimpleLogger.instance.info('Computer wins chess game.')
   SimpleLogger.instance.warning('AE-35 hardware failure predicted.')
   SimpleLogger.instance.error(
       'HAL-9000 malfunction, take emergency action!')


Making Sure There Is Only One

Our singleton is now sort of functional, but it is not really complete. Remember, one requirement of the singleton is to ensure that the one and only singleton is the sole instance of the singleton class. So far we have ignored this requirement. As things stand right now, any program can call SimpleLogger.new to make a second instance of our allegedly singleton class. So how do we go about securing SimpleLogger against promiscuous instantiation?

We do so by making the new method on SimpleLogger private:


   class SimpleLogger
  
     # Lots of code deleted...
  
     @@instance = SimpleLogger.new
  
     def self.instance
       return @@instance
     end
  
     private_class_method :new
   end


There are two ideas to take away from this code fragment, one a detail and the other a bit more profound. The detail is that by adding the private_class_method call, we have done exactly what the name suggests: We made the new class method private, preventing any other class from creating new instances of our logger. The broader issue is that new is just another class-level method. Yes, the new method does perform some special behind-the-scenes magic in allocating a new object, but in the end it is just another class-level method.

The Singleton Module

Our singleton implementation is now complete, in that we have all of the ingredients required of a full-fledged GoF singleton. Our class creates exactly one instance of itself, any code that is interested can access the single instance, and no one can ever create a second instance.

Our singleton implementation does appear to have one problem, however. What if we want to build a second singleton class, perhaps for our configuration data? It seems that we will need to go through the whole exercise again: create a class variable for the singleton instance along with a class method to access it. Oh, and don’t forget to make the new method private. If we need a third instance, we need to do it all again a third time. This seems like a lot of duplicated effort.

Fortunately, we can avoid working so hard. Instead of going through all of the pain of turning our classes into singletons by hand, we can just include the Singleton module:


   require 'singleton'
  
   class SimpleLogger
     include Singleton
  
     # Lots of code deleted...

   end


The Singleton module does all of the heavy lifting of creating the class variable and initializing it with the singleton instance, creating the class-level instance method, and making new private. All we need to do is include the module. From the outside, this new Singleton module-based logger looks exactly like our previous hand-built implementations: Just call SimpleLogger.instance to retrieve the instance and off you go.

Lazy and Eager Singletons

There is one significant difference between the singleton implementation that we constructed and the one provided by the Singleton module. Recall that our implementation created the singleton instance as the class was being defined:


   class SimpleLogger
  
     # Lots of code deleted...
  
     @@instance = SimpleLogger.new
  
     # Lots of code deleted...
  
   end


As a consequence, our singleton instance is created before any client code ever gets a chance to call SimpleLogger.instance. Creating the singleton instance before you actually need it is called eager instantiation—we just can’t wait to make the thing. The Singleton module, by contrast, waits until someone calls instance before it actually creates its singleton. This technique is known as lazy instantiation.

Alternatives to the Classic Singleton

While the class-managed technique for building singletons that we have introduced here closely follows the implementation recommended in Design Patterns, it by no means exhausts the possibilities for realizing some or all of the singleton behavior. There are a number of other alternatives that we might use to achieve the same effect.

Global Variables as Singletons

We might, for example, use a global variable as a singleton. I will pause here while the screams of horror die down. In Ruby, any variable whose name begins with a dollar sign—$logger, for example—is global. Global variables certainly have the global access part of the singleton routine down pat: You can access $logger in any context, in any class, module, or method, and it will always be the same $logger. Because there is only one instance of any given global variable and because that variable is available everywhere (it being global and all), global variables seem like they might be a good platform for implementing singletons.

Sadly, no. Global variables lack some of the fundamental moving parts of a singleton. While $logger always refers to exactly one object at any given time, there is no way to control the value of a global variable. While we might start off with our global pseudo-singleton carefully set to the right thing:

   $logger = SimpleLogger.new

But there is absolutely nothing to prevent some misguided code from changing it:

   $logger = LoggerThatDoesSomethingBad.new

If change is the problem, then maybe we should turn to a flavor of Ruby variable that not only has global scope but also resists change: the constant. Recall that a Ruby constant is a variable whose name starts with an uppercase letter and has the nice property that, once set, its value is not supposed to change:

   Logger = SimpleLogger.new

Recall from Chapter 2 that Ruby will complain if we change the value of a constant, which is at least an improvement in attitude over the "anything goes" philosophy of global variables. So is this the simple solution to the singleton?

Not really. Both global variables and constants share a number of deficiencies as singletons. First, if you use a global variable or a constant for this purpose, there is no way to delay the creation of the singleton object until you need it. The global variable or constant is there from the moment we first set it. Second, neither of these techniques does anything to prevent someone from creating a second or third instance of your supposedly singleton class. You could, of course, deal with that issue separately. For example, you might create the singleton instance and then change the class so that it will refuse to create any more instances—but all of this is beginning to feel rather ad hoc and messy.

Given that global variables and constants seem to fall short, are there any other ways to do the singleton thing?

Classes as Singletons

As we have seen, we can define methods and variables directly on a class object. In fact, our original singleton implementation used class methods and variables to manage the singleton instance. But given that we can have methods and variables on a class, why not just use the class itself as a container for the singleton functionality? Each class is unique—there can be only one SimpleLogger class loaded at any one time—so we might just define our singleton functionality as class methods and variables on a class object:


   class ClassBasedLogger
     ERROR = 1
     WARNING = 2
     INFO = 3
     @@log = File.open('log.txt', 'w')
     @@level = WARNING
  
     def self.error(msg)
       @@log.puts(msg)
       @@log.flush
     end
  
     def self.warning(msg)
       @@log.puts(msg) if @@level >= WARNING
       @@log.flush
     end
  
     def self.info(msg)
       @@log.puts(msg) if @@level >= INFO
       @@log.flush
     end
  
     def self.level=(new_level)
       @@level = new_level
     end
  
     def self.level
       @@level
     end
   end


Using the class-based singleton is not hard:


   ClassBasedLogger.level = ClassBasedLogger::INFO
  
   ClassBasedLogger.info('Computer wins chess game.')
   ClassBasedLogger.warning('AE-35 hardware failure predicted.')
   ClassBasedLogger.error('HAL-9000 malfunction, take emergency action!')


The "class as singleton" technique has a key advantage over the global variable and constant methods: You are sure that no one will create a second instance of your singleton. Lazy initialization remains a problem with this technique, however. Specifically, your class is initialized when it gets loaded (typically when someone requires the file that the class lives in), and you do not have a lot of control over the timing of this initialization. Another disadvantage of using a class as a singleton is that programming class methods and variables is just not as easy as coding garden-variety instance methods and variables; all of those self.methods and @@variables have a strange feel to them.

Modules as Singletons

Another possibility is to use a module as the container for your singleton behavior. As noted earlier in this chapter, modules have a lot in common with classes. In fact, modules are so much like classes that you can define module-level methods and variables in exactly the same way that you define class methods and variables. Except for changing class to module, the module-based implementation is exactly the same as the class-based one:


   module ModuleBasedLogger
     ERROR = 1
     WARNING = 2
     INFO = 3
  
     @@log = File.open("log.txt", "w")
     @@level = WARNING
  
     def self.error(msg)
       @@log.puts(msg)
       @@log.flush
     end
  
     # Lots of code, exactly like the
     # ClassBasedSingleton deleted...
  
   end


You can use module methods from just about anywhere, just like class methods:

   ModuleBasedLogger.info('Computer wins chess game.')

The "module as singleton" technique does have one notable advantage over the "class as singleton" technique. Because you cannot instantiate a module (that is the key difference between a module and a class), the intent of a module-based singleton is probably a bit clearer from the code: Here is a bucket of methods meant to be called and not something you can instantiate.

A Safety Harness or a Straitjacket?

The discussion of the alternative ways of implementing the Singleton pattern raises the question of language-based safety features, and what such features can mean in a language as flexible as Ruby. To reach for a handy example, we have seen that one of the effects of including the Singleton module is to make the new method private. This, of course, prevents anyone from making a second or third instance of the singleton class. If our singleton class is defined as


   require 'singleton'
  
   class Manager
     include Singleton
  
     def manage_resources
       puts("I am managing my resources")
     end
   end


I cannot make another instance of Manager. For example, if I try

   m = Manager.new

I will get

   private method 'new' called for Manager:Class

Actually, the Singleton module cannot really prevent anything. All I need is a little insight into how Singleton works and a bit of knowledge about public_class_method (the evil twin of private_class_method), and it becomes very easy to circumvent all of that prevention:


   class Manager
     public_class_method :new
   end
  
   m = Manager.new


In the same spirit, we noted earlier that one advantage of the class- or module-based singleton is that no one can make a second instance of your singleton. Well, not by accident, they can’t. But no matter whether you are using ClassBasedLogger or its cousin ModuleBasedLogger, your logger is in the end an object, and all objects in Ruby inherit the clone method. The clone method is wonderful utility for short-circuiting that singleton-ness that we have been working so hard to establish:


   a_second_logger = ClassBasedLogger.clone
   a_second_logger.error('using a second logger')


We might, of course, override the clone method in ClassBasedLogger to prevent unauthorized cloning. Of course, the determined cloner could just reopen your class to un-override the method . . .

The point is not that this kind of thing is a good idea, but rather that in a language where virtually everything done at runtime can be undone a little later in runtime, very few decisions are irreversible. The Ruby philosophy is that if you decide to circumvent the very clear intent of the author of the ClassBasedLogger class by cloning it, the language is there to help you out. You are in the driver's seat, not the language. By keeping almost everything open to modification, Ruby allows you to do the things that you say you want to do—but it is up to you to say the right things.

Using and Abusing the Singleton Pattern

Now that we know how to build a singleton, let’s figure out why this is perhaps the most hated of all patterns.

They Are Really Just Global Variables, Right?

Let’s start with the most obvious problem first: A singleton bears a very strong family resemblance to its outlaw cousin, the global variable. No matter whether you implement your singleton with the GoF class-managed technique or as a bunch of class- or module-level methods and variables, you are creating a single object with global scope. Create a singleton, and you have just made it possible for widely separated bits of your program to use that singleton as a secret channel to communicate with each other and, in the process, tightly couple themselves to each other. The horrible consequences of this coupling are why software engineering got out of the global variable business in the first place.

There is only one solution to this problem: Don’t do that. Properly applied, singletons are not global variables. Rather, they are meant to model things that occur exactly once. Yes, because it occurs only once, you can use a singleton as a unique communications conduit between bits of your program. But don’t do that. Singletons are like every other pattern and programming technique—which means you can really screw things up if you abuse them. I can only repeat: Don’t do that.

Just How Many of These Singletons Do You Have?

Which brings us to another obvious-sounding, but all-too-common way to come to grief with the Singleton pattern: to lose count. As you are considering applying the Singleton pattern, ask yourself this question: Am I sure that there is only one of these things? The Singleton pattern gives us a way to model a single instance of something, but this modeling also just happens to come with a nice coding feature that makes that single instance very easily accessible—just call SimpleLogger.instance. That easy access can have a hypnotic allure: "My code will be so much simpler if this thing is a singleton." Don’t listen to the siren song of that easy access. Instead, focus on the question of how many of these things exist and treat the easy access as a bonus.

Singletons on a Need-to-Know Basis

Another mistake that many people make is to spread the knowledge of a class's singleton-ness far and wide. You can look at the fact that a class is a singleton as something of an implementation detail: Once you get hold of the configuration file, exactly how you got hold of it is not really important. Remember that you can always grab the singleton object in one or a few places and then pass it around from there.

This technique comes in handy when your application needs to use the singleton in a few widely scattered clusters of code. For example, you might have an application structured like the one shown in Figure 12-1.

Figure 12-1. An application with widely scattered uses of a singleton

image

Imagine that the PreferenceManager class and the classes that it uses need access to a database connection, as does the DataPersistence class and its friends. Further imagine that the entire application uses a single instance of the class DatabaseConnectionManager for all of its connection management needs. Recognizing this, you make DatabaseConnectionManager a singleton:


   require 'singleton'
  
   class DatabaseConnectionManager
     include Singleton
  
     def get_connection
       # Return the database connection...
     end
   end


Now here's the question: Which classes are actually aware that DatabaseConnectionManager is a singleton? We could spread this information far and wide, perhaps among the preference readers and writers:


   class PreferenceManager
     def initialize
       @reader = PrefReader.new
       @writer = PrefWriter.new
       @preferences = { :display_splash=>false, :background_color=>:blue }
     end
  
     def save_preferences
       preferences = {}
       # Preference are in
       @writer.write(@preferences)
     end
  
     def get_preferences
       @preferences = @reader.read
     end
   end
  
   class PrefWriter
     def write(preferences)
       connection = DatabaseConnectionManager.instance.get_connection
       # Write the preferences out
     end
   end
  
   class PrefReader
     def read
       connection = DatabaseConnectionManager.instance.get_connection
       # Read the preferences and return them...
     end
   end


A better approach might be to concentrate the knowledge that DatabaseConnectionManager is a singleton in the PreferenceManager class and simply pass it into the preference reader and writer:


   class PreferenceManager
     def initialize
       @reader = PrefReader.new
       @writer = PrefWriter.new
       @preferences = { :display_splash=>false, :background_color=>:blue }
     end
  
     def save_preferences
       preferences = {}
       # Preference are in
       @writer.write(DatabaseConnectionManager.instance, @preferences)
     end
  
     def get_preferences
       @preferences = @reader.read(DatabaseConnectionManager.instance)
     end
   end


This little refactoring decreases the amount of code that needs to know that DatabaseConnectionManager is a singleton. There are two advantages to doing this. First, there is less code to fix if it turns out that your singleton is not, in fact, quite so alone. Second, by excising the singleton from the PrefReader and PrefWriter classes, you have made those classes much more easily testable.

Curing the Testing Blues

This last point brings us to testing. One exceedingly nasty thing about the Singleton pattern is the way that it interferes with unit testing. A good unit test needs to start with a known state. After all, your test results are unlikely to be worth much if you aren’t sure how things were set up when you started the test. A good unit test also needs to be independent of any other test, so that test 3 should give you exactly the same results no matter whether you run it between tests 2 and 4, after test 20, or all by itself. The problem, of course, is that if tests 1 through 20 are testing a singleton, each test is liable to modify the one and only singleton instance in some unpredictable way. So much for test independence.

One way to deal with this problem is to create two classes: an ordinary (i.e., non-singleton) class that contains all of the code, and a subclass of the first class that is a singleton. Something like this:


   require 'singleton'
  
   class SimpleLogger
     # All of the logging functionality in this class...
   end
  
   class SingletonLogger < SimpleLogger
     include Singleton
   end


The actual application code uses the SingletonLogger, while the tests can use the plain old, non-singleton Logger class.

Singletons in the Wild

You can find a good example of the use of the Singleton pattern in real life in ActiveSupport, which is a library of utility classes used by Rails. Rails relies heavily on the use of conventions, and many of the Rails conventions involve working out the plurals of singular words and the singulars of plural words. To do so, ActiveSupport maintains a list of rules, which encapsulate facts like "The plural of employee is employees, but the plural of criterion is criteria." But since the rules are, well, the rules, you really need to keep only one copy of them around. So the Inflections class is a singleton, which saves space and ensures that the same inflection rules are available everywhere.

Ruby’s build utility, rake, also uses a singleton. As it runs, rake—like most build tools—reads in information about what it needs to do: which directories to create, which files to copy, and so on.[3] All of this information needs to be available to all of the moving parts of rake, so rake stores it all in a single object (the Rake::Application object, to be precise) that is available as a singleton to the entire rake program.

Wrapping Up

In this chapter, we looked at the somewhat checkered career of the Singleton pattern. The Singleton pattern can help us deal with the cases where there is only one of something. There are two characteristics that make a singleton a singleton: A singleton class has exactly one instance, and access to that one instance is available globally. Using class methods and variables, we can easily build the "classic" implementation of the singleton, the one recommended by the GoF.

We can also build singletons (or at least near-singletons) using a variety of other methods. For example, we could get some of the singleton behavior from global variables or constants, although these elements lack the uniqueness characteristic that makes a real singleton a singleton. In addition, we can build singletons from class- or module-level methods and variables.

We spent a fair bit of time in this chapter looking at the landmines scattered around Singleton-land. We saw that the singleton presents rich opportunities for coupling your code to itself in very unfortunate ways. We also saw that you might want to limit the amount of code that is aware of an object's singleton-ness, and we looked at one way to ease the burden that singletons place on testing.

The Singleton pattern is a bit like that ancient table saw that my dad used to have. That saw was incredibly effective at cutting lumber, but, since it had very few safety features, it was equally adept at slicing an unwary hand in two.

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

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