This article is aimed at anyone who, for the first time, is encountering the concept of design patterns, has heard the term singleton, or somehow implemented the singleton pattern but didn't understand what was happening. Welcome! CodeGym students encounter design patterns for the first time on Level 15, when the captain unexpectedly asks them to "reinforce" their understanding by implementing the singleton pattern with lazy implementation. Students hearing about the singleton pattern for the first time instantly have lot of questions: what in the world is a design pattern? Why do we need it? What is a singleton? And finally, what is lazy implementation? Let's answer these questions in order: Patterns and singleton - for everyone encountering them for the first time - 1

What in the world is a design pattern?

I believe a little history is in order to answer this question with the best understanding. There are four famous programming authors (Erich Gamma, John Vlissides, Ralph Johnson, and Richard Helm) who came up with an interesting idea. They noticed that software development often required them to solve approximately the same problems and write code structured in the same way. So they decided to describe typical patterns that often need to be used in object-oriented programming. Their book was published in 1994 under the title Design Patterns: Elements of Reusable Object-Oriented Software. The book's name turned out to be too long and people began to simply call it the book by the Gang of Four. The first edition included 23 patterns. Afterward, dozens of other patterns were discovered. So let us summarize the answer the question of this paragraph (What in the world are design patterns?) in a few words:
A design pattern is a standardized solution to a common problem.
And the singleton pattern is just one of them.

Why do we need design patterns?

You can program without knowing patterns: after all, by Level 15, you've already written hundreds of mini-programs on CodeGym without even knowing that they exist. This suggests that design patterns are a kind of tool whose usage distinguishes the master from the amateur: Design patterns describe how to properly solve a typical problem. This means that knowing patterns saves you time. In that way, they are similar to algorithms. For example, you could create your own sorting algorithm with blackjack and numbers and spend a lot of time doing so, or you could implement one that has been understood and described for a long time. The same is true with design patterns. Additionally, with design patterns, code becomes more standard, and when using the appropriate pattern, you're less likely to make mistakes, since the pattern's common pitfalls were identified and eliminated long ago. On top of everything else, knowledge of patterns helps programmers understand each other better. You can simply say the name of a pattern instead trying to provide a lengthy explanation to your fellow programmers. Summing up, design patterns help you:
  • not reinvent the wheel, but instead use standard solutions;
  • standardize code;
  • standardize terminology;
To conclude this section, we note that the whole body of design patterns can be divided into three large groups: Patterns and singleton - for everyone encountering them for the first time - 2

Finally, the singleton pattern

Singleton is a creational pattern. This pattern ensures that there is only one instance of a class and provides a global access point for this object. From the description, it should be clear that this pattern should be applied in two cases:
  1. when your program requires that no more than one object of a particular class should be created. For example, a computer game might have a Hero class and only one Hero object that describes the sole hero in the game.

  2. when you need to provide a point for global access to an object. In other words, you need to make the object available from anywhere in the program. Alas, it's not enough to simply create a global variable, since it's not write-protected: anyone can change the variable's value, so the object's global access point might be lost. These properties of a Singleton are necessary, for example, when you have an object that works with a database, and you need to access the database from different parts of the program. A Singleton will ensure that no one writes code that replaces the previously created instance.
So a Singleton satisfied these two needs: there must be only one of a certain kind of object in the program and there must be global access to it. In the example on Level 15, the captain asks you to implement this pattern for the following task:
  1. Find an example of Singleton with lazy initialization.

  2. Create three singleton classes — Sun, Moon, Earth — in separate files using the same principle.

  3. Implement Planet interface in Sun, Moon and Earth classes.

  4. In static block of the Solution class call the readKeyFromConsoleAndInitPlanet method.

  5. Implement the readKeyFromConsoleAndInitPlanet method functionality:

    • 5.1. Read one String parameter from the console

    • 5.2. If the parameter is equal to one of the Planet interface’s constants, create suitable thePlanet object.

After carefully reading the task conditions, we can clearly see why a Singleton is needed here. Indeed, we are asked to create an instance of each of the following classes: Sun, Moon, Earth. It makes sense to assume that we should create no more than one Sun/Moon/Earth. Otherwise, we get into an absurd situation, unless of course you are writing your version of Star Wars. Implementing the Singleton pattern in Java in three steps In Java, Singleton behavior cannot be implemented using an ordinary constructor, because a constructor always returns a new object. Therefore, all implementations of Singleton boil down to hiding the constructor, creating a public static method that controls the singleton object's lifetime, and "destroying" all newly appearing objects. If a Singleton is accessed, it should either create a new object (if one does not already exist in the program), or return an existing one. To accomplish this:
  1. You need to give the class a private static field that stores a single object:

    public class LazyInitializedSingleton {
    	private static LazyInitializedSingleton instance; // #1
    }
  2. Make the (default) constructor private. This means that it cannot be accessed outside the class and will not be able to return new objects:

    public class LazyInitializedSingleton {
    	private static LazyInitializedSingleton instance;
    private LazyInitializedSingleton(){} // #2
    }
  3. Declare a static creation method that will be used to get the singleton:

    public class LazyInitializedSingleton {
        private static LazyInitializedSingleton instance;
            private LazyInitializedSingleton() {}
            public static LazyInitializedSingleton getInstance() { // #3
            if (instance == null) { // If the object has not yet been created
                instance = new LazyInitializedSingleton(); // Create a new object
            }
            return instance; // Return the previously created object
        }
    }
The above example is somewhat clumsy, since we simply hide the constructor and provide our own method instead of a standard constructor. Since this article aims to ensure that CodeGym students come into contact with this pattern (and design patterns in general), the nuances of more complex singleton implementations will not be described here. We note only that, depending on the complexity of the program, this pattern may need to be further refined. For example, in a multithreaded environment (see articles about threads), several different threads may access the singleton method simultaneously, and the code described above will stop working, since each separate thread could create an instance of the class. As a result, there are still several different approaches to creating proper thread-safe singletons. But that's another story =) And finally... What is this lazy initialization that the captain asked about? Lazy initialization is also called deferred initialization. This is programming trick where a resource-intensive operation (and creating an object is a resource-intensive operation) is performed on demand rather than in advance. Sp what actually happens in our Singleton code? In other words, our object is created the time it is accessed, not in advance. You shouldn't assume that lazy initialization is somehow rigidly tied to the Singleton pattern. Lazy initialization is also used in other creational design patterns, such as Proxy and Factory Method, but this is also another story =) The following source was used to prepare this article: