In this article, we'll be diving into the Singleton Design Pattern, implemented in Python.
As time progresses, software gets more tailored to solving specific problems in different domains. While there are many difference in the application-level of our software, some aspects of software design remain largely the same. These aspects might not remain the same for all software out there but will hold true for a lot of scenarios. Therefore, learning and understanding them will be highly beneficial in helping us build resilient programs.
This is the first in a series about Design Patterns in Python and the different patterns that we can utilize to build software.
What is a Design Pattern?
A design pattern is a particular approach to solving a recurring problem in software development and is also used to represent good practices. It is not actual code, but it represents a particular way of organizing code to produce the desired solution in a recommended way. This means that we do not have to strictly follow set design patterns for all of our code, but analyze our needs and apply the best-suited design pattern for our needs.
Design patterns arise from a wealth of experience in solving problems in software development and are tried and tested to fit particular scenarios. Another reason to learn about design patterns is that, while a pattern may not work for a particular scenario, it can offer a base off which a solution can be formulated.
However, as much as design patterns can help us out of various situations, it is important to assess the current situation and explore all the options available before jumping directly into a pattern because there may be a better solution out there.
Design patterns are divided into a few broad categories, though mainly Creational patterns, Structural patterns, and Behavioral patterns.
There are several aspects of design patterns that set them apart from each other, including the complexity of the design pattern, the application level within a system, and the amount of detail.
Creational patterns include those that define ways to create objects that contribute to increased flexibility and reusability of code throughout the entire application.
Examples of creational patterns include the Singleton pattern, Factory Method, Abstract Factory, Builder and Prototype patterns.
The Singleton Pattern
The singleton pattern is a common creational pattern that is used to define the creation of a single instance of a class while providing a single global access point to that object.
This pattern restricts the number of objects that can be created from a class to one single object that will often-times be shared globally in an application.
This pattern is commonly implemented in features that require control over access to a shared resource such as a database connection or a file. By ensuring that a class can only be used to create a single instance and providing a single global access point, access to the shared resource can be restricted and integrity can be maintained.
The creation of single instances also helps ensure that some aspects of our programs cannot be overwritten by other classes resulting in unsafe or inefficient code. This also allows us to access the same object in multiple points of our programs without the fear that it may be overwritten at some point in our program.
For instance, database connections are done once in our programs, and the same object is used to perform operations on our database all over the application. If different parts of our application could create their own database connections, integrity issues may arise over time as each part tries to access the database on their own.
The Singleton pattern requires that the instantiation of a class is restricted to one object only. The control of object creation is achieved by implementing a creation method that saves the created object in a static field.
All calls to this creation method either return the original singleton object or an error signaling the existence of an instantiated object. This prevents the creation of more than one object for our class and maintains the singleton property.
A good analogy of a singleton pattern is that a country can have a single government that controls access and operations within the country. Any attempts to create another government are forbidden.
We can implement this government analogy in a singleton class as follows in Python:
class SingletonGovt: __instance__ = None def __init__(self): """ Constructor. """ if SingletonGovt.__instance__ is None: SingletonGovt.__instance__ = self else: raise Exception("You cannot create another SingletonGovt class") @staticmethod def get_instance(): """ Static method to fetch the current instance. """ if not SingletonGovt.__instance__: SingletonGovt() return SingletonGovt.__instance__
In our example, we define the variable that will hold the single object to be instantiated. Our constructor checks if there is an existing class and raises an error.
When fetching the object using the
get_instance() method, we check whether an existing instance is available and return it. If not, we create one and return it.
SingletonGovt in action:
government = SingletonGovt() print(government) same_government = SingletonGovt.get_instance() print(same_government) another_government = SingletonGovt.get_instance() print(another_government) new_government = SingletonGovt() print(new_government)
When we run our script, we can see that we have just one
SingletonGovt instance stored at a single point in memory. Any attempts to create another government are thwarted by the exception that we raise:
Pros and Cons
- The Singleton pattern offers the guarantee that only one instance of our class exists and reduces the risk of unexpected behavior in our program.
- Since the creation of the class is controlled by a single class, this offers flexibility since changes only need to be made to one class and object.
- A class created using the singleton pattern violates the Single Responsibility Principle since the class may have to handle more than one responsibility at a given time.
- Life cycle management can pose problems in other areas such as testing since the singleton class is kept alive during the lifespan of the application and different test cases might require new versions of the class.
In this post, we have introduced, discussed and implemented the Singleton Design Pattern.
Just like any other design pattern, it has its pros and cons and while it may be suitable for some situations, it might not apply to all our development needs. It is therefore up to us to analyze the problem at hand and make the decision whether or not the singleton pattern will make our work easier.