Understanding Design Patterns

And why they’re so important

Timo Kats
Level Up Coding

--

Photo by Hal Gatewood on Unsplash

Software is often created by multiple developers working together as a team. On one hand, this can be very beneficial, since the combined effort of many is always more than one. On the other hand, this can bring a number of challenges.

One of these challenges is aligning the different coding styles from the different team members. Moreover, it’s an additional challenge to align these different coding styles in a way that makes the final code-base understandable for future developers who might want to work with it.

To tackle these challenges, developers can use “software design patterns”, which are solutions to commonly occurring problems in software development. These software design patterns aim to serve as a template for developers and can help to structure code, in particular when it’s written by multiple developers.

How to use software design patterns

Before learning how to use software design patterns you must first know if you can use software design patterns. That’s because only OOP (Object Oriented Programming) languages allow them, which is a category of languages that’s based on objects (e.g. classes, structs etc.). Examples of OOP languages are Python, C++ and JAVA.

Next, after figuring out whether you can use software design patterns in your programming language, it’s important to familiarize yourself with some of the necessary theory.

History of software design patterns

Firstly, some history of software design patterns. Software design patterns were first mentioned in the 90s, in the book Design Patterns: Elements of Reusable Object-Oriented Software. In this book there are a total of 23 design patterns, divided in three categories: creational, behavioral and structural.

Often, these design patterns are referred to as “GoF patterns” (GoF means “gang of four”, which refers to the four authors). And, although many new (unofficial) patterns have been created since then, they’re still very relevant today. Thus, if you want to learn more about this subject, it’s a good idea to read that book.

Theory for software design patterns

Next, some technical theory required to understand software design patterns. As mentioned, software design patterns can only be used in OOP languages. That’s because they require code to be structured in objects. Therefore, a prerequisite mastering software design patterns is mastering OOP.

In summary, Object Oriented Programming (OOP) organizes software design around objects (often in the form of classes), instead of functions and logic. With these objects there are four fundamental concepts a developer needs to know, namely: inheritance, encapsulation, polymorphism, and data abstraction.

Firstly, encapsulation. This term refers to objects being able to shield off certain data, meaning that they can be called only from inside the object. For example, if we create a class called “account”, then things like social security number and home address are set to “private” (meaning, they can’t be called from outside the class). But, things like name and job title, are set to “public” (meaning they can be changed from outside the class). This is the core of encapsulation in OOP.

Next, inheritance. As the name suggests, this concept allows objects to inherit functions from each other. This can help avoid writing duplicate functions. For example, if you want to make an object for cats and an object for dogs, you can write functions that are unique to them (like meowing for cats and barking for dogs) in their own object, whilst also writing the functions they have in common (like running and walking) in an abstract class that they both inherit from. (Note, in C these inherited functionalities are often denoted with protected.)

Thirdly, polymorphism. This concept shows how objects can occur in several different forms throughout the code-base. For example, if we create a class called “Animal” with a function to make a sound, we might want to override that function for classes like “Dog” and “Cat” that make very specific sounds. This can be accomplished through polymorphism.

Finally, data abstraction. In some ways, this concept is similar to encapsulation, since it also deals with shielding off functions to the outside world. However, they’re different because encapsulation deals with shielding off information whilst data abstraction deals with shielding off implementations.

For example, if we make an abstract class “Shape”, and two classes beneath that called “Square” and “Circle”, the Area function has to be computed differently for both of them. Hence, (through data abstraction) we can make separate Area functions in the class “Shape”. Thus, we can compute the areas for “Square” and “Circle” in one class!

How to communicate software design patterns

Often, software design patterns are documented in UML (Unified Modeling Language). This is a visual language that can easily document and communicate how software is structured. Thus, it’s very helpful to become more familiar with this language. In fact, the examples from this article were also written in UML.

Example (the Singleton design pattern)

With the theory in mind, let’s implement it in an example. For this example, we’ll look at one of the most popular design patterns, called the “Singleton” pattern. This pattern limits the instantiation of a class to only one instance. In UML, this could be visualized like this.

Quiz: Can you write some JAVA code that complies with the singleton pattern? Answer is given at the end of this article!

So, why could this pattern be useful? Well it could be useful because it avoids initiating to many objects and therefore losing overview of the code-base. Moreover, having only one instance of a class allows for easier access.

In conclusion

Hopefully this article gave a good introduction to the subject of software design patterns. For those who want to learn more about it, there are many books and blogs written about this subject that you can understand with the knowledge gained in this article.

Answer to the quiz

The key to implementing the Singleton pattern is that only one instance of the singleton class can exist (and it has to be accessible globally). Thus, whenever the class is created it should first check whether it already exists. Only if this is not the case (i.e. instance == null) we can create a new singleton…and yes, this is the lazy implementation.

public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {}

public static Singleton getInstances() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

--

--