In the last article, we learned about the first design pattern called the Prototype Pattern. In this article, let’s learn about a new-creational design pattern called the Singleton Pattern.

What is the Singleton Pattern?

Singleton Pattern is a creational design pattern. In essence, this kind of design pattern ensures that each class has only a single instance and all interactions are carried out on this instance.

It provides a private initialization method and maintains a static property to refer to an instance of this Singleton class. Besides, it provides an additional static method to return this static property.

Singleton Pattern
Source: refactoring.guru

Structure

A Singleton Pattern is usually a class that has:

Singleton Pattern
Source: refactoring.guru
  • A private constructor to prevent the creation of the class instance from other classes.
  • A static variable that contains the only instance of that class.
  • A getInstance() method that returns the class instance.

When to Use the Singleton Pattern?

Here are some cases you should consider using it for your code:

  • When a class in your program should have just a single instance available to all clients; for example, a single database object shared by different parts of the program.
  • When you need stricter control over global variables.

Example of the Singleton Pattern

<?php
     class BookSingleton {
          private $author = 'Gamma, Helm, Johnson, and Vlissides';
          private $title  = 'Design Patterns';
          private static $book = NULL;
          private static $isLoanedOut = FALSE;

          private function __construct() {
          }

          static function borrowBook() {
            if (self::$isLoanedOut == FALSE) {
              if (self::$book == NULL) {
                 self::$book = new BookSingleton();
              }
              self::$isLoanedOut = TRUE;
   
              return self::$book;
            } else {
              return NULL;
            }
          }

          function returnBook(BookSingleton $bookReturned)
{
              self::$isLoanedOut = FALSE;
          }

          function getAuthor() {
              return $this->author;
          }

          function getTitle() {
              return $this->title;
          }

          function getAuthorAndTitle() {
return $this->getTitle() . ' by ' . $this->getAuthor();
          }
       }

     class BookBorrower {
          private $borrowedBook;
          private $haveBook = FALSE;

          function __construct() {
          }

          function getAuthorAndTitle() {
            if ($this->haveBook == TRUE) {
              return $this->borrowedBook->getAuthorAndTitle();
            } else {
              return "I don't have the book";
            }
          }

          function borrowBook() {
            $this->borrowedBook = BookSingleton::borrowBook();
            if ($this->borrowedBook == NULL) {
              $this->haveBook = FALSE;
            } else {
              $this->haveBook = TRUE;
            }
          }

          function returnBook() {
$this->borrowedBook->returnBook($this->borrowedBook);
          }
       }

     // Initialization

     echo 'BEGIN TESTING SINGLETON PATTERN\n\n';

     $bookBorrower1 = new BookBorrower();
     $bookBorrower2 = new BookBorrower();

     $bookBorrower1->borrowBook();
     echo 'BookBorrower1 asked to borrow the book\n';
     echo 'BookBorrower1 Author and Title:\n';
     echo $bookBorrower1->getAuthorAndTitle() . '\n\n';

     $bookBorrower2->borrowBook();
     echo 'BookBorrower2 asked to borrow the book\n';
     echo 'BookBorrower2 Author and Title:\n';
     echo $bookBorrower2->getAuthorAndTitle() . '\n\n';

     $bookBorrower1->returnBook();
     echo 'BookBorrower1 returned the book\n\n';

     $bookBorrower2->borrowBook();
     echo 'BookBorrower2 Author and Title:\n';
     echo $bookBorrower1->getAuthorAndTitle() . '\n\n';

     echo 'END TESTING SINGLETON PATTERN';
?>

Output:

BEGIN TESTING SINGLETON PATTERN

BookBorrower1 asked to borrow the book
BookBorrower1 Author and Title:
Design Patterns by Gamma, Helm, Johnson, and Vlissides

BookBorrower2 asked to borrow the book
BookBorrower2 Author and Title:
I don't have the book

BookBorrower1 returned the book

BookBorrower2 Author and Title:
Design Patterns by Gamma, Helm, Johnson, and Vlissides

END TESTING SINGLETON PATTERN

The Pros and Cons of the Singleton Pattern

Pros

  • With the Singleton Pattern, you can be sure that a class has only a single instance.
  • You also gain a global access point to that instance.
  • The singleton object is initialized only when it’s requested for the first time.

Cons

The Singleton Pattern is now considered as an anti-pattern and is also causing a lot of controversy in the developer community. This stems from several reasons:

  • It violates the Single Responsibility Pattern (SRP) because the class that applies this pattern is not only responsible for handling its own operations but is also responsible for managing the number of instances it creates and for providing an access point to that instance.
  • Manipulating global state is what it revolves around. Most programmers, especially programmers specializing in functional programming, agree that the use of global state in the program is one of the riskiest actions because in large programs the ability to accidentally change the value of this global state is inevitable. The accidental change of the global state means that the result of the other code that depends on it will also be changed.
  • The class that applies it cannot be inherited or extended.
  • The unreasonable installation of the Singleton Pattern can lead to some thread-related issues.
  • The Singleton object makes it difficult to carry out testing.

Based on these reasons, it is highly recommended that most developers should avoid using the Singleton Pattern as much as possible.

Are There Any Reasons Left to Use the Singleton Pattern?

In spite of being considered as an anti-pattern, it can still be used in some of the following cases:

  • Debug Logging: Almost all developers will agree that a way to debug logging should be available for every function and part of the code. A singleton could serve this purpose without harming readability, testability or maintainability.
  • Filesystem and Database Access: The argument can be made for Singletons proving access to the filesystem and database, now this might work if you need a single global filesystem or database access point it trades flexibility and testability for a modicum amount of convenience.

Conclusion

In this article, we have learned a lot about the Singleton Pattern. I hope that this design pattern will be a perfect solution to one of your software development problems sometime in the future.

In the next article of this series, we will learn about another creational design pattern called the Object Pool Pattern. Stay tuned!

For further reading:

  1. https://sourcemaking.com/design_patterns/singleton/php/1
  2. https://refactoring.guru/design-patterns/singleton
  3. https://coderoncode.com/design-patterns/programming/php/development/2014/01/27/design-patterns-php-singletons.html

Leave a Reply