Software design patterns have always been a big topic of discussion amongst everyone in the software development world. It is rather as controversial of a topic as functional vs object oriented programming that often leads to a lot of conflicts amongst peers due to the argument that – overusing patterns can lead to underlying code that can get difficult to understand and intricate to manage especially without understanding the patterns.
If you wish to redo the design patterns of software you work on, you will surely get your chances to be more innovative in creating your code. However, if you wish to get the best code outcome with a strong underlying design, you should be aware of the common design patterns used in software development.
Though there are not too many of these design patterns that are used, knowing these patterns can really help you a long way in surviving in the software world or even landing a good job with one of the software giants. But before we get there, let us get a brief understanding about what software design patterns really are and why they are needed in Software Development.
What are Design Patterns in Software Development?
Designs Patterns are nothing but templates that can be used to develop the final product or software. These patterns are reusable designs or solutions that can be used to develop the product. However, these patterns do not consist of specific code that can be used to code the new solution.
Using design patterns is considered as a best practice in the industry as the design patterns are already tried and tested and commonly written about in recommended software books, which makes the proposed final code more robust and readable. As such design patterns are more like descriptions that help you in tackling the problems for which solutions may have already been designed, tried, tested, deployed.
These design patterns are often used in OOP languages such as Java. Let us see why Design Patterns are used in software development.
Why Use Design Patterns?
The use of pre-existing design patterns for problem-solving and for developing new solutions to existing or recurring problems has always been a point of discussion amongst software developers. That is because there is an increased usage of design patterns and that leads to an absence of originality in the development of the solutions.
Having said that, design patterns were never meant to be a shortcut to a good software design anyway. In the dynamic world of software engineering, nothing can replace the need for the unique problem-solving capabilities of a software developer. No matter what design pattern you choose you will still need to design and develop the solution yourself.
When used appropriately in the right places, design patterns can prove extremely useful. Using design patterns strategically can make a programmer far more efficient thereby allowing them to avoid implementing something completely from scratch, instead they can use a format from the method already developed, refined, and used by others who have developed similar solutions. Design patterns also provide for a common language to communicate solutions to repeated problems with team members.
Now, that you know why the use of design patterns is important, let’s go over five of the most used design patterns.
Top 5 Most Used Design Patterns
1. Singleton or Creational
This design pattern is aimed at creating a class for a single instance of an object. This pattern can prove extremely useful when only one of the objects is required to coordinate and trigger actions across the whole system. For instance, a logger is an object that typically creates just one instance using a getInstance() method to obtain the object and then the log can output information using a single configuration reducing the amount of code and plumbing in the application.
It is not a big deal to initiate only one object for a class, but what is important is to ensure that only one object is created. In order to do that you will need to ensure that the constructor is defined to retain a self reference as a Singleton. This way it can either create itself if it is the first instance or return a previously created refence if it has been instantiated somewhere else in the application.
You can also create subclasses of a singleton by declaring a protected constructor which can help you in certain circumstances. One way of subclassing is to create a singleton register of the subclasses. In this scenario parameters can be passed to the getInstance method or an environment variable can be used to return the required singleton type. The registry can then be used to maintain a mapping of singleton objects to string names and vice-versa which may be accessed when required.
This pattern is used often in thread pools, logging, caching, and driver objects. This pattern is interoperable and can work well with many other advanced designs.
2. Factory Method
This is one of the most popular patterns used by software developers and I personally use it often while writing enterprise code. This is a creational pattern that helps the developer create an object without the need of exposing the logic to the user. To understand this you can consider exactly what happens in a Factory. A factory produces the end products as goods, just as your software factory produces the objects that you call in your classes. And it is done without the need of specifying the class for the object that is to be created but merely a type adapting a similar interface.
To achieve this, a factory method is called instead of a constructor. Example of how an object is created in Java:
AbcClass abcClassObject = new AbcClass();
There is but a problem with this approach. The constructor method being used now relies on a concrete component known as the AbcClass and its implementation. When using a factory method you can create types dynamically as needed. For example if you are creating a game with several monster types that all have an attack player method; you can create these dynamically during runtime using monster = monsterFactory.new(‘monsterType’) and depend on the factory method to return you a new vicious monster to seek and destroy the player.
This design pattern works best when an add-on class is required. For instance, say you have started a pizza restaurant that is offering just two pizzas – cheese and meat. Your billing system as such is designed to handle only these two types. But as the customers start coming in, you get a lot of requests for add-ons such as extra cheese, mushrooms, jalapenos, and onions. When this happens your billing system as such goes haywire.
If you are the restaurant owner you ask your IT guy to resolve the problem and your IT guy then creates subclasses for all the add ons such as the mushrooms, onions, and jalapenos. But when this is done, there is a need for creation of many other subclasses as well for other add ons such as olives, peppers, etc.
In cases like these when requirements increase, the need for making additional subclasses would arise and so would the amount and level of coding needed. Using the decorator design pattern, you can limit the amount of sub-classes needed. This pattern further makes use of abstract components when the structural design is created by being aware of how the system will likely expand.
This software design pattern allows classes that are initially incompatible to be able to work with each other by allowing interfacing one class to the other and vice versa. This design can be explained by taking the example of a translator. When two leaders of different countries whom speak different languages meet, they understand each other through a common medium which is the translator. The translator is the adaptor here that is allowing the interchange and exchange of information between the two.
Consider two applications; one which gives an XML output, and the other with a JSON input, an adapter is required to ensure the two applications work seamlessly with each other and without any issues.
This design pattern is based on the one-to-many relationship between the various objects. When the state of one object changes all the dependents are also notified. This is achieved by calling an observer methods build into the classes. This pattern thereby helps you in creating objects with multiple dependencies.
But before including the observer pattern in your software architecture, you need to consider a few things. You will need to identify the dependent and the independent functionalities. Provide subject abstracts to all the independent classes and place the dependents in an observer hierarchy. The subjects are then coupled with the primary observer class. The observers register to the subjects and the observer classes are informed about their respective changes in the state through subjects.
This is a more advanced concept then the previous concepts but allow for more dynamic systems that frequently need to respond to events.
It is important to decide on the dependency chain while executing the observer design pattern. Another critical thing to do is to determine your sender and the recipients during your design phase to make the code easier to maintain and understand in the future.
Software design patterns can help you create a robust, and well-structured architecture. Solutions developed with such design patterns make your development work easy and allows maintenance and deployment of code feel like a calming summers breeze for other developers that end up working on your code.
Use this piece for a brief idea on these design patterns, so that you get the right knowledge to apply it in the right places.