[ad_1]
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!
[ad_2]
Source link