Design Patterns: Part 1
What is Design Pattern ??
To achieve best practices in object-oriented programming, many developers use Design Patterns. Design Patterns are interpreted as common solutions given for most of the problems we faced in the software development process. These solutions were obtained by trial and error by numerous software developers over quite a substantial period. The concept of design pattern was initiated by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Gang of Four). They published a book called “Design Pattern-Elements of Reusable Object-Oriented Software” in 1994 which led to the emergence of design pattern concepts. According to the fundamentals of this publication, design patterns are primarily based on the principles called program to an interface, not an implementation, and favor object composition over inheritance.
Usage of Design Patterns
The main use of design patterns is to have a common platform or way of implementing the flow of the programs. Therefore all the developers can have an idea of how a particular part of a large software is implemented. As a result, if you found any bug or default in your program, it will help other developers to understand your code in the process of debugging too. Therefore, learning these patterns helps inexperienced developers to construct their code in a well-structured way.
Types of Design Patterns
According to the book published by Gang of Four, design patterns are classified into three categories as Creational Patterns, Structural Patterns, and Behavioral Patterns.
Creational Patterns allow us to create objects simply without exposing the creation logic. Thereby we can pass the type of object we need, and it will provide us that object. As a result, it avoids instantiating objects directly, using a new keyword. This gives the program more flexibility in deciding which objects need to be created for a given input type. Some examples of creational patterns are;
- Singleton Design Pattern
- Factory Design Pattern
- Abstract Factory Design Pattern
- Builder Design Pattern
- Prototype Design Pattern
Structural Patterns consider the structure and composition of classes and objects. To define the composition and structure of classes and objects, it uses the inheritance concept. Thereby we can add new functionalities flexibly to classes and objects. Some examples of structural patterns are;
- Adapter Design Pattern
- Composite Design Pattern
- Proxy Design Pattern
- Fly Weight Design Pattern
- Facade Design Pattern
- Bridge Design Pattern
- Decorator Design Pattern
Behavioral Patterns consider how communication takes place when objects interact with each other to increase the flexibility in communication. Assignment of responsibilities between objects, encapsulating behavior in an object, and delegating responsibilities to objects are some of the techniques used by behavioral patterns. Some examples of behavioral patterns are;
- Chain of Responsibility Design Pattern
- Template Design Pattern
- Observer Design Pattern
- Strategy Design Pattern
- Memento Design Pattern
- Command Design Pattern
Singleton Design Pattern
The Singleton Pattern is one the most used design pattern in java. This type of design pattern comes under a Creational Pattern as this enables one of the best ways to create an object. Singleton Pattern allows only one instance per container. In the java context, one instance per JVM. Singleton Pattern is responsible for the creation of only a single object from a class and providing access to that object within our program. In Singleton classes, we can access the only object created by it without instantiating an object from the class on the outside. To achieve that, we make the constructor of the singleton class Private. Apart from the normal implementation of singleton, we can use a Double-Checking Singleton using the Synchronization keyword while checking the nullability of the singleton instance in multithreaded environments.
Usecase of Singleton Design Pattern
Imagine we have a printer in an office. For the operation of that printer, we have a singleton class called PrintManager. PrintManager is responsible to create a single instance of itself for printer management purposes. The PrintManager is also responsible for the creation and provision of access to a single instance of Printer for printing purposes.
Factory Design Pattern
Factory Pattern also comes under Creational Pattern. The Factory Pattern uses a special Factory Class and replaces the direct usage of new keywords for the construction call of objects. Factory Pattern allows interfaces or abstract classes to create objects but let the subclasses decide which class to instantiate. Therefore subclasses are responsible to create the instance of the class. It allows creating objects without exposing the creation logic by simply passing the needed object type inside the Factory Class and then refer to the newly created object using a common interface.
Usecase of Factory Design Pattern
In this scenario, imagine we have different kinds of printers in an office as LaserPrinter, InkjetPrinter, LEDPrinter, and ThreeDPrinter. All these printer types are subclasses of the class Printer that implements the base functionalities of printers. For the creation of different printer instances depending on the user’s input, we have a class called PrinterFactory. In our program, we can pass the type of printer we need to the PrinterFactory class. Then we can get the desired printer instance as the output for operation purposes.
Prototype Design Pattern
Prototype Pattern avoids the creation process and enables the cloning of created objects to improve the performances. Prototype Pattern comes under a Creational Pattern as it provides one of the best ways to create an object. For the cloning process prototype pattern will create an instance from all the objects in the program one-time and place them in registers or cache place. After that, when a creation request is received for a certain class instance, it will obtain an already created instance from registers(or cache) and clone it and provide it to the user. This pattern is ideal for the object whose object creation process is expensive and costly. But when cloning, we have to be mindful of the Shallow Copy and Deep Copy. Otherwise, it will introduce default or bugs in our program execution.
- Shallow Copy: Copy the value type fields to a new object when cloning. When the data is a reference type, only the reference is copied but not the referenced object itself. Therefore the original and clone refer to the same object.
- Deep Copy: A complete copy of the internal reference type is made when cloning the objects. Copy the value type fields to a new object. If the specified field is a reference type, then a new copy of the referenced object is assigned. Therefore the original and clone will refer to different objects.
Usecase of Prototype Design Pattern
In this scenario, imagine we have different kinds of meals in a Restaurant as ChildrenMeal, VegeterianMeal, and NonVegeterianMeal. All these meal types are subclasses of the class Meal that implements the base functionalities and base attributes of different meals. Since several steps needed to be performed when creating different meals, such as picking the main meal, side meal, beverages, dessert, etc. MealRegistry class is used for the creation and storage of all default meal types in a HashMap. After that, when a user asks for a meal-type, they can pass that meal-type they want and get a clone of the pre-created instances of ChildrenMeal, VegetarianMeal, and NonVegetarianMeal classes. After obtaining cloned instance, if we like, we can set different values to the main meal, side meal, beverages, dessert, etc.
For further more clarification check these resources;