
Want to know how much website downtime costs, and the impact it can have on your business?
Find out everything you need to know in our new uptime monitoring whitepaper 2021



The term “Design Pattern” describes a well-known and battle-tested solution to a problem that developers tend to encounter again and again when developing software. Design patterns are conceptual and can be implemented in any programming language.
Design patterns generally fit into one of the following three different categories depending on the problem they address:
In this blog post I’m going to cover a pattern from each of these categories in depth.
This is probably the best known and the simplest to implement design patterns in software engineering. Overuse of the singleton pattern can be a sign of poor architecture but used strategically the singleton pattern is a tried and true solution to a lot of commonly reoccurring scenarios.
A singleton is a class that only allows a single instance of itself to be created, with the class itself being required to enforce this rule. This pattern is generally implemented for use cases where you would be accessing a shared resource, such as read and write operations to a file system or a shared network resource such as a scanner/printer. Another reason might be because the object you are trying to instantiate has such a high resource impact or cost involved that you choose to only create it once.
Setting up the initial structure of a singleton class is very simple. As shown in the class diagram above, it’s just a single class with a private instance and a public static method that provides the only way to reference the instance of the class. Of course, in a real-world scenario, the class would contain other properties and methods, but these are just the ones required for the initial set-up.
The above code does adhere to the rules of a singleton pattern however it is not thread-safe. Two different threads could both have evaluated the test if (instance==null) and found it to be true, then both create instances, which violates the singleton pattern. So, if you are using or plan to use multi-threading in your program, you’ll need to implement the following safety features.
This implementation is thread-safe. The thread takes out a lock on a shared object and then checks whether the instance has been created before creating the instance. You can read more about locking from Microsoft.
The adapter pattern (or sometimes known as a wrapper) is one of the most useful and most popular design patterns in software engineering. This pattern seeks to solve the problem of incompatible interfaces between a client and a service provider. An adapter is created to convert the interface of one class into an interface a client expects. In real-world terms, it’s like a travel adapter for your hairdryer, the adapter doesn’t add any additional functionality, it just allows your device to work with a plug socket it wasn’t designed for.
This design pattern has the following major components:
And there are two kinds of adapters, Object Adapters and Class Adapters
In this scenario let’s imagine a three-tier web application consisting of a front-end client, web server, and a database of some kind. In most cases, the desire of the software engineer creating the system is that the web app should never really care about where it reads and stores data from. Designing a common interface to handle these operations means we can change where we carry out these database read/write jobs without needing to change any code in the logic layer.
So, in our scenario let’s say we have the Cake interface described below.
And here we have some legacy code that has always managed the loading of Cake objects from the database.
You can see that CakeLoader implements a common interface ICakeLoader that defines the contract for a required method called LoadCake(), let’s take a look at that method.
Here the loader is of type ICakeLoader, and CakeLoader just happens to implement that interface. So a CakeLoader is created, and the code can then call LoadCake().
In our scenario, the decision has been made to replace the legacy CakeLoader class as it is written poorly and has a history of being slow and unreliable. A new class has been created to replace CakeLoader and our goal is to implement this change without breaking the application.
We now have an ideal opportunity to use the adapter pattern.
Below is our new Cake loading class, NewCakeLoader() that implements INewCakeLoader which also contains a new method GetCake().
Unfortunately, we can’t just plug in NewCakeLoader into our LoadCake client. It’s not compatible as it requires an ICakeLoader interface. What we can do however is define a CakeAdapter that implements that interface, shown below.
We can now update our LoadCake client class to use our newly created adapter.
Now we have an adapter, our code is protected against this type of change again in the future. If after some time the team designs an Even Better CakeLoader class, we can simply update our CakeAdapter class and the LoadCake client never needs to be changed.
Hopefully, you can see how implementing an adapter can lead to reduced coupling to just the agreed-upon contact, which in turn, increases flexibility and usability into your code.
An observer design pattern (also known as publish-subscribe, or just pub-sub) is perhaps the most widely used design pattern in this list, commonly found in things like GUI components, mailing lists, read receipts in instant messaging applications. The list goes on.
This design pattern consists of multiple software objects which are called observers, these observer objects are registered to an object called a subject for the purpose of getting automatic notifications whenever any state changes occur in the subject object. Based on changes in the subject object your program can then trigger the relevant event handling modules.
Code example
First off, the Subject class. In a real-world scenario, the state of the Subject would almost certainly hold more than a simple string and the observer/pub-sub logic would only be a small fraction of the Subject class, but for the sake of clarity, we’re keeping things bare-bones.
The Subject keeps a list of all of its attached Observers and contains a Notify() method that is called whenever a state change is made. This method loops over the _observers list and updates them all.
In terms of business logic, I’ve just included one method ChangeCakeFlavour() that randomly sets the State value.
Here are our observer classes. Once instantiated and attached, these are the objects that will receive updates from the Subject Notify() method when a state change happens.
and here are the Observer and Subject interfaces that both classes implement.
To test this pattern, I’ve updated the main program code to set up the Subject object and attached the Observers. Then I’m manually calling the public ChangeCakeFlavour() method so we can see the pattern in action.
Once run, the program produces the following output as expected
I hope you have found this post describing three of the most used design patterns useful. There are many more patterns used all across software engineering, examples and descriptions of these can be found in further detail in the book, Design patterns: elements of reusable object-oriented software. Originally published in 1994 the accumulated wisdom found in this book is still as relevant as it was 27 years ago!
Share this

3 min read The allure of OpenClaw is undeniable. You deploy a highly autonomous, self-hosted AI agent, give it access to your repositories and inboxes, and watch it reason through complex workflows while you sleep. It is the dream of the ultimate 10x developer tool realized. But as any veteran DevOps engineer will tell you: running an LLM-backed
7 min read There are cloud outages, and then there are us-east-1 outages. That distinction matters because failures in AWS’s Northern Virginia region rarely feel like ordinary regional incidents. They tend instead to expose something larger and more uncomfortable: too much of the modern internet still behaves as though one place is an acceptable concentration point for infrastructure,
7 min read Artificial intelligence is making software easier to produce. That much is already obvious. Code that once took hours to scaffold can now be drafted in minutes. Boilerplate, integration logic, tests, refactors and small internal tools can be generated with startling speed. In some cases, even substantial pieces of implementation can be assembled quickly enough to
10 min read Whilst AI has compressed the visible stages of software delivery; requirements, validation, review and release discipline have not disappeared. They have been pushed into automation, runtime and governance. The real risk is not that the lifecycle is dead, but that organisations start acting as if accountability died with it. There is a now-familiar story about
4 min read How AI Is Shifting Software Engineering’s Primary Constraint For most of the history of software engineering, the primary constraint was production. Code was expensive, skilled engineers were scarce, and shipping features required concentrated human effort. Velocity was limited by how fast people could reason, implement, test, and deploy. That constraint shaped everything from team size,
5 min read Autonomous Code, Trust Boundaries, and Why Governance Now Matters More Than Ever In Part 1, we looked at how AI has reduced the cost of building monitoring tools. Then in Part 2, we explored the operational and economic burden of owning them. Now we need to talk about something deeper. Because the real shift isn’t
Find out everything you need to know in our new uptime monitoring whitepaper 2021