So-called design patterns help de­velopers in object-oriented pro­gram­ming by providing tried and tested templates to solve pro­gram­ming tasks. Once the most suitable template has been found from the ap­prox­im­ately seventy design patterns, it is refined by making in­di­vidu­al ad­apt­a­tions. However, the general approach for the pattern stays the same. The singleton design pattern is very powerful but has the repu­ta­tion of being a relic in object-oriented pro­gram­ming. In our guide, we’ll introduce you to its strengths and weak­nesses, and show you how it is used in pro­gram­ming.

What is the singleton pattern?

The singleton pattern belongs to the category of cre­ation­al patterns within the realm of design patterns. A less fre­quently used name is simply ‘singleton’. Its purpose is to prevent more than one object from being created by a class. This is achieved by creating the desired object in a class and re­triev­ing it as a static instance. The singleton is one of the simplest but most powerful patterns in software de­vel­op­ment.

Quote

The ‘Gang of Four’ (GoF) – a team of pro­gram­mers from the US – says this about the singleton pattern: “Ensure that a class has exactly one copy and provide a global access point to it.”

What are the char­ac­ter­ist­ics of the singleton pattern?

If the singleton design pattern was used to create an instance of a class, then the pattern makes sure that it really only remains with this single instance. The singleton makes this class of software globally ac­cess­ible. In different pro­gram­ming languages, there are different methods to achieve this. To make sure that it remains with only one unique instance, users must be prevented from creating new instances. This is achieved by the con­struct­or declaring the pattern as ‘private’. This means that only the code in the singleton can in­stan­ti­ate the singleton itself. In effect, this guar­an­tees that only one and the same object can reach the user. If this instance already exists, no new instance is created. A possible singleton looks like this:

public class Singleton {
	private static Singleton instance; // protected from external access and static
	private Singleton() {} // private constructor with external access protection
	public static Singleton getInstance() { // public method, call out by code
		if (instance == null) { // only if no instance exists, then create a new
			instance = new Singleton();
		}
		return instance;
	}
}

The singleton pattern in a UML rep­res­ent­a­tion

In the below diagram using UML, the entire singleton design pattern is made up of a single object, since only a single instance of a class needs to be created.

From the outside, it’s not possible to change any part of the unique piece that’s been created. This is the goal when using the singleton design pattern.

Ad­vant­ages and dis­ad­vant­ages of the singleton design pattern

An overview of the ad­vant­ages

A singleton can be written quickly and easily, because it’s not populated with countless (global) variables. It en­cap­su­lates its creation, which means that it can also exercise precise control over when and how it is accessed. An existing singleton pattern can be derived by means of sub­classes to fill new func­tion­al­it­ies. Which of these is used is decided dy­nam­ic­ally. And, last but not least, a singleton is created exactly when it is needed – a char­ac­ter­ist­ic that’s referred to as lazy loading. The process of in­stan­ti­at­ing a singleton earlier – before it’s even needed – on the other hand, is called eager loading.

An overview of the dis­ad­vant­ages

The un­in­hib­ited use of singletons leads to a state similar to that in pro­ced­ur­al pro­gram­ming (i.e., the non-object-oriented), and can lead to unclean programme code. The global avail­ab­il­ity of singleton design patterns poses risks if sensitive data is being handled. If changes are made to the singleton, you won’t be able to trace which programme parts are affected. This makes software main­ten­ance difficult, because mal­func­tions are difficult to trace. The global avail­ab­il­ity of the pattern also makes it difficult to delete singletons, since software com­pon­ents can always refer back to this singleton. In ap­plic­a­tions with many users (multi-user ap­plic­a­tions), a singleton can reduce programme per­form­ance, because it rep­res­ents a data bot­tle­neck, being singular.

The singleton design pattern ‘in real life’

The singleton is mostly used when recurring tasks in a programme routine have to be completed. This includes data that has to be written into a file, e.g. during logging, or print jobs that have to be written into a single printer buffer again and again. Because drivers and cache mech­an­isms also have recurring processes, the singleton design pattern is commonly used for these as well.

Since it’s very difficult to test the singleton pattern, we’ll il­lus­trate how it works using the example of a small company in which several employees use one printer. An example that is close to practice is presented in the Design Pattern Tutorial Series by Daniel H. Jacobsen. The following singleton design pattern is based on this.

If a user sends a request to the printer, the singleton asks the ‘question’: ‘Is there already a printer object? If not, then create one.’ This is solved with an if/then-statement (return printer == zero ?). To prevent access and changes, single variables and the printer are set to ‘private’ rather than ‘public’.

public class printer {
	private static printer;
	private int NumberPages;
	private printer() {
	}
	public static printer getInstance() {
		return printer == Null ? 
				printer = new printer() : 
				printer;
	}
	public void print(String text){
		System.out.println(text +
				"\n" + "number of pages printed today" + ++ NumberPages +
				"\n" + "---------");
	}
}

The next step is to ‘en­cap­su­late’ employees of the branch office. The strings for the names, position, and role within the company are also set to ‘private’.

public class Employee {
	private final String name;
	private final String position;
	private final String role;
	public employee(String name, String position, String role) {
		this.name = name;
		this.position = position;
		this.role = role;
	}
	public void printCurrent role (){
		printer = printer.getInstance();
		printer.print("employee: " + name + "\n" +
			"Position: " + position + "\n" +
			"Role: " + role + "\n");
	}
}

Finally, the two singletons are in­teg­rated into an output routine.

public class Main {
	public static void main(String[] args) {
		Employee andreas = new employee ("Andreas",
				"Boss",
				"Manages the branch office");
		Employee julia = new employee ("Julia",
				"Consultant",
				"Advises customers on complaints");
		Employee tom = new employee ("Tom",
				"Selling",
				"Sells the products");
		Employee stefanie = new employee ("Stefanie",
				"Developer",
				"IT maintenance in the branch office.");
		Employee matthias = new employee ("Matthias",
				"Accountant",
				"Financial accounting of the branch office.");
		andreas.printCurrentRole();
		julia.printCurrentRole ();
		tom.printCurrentRole ();
		stefanie.printCurrentRole ();
		matthias.printCurrentRole ();
	}
}
Go to Main Menu