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:

  • Creational Patterns – deal with the creation of software objects.
  • Structural Patterns – designing a class to be functional and flexible.
  • Behavioural Patterns – describe ways in which object classes communicate with one another.

 In this blog post I’m going to cover a pattern from each of these categories in depth.

Singleton (Creational)

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.

What is the Singleton pattern and why use it?

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.  

The structure

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.

Overview of the features of the Singleton pattern

  • At any time, only a single or zero instances of the Singleton class may exist in the application.
  • Singleton classes should have a single, private, parameter less constructor. This means subclassing isn’t allowed and the Singleton class should be marked as sealed.
  • It’s assumed Singleton objects will be lazy instantiated when they are needed, but it’s not going against the pattern to instantiate when the application is loaded.
  • The private static field holds the only reference to the instance and the public static method provides access to this field.

 Basic implementation

Text

Description automatically generated

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.

Thread-Safe implementation

Text

Description automatically generated

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.

Adapter (Structural)

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:

  • Adaptee – This is the actual class interface which is needed by the client but can’t be accessible directly by the client.
  • Adapter – This is the class which adapts the adaptee interface and exposes the interface needed by the client.
  • Client – This class uses interface defined by the adaptor class to execute the operation.

And there are two kinds of adapters, Object Adapters and Class Adapters

Object Adapters

  • Hold an instance of the adaptee
  • Implement or inherit the adapter type
  • Use composition and single inheritance

Class Adapters

  • Inherit from the adaptee
  • Implement the adapter interface

Where would we need an adapter?

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.

Text

Description automatically generated

And here we have some legacy code that has always managed the loading of Cake objects from the database.

Text

Description automatically generated

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.

Text

Description automatically generated

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.

Implementing the Adapter pattern

Below is our new Cake loading class, NewCakeLoader() that implements INewCakeLoader which also contains a new method GetCake().

Text

Description automatically generated

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.

A screenshot of a computer

Description automatically generated with medium confidence

We can now update our LoadCake client class to use our newly created adapter.

A screenshot of a computer

Description automatically generated with medium confidence

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.

A screenshot of a computer

Description automatically generated with medium confidence

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.

Observer Pattern (Behavioural)

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.

The structure

Diagram

Description automatically generated

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.

Text

Description automatically generated

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.

Text

Description automatically generated

and here are the Observer and Subject interfaces that both classes implement.

Text

Description automatically generated

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. 

Text

Description automatically generated

Once run, the program produces the following output as expected

Text

Description automatically generated

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! 



Source link

Related Post

Leave a Comment