logo
    • Home
    • Categories
    • About
  • en-languageEnglish
Design patternBy Pierre Colart

Design Pattern Singleton

Description

The singleton design pattern aims to prevent the creation of multiple objects by a class. To do so, if the object has already been created, its class will return its instance. It is one of the simplest design patterns, but it is also the most powerful. It can be implemented in 2 different ways:

  • Lazy
  • Eager

Scenario

🖨️ You are an employee in a company, when the employee launches a print, we check if a printer already exists. In real life, you agree that we don't create a printer for every print, that's why we're going to use the singleton to do that.

Pros and Cons

Pros Cons
✔️Controlled access ❌Procedural programming: in case this pattern is used excessively, we lose all object-oriented notion.
✔️Reduced namespace ❌Difficult maintenance, as malfunctions are very difficult to trace. In case of modifications/deletions, it is difficult to trace which object is using it.
✔️Simple method refinement ❌Reduced performance: because it represents a bottleneck due to its singularity.
✔️Control of number of instances

When to use it?

💡 You have recurring tasks: drivers, cache mechanism, ... 💡 Writing data to a file: logging, print jobs to a printer buffer, ...

UML Diagram

This design pattern can be used in two ways:

  • Lazy
  • Eager

Implementation

Let's start by creating the basis of our design pattern: the Printer class.

Lazy Printer

 package com.design.singleton;

public class Printer {

    private volatile static Printer printer;
    private int numberOfPage = 0;

    private Printer() {}

    public static synchronized  Printer getInstance() {

        if(printer == null) {
            printer = new Printer();
        }

        return printer;
    }

    public void print(String printable, int numberOfPage) {
        this.numberOfPage += numberOfPage;
        System.out.println("This printer has print : " + numberOfPage);
        System.out.println("To be print:" + printable);
        System.out.println("Number of page:" + numberOfPage);
    }

}

Eager Printer

 package com.design.singleton;

public class Printer {

    private volatile static Printer printer = new Printer();
    private int numberOfPage = 0;

    private Printer() {}

    public static synchronized  Printer getInstance() {
        return printer;
    }

    public void print(String printable, int numberOfPage) {
        this.numberOfPage += numberOfPage;
        System.out.println("This printer has print : " + numberOfPage);
        System.out.println("To be print:" + printable);
        System.out.println("Number of page:" + numberOfPage);
    }

}

Now that our singleton is implemented, let's define our employee class:

Employee

 package com.design.singleton;

public class Employee {

    private String name;

    public Employee(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Now, let's test all of this in our main class:

Main

 package com.design.singleton;

public class Main {

    public static void main(String[] args) {

        Employee employee = new Employee("John doe");
        Printer printer = Printer.getInstance();

        System.out.println("Employe : " + employee.getName());
        printer.print("Print this text", 10);

        Printer printer2 = Printer.getInstance();

        System.out.println("Employe : " + employee.getName());
        printer.print("Print this text again", 20);

        printer.print("Print this text again again", 20);

        // Check reference
        System.out.println("Object printer: " + printer);
        System.out.println("Object printer: " + printer2);
    }
}

Result:

 Employe : John doe
This printer has print: 10
To be print: Print this text
Number of page: 10
Employe : John doe
This printer has print: 20
To be print: Print this text again
Number of page: 20
This printer has print: 20
To be print: Print this text again again
Number of page: 20
Object printer: com.design.singleton.Printer@1a407d53
Object printer: com.design.singleton.Printer@1a407d53

Process finished with exit code 0

Here, we have finished our implementation. What we can notice:

  • It doesn't matter if we create a new instance, the object reference remains the same.
  • We can easily put conditions to load multiple instances, for example, to say that our printer can only be implemented X times.

Pierre Colart

Passionate developer and architect who wants to share their world and discoveries in order to make things simpler for everyone.

See profil

Latest posts

Sequences, Time Series and Prediction

© 2023 Switch case. Made with by Pierre Colart