Singleton: One is the Loneliest Number

What is a Singleton?

A singleton is a creational design pattern in which a class is responsible for the creation and management of its sole instance and it provides a way for the instance to be accessed from the global namespace.

For more information on the singleton pattern I highly recommend Source Making’s explanation.

The Singleton and Me

Currently I use a custom logging mechanism that is implemented using the singleton pattern and a more basic version of this will be what I use in the code example. The logger needs to behave differently based on the environment in which it is executed (development, testing, or production). Loggers in general are typically designed as a singleton.  Subclassing a singleton is not different logistically as it would be with any other class. The trick is to make sure that proper instance is available when requested.  For this purpose I used a registry of singletons.

Let’s Get to the Code

The code below is written in ruby and is one of the many ways to create a singleton along with its registry in this language.  It is composed of the Logger class and two of its children, the ProductionLogger and the DevelopmentLogger.

The Logger Class

class Logger

  @@instance = new

  def initialize *args, &block
    populate_registry
  end

  def self.instance
    Logger.singleton_registry_look_up ENV['DEVELOPMENT_ENVIRONMENT']
  end

  #using a mutex for thread safety
  def self.mutex
    @mutex ||= Mutex.new
  end

  def self.registry
    @registry ||= {}
  end

  #force all children to implement log so there is a common interface
  def log information
    raise "Must Implement Log Method"
  end

  def close
    @log.nil? ? (raise "Must Declare Log Variable as @log") : @log.close
  end

  def self.singleton_registry_look_up singleton_name
    self.registry[singleton_name.to_sym]
  end

  private

  def register singleton_name, logger_type
    self.class.registry[singleton_name.to_sym] = logger_type
  end 

  #major drawback of using static variables is that all subclasses have to be
  #instantiated so that they can be registered
  def populate_registry
    ["DevelopmentLogger", "ProductionLogger"].each do |logger_type|
       register logger_type, Object.const_get(logger_type).send(:new)
    end
  end

  def logger_mutex
    self.class.mutex.synchronize { yield }
  end

  private_class_method :new
end
What Makes Logger Class a Singleton

Making the Logger.new method private keeps new Logger instances from being created, the only Logger instance belongs in the @@instance class variable.

Setting Up the Registry of Singletons

When the new method is called and assigned to the @@instance variable it performs new’s normal duties which involves calling the private method initialize . The initialize method on the Logger class starts a chain of events that create the registry and then populates it.

The registry itself, a hash, holds the name of the classes (keys) and also an instance of the class (values). The point of the registry of singletons is to provide the correct instance when an instance is requested. This is done via the Logger.singleton_registry_look_up which takes the string name of the class and returns an instance. I prefer to read the string from the environment but there are other ways to do this as well.

The DevelopmentLogger Class

class DevelopmentLogger < Logger

  def initialize *args, &block
    @log = File.open('log/development.log', 'a')
  end

  def log information
    #development log does not need to redact any information
    logger_mutex { @log.puts information }
  end

end

This class inherits from the Logger class and overwrites the initialize method to assign the proper log file to the class variable called @log . It also implements the log method which is abstracted in the parent.  The log method literally just appends the argument to the file assigned to the @log variable.

The ProductionLogger Class

class ProductionLogger < Logger

  def initialize *args, &block
    @log = File.open('log/production.log', 'a')
  end

  def log information
    #the actual password should be redacted in production log
    logger_mutex { @log.puts information.gsub(/(?<=password:\s).*/im, "FILTERED") }
  end
end

This class also overwrites the initialize method and opens the correct log for production.  The ProductionLogger’s log implementation filters out any potential passwords that meet the regular expression from the log file.

Using the Logger Class

#inject into the application
load './logger.rb'

#access the instance
LOGGER = Logger.instance

#write to log
LOGGER.log "Hello Log!"

Using load to inject the Logger into your application executes the @@instance assignment.  Assigning the Logger.instance to a constant ensure reassignment won’t happen later.

The Singleton and Its Registry

There are plenty of reasons people do not use the singleton design pattern. Furthermore using a registry can cause further implementation problems. One implementation choice I struggled with was when and how to register subclasses in the registry. I came to the conclusion that it varies on language and needs. Even though the singleton does have drawbacks in my opinion there are valid use cases for them. For instance a logger being implemented as a singleton alleviates the probability of several different objects trying to access the same resource (usually a file) at the same time. If you throw in some thread safety you can be confident the log file has optimal integrity.

Find the Code

Singleton With Registry Repo

* For the sake of this tutorial I did not use ruby’s multiton or singleton modules, however the same effect could have been achieved with them.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s