What is a Pattern?
Patterns are situation-based templates for solving problems. Patterns are proven solutions and can be easily reused.
Patterns are NOT exact solutions. They provide a solution scheme.
Anti-patterns — It’s simple, what not to do.
- Object Creational Patterns
Objects are the fundamental building blocks of any logic we write so we must do it right and do it right the first time to save time and effort with quality code.
Patterns such as Factory, Prototype, Singleton and Constructor are all Object Creational Patterns.
- Structural Patterns
Structural patterns help with object composition and typically identify simple ways to realize relationships between different objects.
They help ensure that when one part of a system changes, the entire structure of the system doesn’t need to do the same.
They also assist in recasting parts of the system that don’t fit a particular purpose into those that do.
Patterns such as Adapter, Bridge, Facade, Proxy and Flyweight are Structural Patterns.
- Behavioural Patterns
Based on the way objects play and work together. Behavioural patterns focus on improving or streamlining the communication between disparate objects in a system.
Some behavioural patterns include Iterator, Mediator, Observer, and Visitor.
Here is a list of common/useful patterns and their usage explained that can help in identifying the right pattern for a problem presented.
Like a factory producing multiple copies of the same unit or product — thus the name. We have a class with common properties and functions and we create objects from this class.
Follow the example below.
- Here, the factory is “peopleFactory”, the common traits are “name”, “age”, “hobby” and “printPerson”.
- “person1” and “person2” are the products of the factory “peopleFactory”.
This is more like constructors in C++. Constructors are used to creating specific types of objects — they both prepare the object for use and can also accept parameters, which the constructor uses to set the values of member variables when the object is first created.
Each object is created using the ‘new’ keyword would have its own object created with all the parameters.
Consider the example below.
- Here, the objects are always created using the “new” keyword.
- Each object created have its own properties as well as the function “printPerson”.
- When creating less number of objects, this shouldn’t be an issue but if the numbers of objects grow, redundancy is a problem.
The disadvantage here is Redundancy.
Solution: Constructors With Prototypes.
Here we use Prototype for avoiding code redundancy and having the same function to be used by all objects.
Follow the code below.
- If you look from line 8–11, the function has been moved out of “peopleConstructor” and we are using “prototype” for defining the function.
- This code tweak allows all the objects created using “peopleConstructor” like “person1” and “person2” to share a common method instead of having their own instances.
- The disadvantage here is that the function has been moved outside of the “peopleConstructor” definition so the code is not exactly encapsulated.
Unlike Constructors With Prototypes, which is using the prototype for just defining the function, Prototype Pattern differs.
Consider the code snippet below.
- At the very beginning, the “peoplePrototype” was created empty.
- We assign “name”, “age” and “hobby” DEFAULT properties using the “prototype” keyword.
- Similarly, add the function “printPerson”.
- Now, while creating the objects, we use the “new” keyword to initialize “peoplePrototype” and with Defaults the assign its properties, line 14–17.
- Lines 22–24 help in determining if “hobby” is in “person1” and if “person1” has its own property.
Dynamic Prototype Pattern
This is something very similar to what we did in “Constructors With Prototypes”.
The difference is that the function also becomes part of the class definition so the encapsulation becomes intact here.
Check out the code below.
- We can now pass the values as parameters for creating objects.
- Now, when we create the first object, the function “printPerson” gets created then, next object onwards, all objects share the same instance of the function “printPerson”.
In one line — A class with only a single instance with global access points.
Instead of creating multiple instances from the same class, we have a single instance of the class that has to be managed hence the name, “Singleton”.
Check out the code below.
- If you have noticed, both “singleA” and “singleB” are pointing to the same instance.
- “init” function initializes the singleton.
- We access private properties via public functions.
Facades are a structural pattern. It provides an interface, which shields clients from complex functionality.
When we put up a facade, we present an outward appearance to the world, which may conceal a very different reality, inspiring the name — the facade pattern.
The facade pattern provides a convenient higher-level interface to a larger body of code, hiding its true underlying complexity.
Think of it as simplifying the API being presented to other developers, something that almost always improves usability.
Let us look at the code below.
First is the external independent functions or classes.
“Bank”, “Credit” and “Background” are using Dynamic Prototype Pattern for all the required functionalities for “Mortgage” approval.
Class for “Bank”.
Class for “Credit”.
Class for “Background”.
“Mortgage” acts as an API, which is an interface that hides complex functionalities. Meanwhile, helps with keeping the code loosely coupled.
And this is what runs the show, simple and clean.
- Technically “applyFor” is our API for “Mortgage”.
- We have used Dynamic Prototype Pattern for other classes which API will be called.
- When we call the function “run”, it only knows and calls “Mortgage” and need not know what “Mortgage” does to get the final outcome. Thus, creating a “facade”.
“Proxy” as the name suggests, the calling function calls the proxy to get a job done and this proxy checks/processes and then make the actual call to the real guy.
Likewise, the response returns to the client.
Two things happen here:
- The client need not know the exact function which is getting called.
- If there is processing required before or after request/response, it can be done in the proxy.
Consider the code below.
The actual method that returns the response.
A proxy method called by the client.
Helper function to keep the logs.
The client function calls the proxy method.
- Client function “run” calls “GeoProxy” with the parameters that will get passed to “GeoCoder”.
- “run” calls the function with the same parameters multiple times as well.
- “GeoProxy” checks if the result-set is already in cache for the request, if not it only then calls “GeoCoder”. This avoids the cost of the calls, especially for server calls