Implementing "the lovely" Singleton Pattern
Table of Contents
In every software, there are some things that have to be unique. For example, a Rails app has only one logger. Also, applications must have configurations, like environment, various API keys and etc. Take the configuration example - we need only one configuration for a runtime of an application. If all of the configuration data is stored into a class, then the whole app will need to use an object of that class. Hence, we use singletons - classes that can only have one object instantiated from them.
Basically, singleton classes prevent instantiation of more than one object of that class. Some people say that singletons are bad, some don’t. But I am pretty sure there are fine use-cases that we need to be aware of.
There are couple of ways to implement this pattern in Ruby:
Ruby Singleton Module #
Ruby’s Standard Library has a module that allows the creation of Singleton pattern. Using it is really easy.
require 'singleton'
class Configuration
include Singleton
attr_accessor :data
def initialize
@data = {}
end
def add key, value
@data[key] = value
end
def version
'0.0.1'
end
end
Here, we create a Configuration class - a class that has a hash called ‘data’
that will contain all the configration data. We added the add
method that
accepts a key and a value pair as arguments. This method will add the key-value
pair to the data hash.
>> c = Configuration.new
NoMethodError: private method `new' called for Configuration:Class
from (irb):27
from /Users/ie/.rbenv/versions/2.0.0-p576/bin/irb:12:in `<main>'
As you can see, instantiating a new object of this class is prohibited.
Accessing the singleton is done using the instance
class method.
>> Configuration.instance
=> #<Configuration:0x007fc2799e1568 @data={}>
>> Configuration.instance.data
=> {}
Also, using the add
method is done by calling it on the instance
:
>> Configuration.instance.add "environment", "development"
=> "development"
>> Configuration.instance.data
=> {"environment"=>"development"}
Instantiating a single object of a class #
So, how can we reproduce the same class as a Singleton without mixing in the Singleton module?
class Configuration
attr_accessor :data
def initialize
@data = {}
end
def self.instance
@@instance
end
def add key, value
@data[key] = value
end
@@instance = Configuration.new
private_class_method :new
end
So, where’s the magic? The initialize
and add
methods look pretty
usual. We create a class variable @@instance
that is an object of the
Configuration class. The key thing here is that we ‘protect’ the
Configuration.new
method by making it a private class method. Then,
Configuration.instance
method will just return the @@instance
. The
use case is very much the same.
>> Configuration.new
NoMethodError: private method `new' called for Configuration:Class
from (irb):20
from /Users/ie/.rbenv/versions/2.0.0-p576/bin/irb:12:in `<main>'
>> Configuration.instance
=> #<Configuration:0x007fd76419ebf8 @data={}>
>> Configuration.instance.add 'env', 'dev'
=> "dev"
>> Configuration.instance.data['env']
=> "dev"
>> Configuration.instance
=> #<Configuration:0x007fd76419ebf8 @data={"env"=>"dev"}>
Constants and global variables #
The key feature of a Singleton, beside the only-one-available-object is that the Singleton has to be globally acessible. That’s why constants and global variables can play well here.
CONFIGURATION = Configuration.new
CONFIGURATION.add 'environment', 'development'
$configuration = Configuration.new
$configuration.add 'environment', 'development'
One problem that global variables have is that it can get changed in runtime, without you noticing.
Class #
Singleton pattern can also be implemented by using class methods and variables. By using class methods we have are sure that we will have a single instance of the class.
class Configuration
def self.add key, value
@@data ||= {}
@@data[key] = value
end
def self.data
@@data ||= {}
end
end
Using it is, again, simple:
>> Configuration.data
=> {}
>> Configuration.add 'environment', 'production'
=> "production"
>> Configuration.data
=> {"environment"=>"production"}
Module #
Simillar to the class implementation, a module can be a Singleton. Also, by default, instantiating an object from a module is impossible, which is a nice thing when it comes to the Singleton pattern.
module Configuration
def self.add key, value
@@data ||= {}
@@data[key] = value
end
def self.data
@@data ||= {}
end
end
Using the module is the same as using the class:
>> Configuration.data
=> {}
>> Configuration.add 'environment', 'production'
=> "production"
>> Configuration.data
=> {"environment"=>"production"}
The good, the bad and the Singleton Pattern #
There is quite a debate going on the internet about the Singleton pattern. Some are in love with it, others consider it an anti-pattern and despise it. What’s very crucial that you must understand is that every pattern has it’s use cases. Yes, each and every one of them!
In my opinion, the problem with the Singleton pattern is that if you fall “in love” with it, you might overuse it and then the coupling and the global state might hunt you for years. On the other hand, the best examples where you should use Singletons that come to mind are application Configuration and Loggers. So, like I said, it has it’s use cases but you should really think hard about it before using it.
When and where have you used Singletons? Is it that bad, or is it much better? What’s your opinion on using the Singleton pattern?