Understanding and Implementing the Singleton Pattern in Ruby

Understanding and Implementing the Singleton Pattern

Welcome to the first lesson in our Creational Design Patterns course. We are starting with a powerful and widely used pattern: the Singleton Pattern. This pattern helps ensure that a class has only one instance and provides a global point of access to it. Understanding this pattern is a fantastic first step on your journey to mastering creational design patterns.

In Ruby, implementing the Singleton Pattern is quite convenient due to its built-in support. The Singleton module can be included in any class to make it a Singleton. This module provides methods to guarantee that only one instance of the class exists and simplifies global access.

What You'll Learn

In this lesson, you'll learn how to implement the Singleton Pattern in Ruby. We'll cover the following key points:

  1. Including the Singleton Module: We'll explore how to use Ruby's Singleton module to ensure that a class has exactly one instance.
  2. Accessing the Singleton Instance: You'll learn how to access this single instance through a global access point provided by the Singleton module.

Here's a sneak peek of the code you'll be working with:

Ruby
require 'singleton'

class Logger
  include Singleton

  # Log a message to the console
  def log(message)
    puts message
  end
end

# Access the Logger instance and log a message
logger1 = Logger.instance
logger2 = Logger.instance
puts logger1.object_id == logger2.object_id # Output: true

logger1.log("Singleton pattern example with Logger.")
logger2.log("Hey, another message logged!")

In this Ruby snippet, you can see how we ensure that only one Logger instance is created. By including the Singleton module, we automatically get a configured instance method, which acts as the global access point.

Notice, that the Logger.instance method always returns the same object. This behavior is guaranteed by the Singleton Pattern and we can confirm it by comparing the object IDs of the two instances. As expected, they are equal, indicating that both variables point to the same object.

These are the essential parts of the Singleton Pattern:

  • Singleton Module: The Singleton module provides all the necessary functionality to create a Singleton class.
  • Instance Method: The instance method is used to access the single instance of the class without instantiation.

In addition to the built-in Singleton module, you can manually implement the Singleton Pattern in Ruby. Here's how you can achieve the same behavior without the Singleton module:

Ruby
class Logger
  # Class method to access the single instance
  def self.instance
    @instance ||= new
  end

  # Log a message with a count of total logs
  def log(message)
    @log_count += 1
    puts "#{message} (Log count: #{@log_count})"
  end

  private

  # Constructor initializes log count
  def initialize
    @log_count = 0
  end

  # Restrict instantiation to the class itself
  private_class_method :new
end

# Usage example
Logger.instance.log('First log message.')
Logger.instance.log('Second log message.')

In this manual implementation:

  • The self.instance class method is used to access the singleton instance. It utilizes the @instance variable to store the instance, created only if it doesn't already exist, thanks to Ruby's memoization operator (||=).
  • The new method is made private using private_class_method :new to prevent external instantiation of the Logger class.
  • The initialize method is defined to set up any necessary instance variables, such as @log_count.
Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal