Do you know any Design Patterns?
Or
How many Design Patterns have you worked on ?
When this question is posed during an interview, as an Interviewer, I am often apprehensive about hearing answer like ‘Singleton’, ‘Factory’ (not sure which factory) and ‘Repository Pattern’. People often underestimate the significance of Design Patterns as a development tool and an opportunity to showcase their coding and knowledge prowess. Your response to this question can easily set you apart from the rest – Men from the Boys , if I can say so :).
During Interviews, it is paramount that we answer Design Patterns questions not just theoretically but also understand where and how they are used in practice. I recommend trying out these patterns on your own first with a basic sample scenario. Later, review your project code and consider where these patterns can be applied, even if you haven’t had a direct opportunity to work with them.
Live the patterns, make them your own if you want to fully understand and master them, and excel in interviews or while writing best code.
Let’s start with the basics, and then I will help you understand Design Principles and Design Patterns one by one, using a simple analogy.
Imagine you have to solve a problem or puzzle, and to solve it, you need to follow certain rules or best practices. After some trial and error, you manage to find the best solution of the similar (recurring) problem by following the defined rules(principles) defined. Since this
is the best solution you’ve found for a particular type of problem, you decide to share that solution with others for their benefit. This way, when anyone encounters the same problem, they no longer have to figure out the best solution on their own.
In the world of software, the principles(rules) we referred above are known as the Design Principles and the solution you found for the recurring problems is termed as the Design Patterns.
“Design principles are fundamental guidelines or best practices that provide high-level guidance on how to create software that is maintainable, scalable, and robust. “
Some common design principles include :SOLID (Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, Dependency Inversion), DRY (Don’t Repeat Yourself), KISS (Keep It Simple, Stupid), YAGNI (You Aren’t Gonna Need It), “Prefer composition over Inheritance” etc.
Most of you know these design principles, for now I will stop myself from explaining them right now Let’s start with Patterns first and soon I will come back to these design principles as and when required while explaining design patterns.
Design Patterns can be classified into 3 types:
Creational Pattern– As the name suggests, these patterns focus on creation of objects. The method of creation of objects varies to meet different problem statements. e.g.
Structural Pattern-Structural design patterns focus on the composition of classes and objects to form different or larger structures.
Behavioral Pattern-Behavioral design patterns address how objects interact and communicate with each other to accomplish specific tasks and behaviors in a flexible and maintainable way.
Creational Pattern
“These patterns focus on creation of objects”.
What is big deal in creating objects? As a developer you days in days out you are creating objects, either directly in code by instantiating the class or using IoC container. When we create object any class using new keyword directly from inside any other class , we create a concrete object which is tightly coupled to class which creates that instance from within.
According to Open Closed Principles(OCP) (O in SOLID Principles), our classes should be “Open for extension but Closed for modification“, which means we should write the class in such a way that to add new (& different) functionality we should be able add that functionality without have to change or break the existing method.
Remember, it doesn’t say that you can’t change the class at all, obviously, if you have a bug or some modification in the existing method you have to update that.
Please note, Open Closed Principle(OCP) complements Single Responsibility Principle (SRP)
To illustrate this further, let’s explore the Single Responsibility Principle ((SRP), S in SOLID)- SRP states that a class, module or function should have one responsibility i.e. perform a single responsibility only. So what will you do if
you have to to add additional functionality to this class? Will you add
the additional functionality in the same class as an additional method?
It is good time to introduce the Open Closed Principle (OCP) as well . According to OCP principle “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification“
i.e. once a class or method has been written to perform a functionality,it should not allow further modification. It may be confusing for some. Let’s dig in further.
Most likely you will end up doing one of the below when you have a requirement to change a class to add more functionality.
1. Update the existing method in the same class– You can always do this, if your existing functionality has some bug or requirement change. But that is normal, OCP doesn’t restrict you from changing the method for fixing/modifying the logic.
But what if you need to add different functionality which can be called for certain cases? How to add a new functionality while keeping the method the same? I think it should ring a bell for you, The
question you should ask is whether you should create a new class deriving from the existing class and override the method or overloaded method in the same class.
2. Add a new method/function in the existing class– Is the functionality an integral part of that class? If not creating a new method breaks the SRP principle as the class now will have to do two different
jobs. Also, it will not be following OCP(Open-Closed) which it says the class should be closed for modification.
SRP (Single Responsibility Principle) and OCP (Open/Closed Principle) compliments each other.
3. Adding functionality to a new class – If a class has an additional job to perform you can consider creating another class that can perform that additional job.
4. Extending the existing class– You can extend that existing class by creating a new interface and implementing the additional functionality in the new class. You can then add that interface as a
dependency to that class. You can also derive from the existing class and extend it.
These steps can be achieved in number of ways depending upon different requirements, some of which you will learn in GoF (Gang of Four) Design Patterns like Decorator Pattern, Factory Method, etc.
Similar to SRP and OCP, these design patterns are also governed by the rest of SOLID Design Principles i.e. L- Liskov substitution, I- Interface Segregation, and D – Dependency Inversion Principle.
From the above, it should also be clear to you that SRP (Single Responsibility Principle) and OCP (Open/Closed Principle) compliment each other.
Some of important Creational Design Patterns are :
- Singleton Pattern: Ensures a class has only one instance and provides a global point of access to it. Read this article to know more about Singleton Pattern and its usage.
- Factory Method Pattern: Defines an interface for creating objects, but lets subclasses decide which class to instantiate.
- Abstract Factory Pattern: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
- Builder Pattern: Separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
Tip to Remember– Factory and Builder literal meaning relates to creation of something.
Structural Design Patterns
While creational patterns are related to creation of objects, structural patterns are about relationships between classes or objects, making it easier to manage and organize code. Using different relationship like composition, wrapping one class over another class/classes to abstract complexity ,add more functionality or act as its proxy.
Some important structural design patterns include:
- Adapter Pattern: This pattern allows incompatible interfaces to work together by providing a wrapper with a compatible interface.
- Bridge Pattern: This pattern decouples an abstraction from its implementation, allowing both to vary independently.
- Composite Pattern: This pattern allows objects to be composed into tree structures to represent part-whole hierarchies. Individual objects and compositions of objects are treated uniformly.
- Decorator Pattern: This pattern attaches additional responsibilities to an object dynamically. It provides a flexible alternative to subclassing for extending functionality.
- Facade Pattern: This pattern provides a simplified interface to a larger body of code, such as a complex subsystem or library.
- Flyweight Pattern: This pattern is used to minimize memory usage and improve performance by sharing objects that are used repeatedly.
- Proxy Pattern: This pattern provides a surrogate or placeholder for another object to control access to it.
Tip to Remember– Remember ABCD(E)FFP . Another way to remember is most of these pattern names resemble some object or structure or about its look, like Adapter, Bridge, Flyweight, Decorator, Facade, Composite (material).