Published on

Learning Design Patterns: Singleton Pattern - The Big Star

Authors

Singleton Pattern isn't the name of a beverage. It's a design pattern that every developer has heard of, experienced, and used constantly, but still doesn't understand why it's a Design Pattern.

Problem Statement - Why Singleton?

In some specific logic, we need to ensure that a class has only one Instance and provides a global access point to that Instance.

Imagine you are building a spending management application. This application needs to access a single object to read and write (ExpendManager) customer spending. If there are multiple Instances of the ExpendManager object, conflicts can occur and the configuration data can be corrupted.

The challenge here is how to limit the creation of multiple Instances of the ExpendManager class and provide a unified way to access that single Instance.

Code Without Singleton Pattern

class ExpendManager(
    private var databaseUrl: String = "localhost",
    private var timeout: Int = 10
) {

    fun loadExpend() {
        println("Loading expend from DB")
    }

    fun addExpend(name: String, amount: Double) {
        println("Saving expend to DB")
    }
}

fun main() {
    val manager1 = ExpendManager()
    val manager2 = ExpendManager()
}

Here, we can create multiple ExpendManager objects, leading to changes on one object not being reflected on other objects.

Code With Singleton Pattern

class ExpendManager(
    private var databaseUrl: String = "localhost",
    private var timeout: Int = 10
) {

    fun loadExpend() {
        println("Loading expend from DB")
    }

    fun addExpend(name: String, amount: Double) {
        println("Saving expend to DB")
    }

    companion object {
      private var instance: ExpendManager? = null

      fun getInstance(): ExpendManager {
          if (instance == null) {
              instance = ExpendManager()
          }
          return instance!!
      }
    }
}

fun main() {
    val manager1 = ExpendManager.getInstance()
    val manager2 = ExpendManager.getInstance()
    println(manager1 === manager2)   // Output: true (same instance)
}

Here, the constructor of ExpendManager is private, and we use getInstance() to get the single Instance of the class.

Lessons From Singleton Pattern

Singleton Pattern helps us:

  • Control the creation of objects, ensuring only one Instance exists.
  • Provide a global, easy-to-use access point.
  • Consider the abuse of Singleton because it can lead to high dependencies and difficult Unit Testing. At the same time, it is necessary to strictly manage the Single Instance issue in a multi-threaded environment.

When designing, consider carefully whether having multiple Instances of a class will cause problems. If only 1 is needed, the Singleton Pattern may be a suitable solution.

Good luck! Remember to Follow @dantech :)