Patterns
Source: https://levelup.gitconnected.com/design-patterns-in-modern-javascript-development-ec84d8be06ca https://medium.com/beginners-guide-to-mobile-web-development/javascript-design-patterns-25f0faaaa
Categories of Design Patterns
1. Creational
For handling object creational mechanisms. Solves a problem by controlling the creation process of an object.
Patterns:
Constructor Pattern
Factory Pattern
Prototype Pattern
Singleton Pattern
2. Structural
Concerned with class and object composition. Structure or restructure one or more parts without affecting the entire system. Ie., they help obtain new functionalities without tampering with the existing ones.
Patterns:
Adapter Pattern
Composite Pattern
Decorator Pattern
Facade Pattern
Flyweight Pattern
Proxy Pattern.
3. Behavioral
Improvecommunication between dissimilar objects.
Patterns:
Chain of Responsibility Pattern
Command Pattern
Iterator Pattern
Mediator Pattern
Observer Pattern
State Pattern
Strategy Pattern
Template Pattern.
1. Design patterns
General reusable solution to a commonly occurring problem in software design.
Singleton pattern
Limit the instantiation of a class to a single object. The first time an object of a class implementing the singleton pattern should be instantiated, it is actually going to get instantiated. Any subsequent try is just going to return that first instance.
Use case
Instantiating configuration objects (you probably only want one configuration instance for your application)
Observer pattern
An object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.
In JS terms, you wouldn’t be looping and asking for the result until you run a function any more. You would, instead, let a subject know that you are interested in events (messages) and would provide a callback function which should be called when new data is ready. You are, then, the observer. Multiple observers can subscribe to the subject.
Multiple observers can subscribe to the subject.
Use cases
When you want to create a one-to-many dependency between objects which isn’t tightly coupled and have the possibility to let an open-ended number of objects know when a state has changed.
JS is great for the observable pattern because everything is event-driven and, rather than always asking if an event happened, you should let the event inform you. Adding an event listener to an element has all the markings of the observer pattern:
you can subscribe to the object,
you can unsubscribe from the object,
the object can broadcast an event to all its subscribers.
Facade pattern
Hides the underlying complexity behind a front, effectively allowing you to work with an API which is easier to grasp while providing the possibility to change the underlying code however you want.
Based on article:
Constructor pattern
Class-based creational design pattern.
Constructors are special functions that can be used to instantiate new objects with methods and properties defined by that function.
It is not one of the classic design patterns. It is more of a basic language construct than a pattern in most object-oriented languages. But in JS, objects can be created on the fly without any constructor functions or “class” definition.
Used for creating new objects of a given kind.
Example: define a Hero
class with attributes like name
and specialAbility
and method like getDetails
. Then, instantiate an object IronMan
by invoking the constructor method with the new
keyword passing in the values for the respective attributes as arguments.
Factory pattern
Another class-based creational pattern.
A generic interface that delegates the responsibility of object instantiation to its subclasses.
Used when we need to manage or manipulate collections of objects that are different yet have many similar characteristics.
Example, create a factory class named BallFactory
that has a method and depending on the parameters it delegates the object instantiation responsibility to the respective class. If the type parameter is "football"
or "soccer"
object instantiation is handled by Football
class but if it is "basketball"
object instantiation is handled by Basketball
class.
Prototype pattern
Object-based creational design pattern.
We use a sort of a “skeleton” of an existing object to create or instantiate new objects.
It utilizes prototypal inheritance instead of a classic object-oriented inheritance. Hence, it plays to JS's strength and has native support.
Example, we have a car
object that we use as the prototype to create another object myCar
with JS’s Object.create
feature and define an extra property owner
on the new object.
Singleton Pattern
Special creational design pattern.
Only one instance of a class can exist.
If no instance of the singleton class exists then a new instance is created and returned but if an instance already exists then the reference to the existing instance is returned.
Example, we have a Database
class that is a Singleton. First, we create an object mongo
by using the new
operator to invoke the Database
class constructor. This time an object is instantiated because none already exists. The second time, when we create the mysql
object, no new object is instantiated but instead the reference to the object that was instantiated earlier i.e. the mongo
object is returned.
Adapter Pattern
Structural pattern.
The interface of one class is translated into another. Lets classes work together that could not otherwise because of incompatible interfaces.
Used to create wrappers for new refactored APIs so that other existing old APIs can still work with them.
Example, we have an old API i.e. OldCalculator
class and a new API i.e. NewCalculator
class. The OldCalculator
class provides an operation
method for both addition and subtraction while the NewCalculator
provides separate methods for addition and subtraction. The Adapter class CalcAdapter
wraps the NewCalculator
to add the operation
method to the public facing API while using its own addition and subtraction implementation under the hood.
Composite pattern
Structural design pattern.
Composes objects into tree-like structures to represent whole-part hierarchies. Each node in the tree-like structure can be either an individual object or a composed collection of objects. Regardless, each node is treated uniformly.
A Multi-level Menu Structure:
Example: a multi-level menu. Each node can be a distinct option or it can be a menu itself which has multiple options as its child. A node component with children is a composite component while a node component without any child is a leaf component.
In this example, we create a base class of Component
that implements the common functionalities needed and abstracts the other methods needed. The base class also has a static method that utilizes recursion to traverse a composite tree structure made with its subclasses. Then we create two subclasses extending the base class — Leaf
that does not have any children and Composite
that can have children and hence have methods handling adding, searching and removing child functionalities. The two subclasses are used to create a composite structure i.e. a tree in this case.
Decorator Pattern
Structural design pattern.
Adds behavior or functionalities to existing classes dynamically. It is a viable alternative to sub-classing.
Easy to implement because JS allows us to add methods and properties to object dynamically. The simplest approach would be to just add a property to an object but it will not be efficiently reusable.
Example, create a Book
class. Further create two decorator functions that accept a book object and returns a "decorated" book
object — giftWrap
that adds one new attribute and one new function and hardbindBook
that adds one new attribute and edits the value of one existing attribute.
Facade pattern
Structural design pattern.
Provides a unified and simpler public facing interface for ease of use that shields away from the complexities of its consisting subsystems or subclasses.
Example, create a public facing API with the class ComplaintRegistry
. It exposes only one method to be used by the client i.e. registerComplaint
. It internally handles instantiating required objects of either ProductComplaint
or ServiceComplaint
based on the type argument. It also handles all the other complex functionalities like generating a unique ID, storing the complaint in memory, etc. But, all these complexities are hidden away using the Facade pattern.
Flyweight Pattern
Structural design pattern.
Efficient data sharing through fine-grained objects. It is used for efficiency and memory conservation purposes.
Used for caching purposes. In fact, modern browsers use a variant of flyweight pattern to prevent loading same images twice.
Example, create a fine-grained flyweight class Icecream
for sharing data regarding ice-cream flavors and a factory class IcecreamFactory
to create those flyweight objects. For memory conservation, the objects are recycled if the same object is instantiated twice. This is a simple example of flyweight implementation.
Proxy Pattern
Structural design pattern.
Acts as a surrogate or placeholder for another object to control access to it.
Used in situations where a target object is under constraints and may not be able to handle all its responsibility efficiently. A proxy, in this case, usually provides the same interface to the client and adds a level of indirection to support controlled access to the target object to avoid undue pressure on it.
Useful when working with network request heavy applications to avoid unnecessary or redundant network requests.
A Proxy object is used to define custom behavior for fundamental operations of a JS object (remember, function and arrays are also object in JS). It is a constructor method that can be used to create a
Proxy
object. It accepts atarget
object that is to be proxied and ahandler
object that will define the necessary customization. The handler object allows defining some trap functions likeget
,set
,has
,apply
, etc. that are used to add custom behavior attached to their usage.Reflect
, on the other hand, is a built-in object that provides similar methods that are supported by the handler object of Proxy as static methods on itself. It is not a constructor, it's static methods are used for intercept-able JS operations.
Now, we create a function that can be thought of as a network request. We named it as networkFetch
. It accepts a URL and responds accordingly. We want to implement a proxy where we only get the response from the network if it is not available in our cache otherwise we just return a response from the cache. The cache
global variable will store our cached responses. We create a proxy named proxiedNetworkFetch
with our original networkFetch
as the target
and use apply method in our handler
object to proxy the function invocation. The apply method gets passed on the target
object itself, this value as thisArg
and the arguments passed to it in an array-like structure args
. We check if the passed url argument is in the cache, if it exists in the cache we return the response from there, never invoking the original target function. If it does not, then we use the Reflect.apply
method to invoke the target
function with thisArg
(although it's not of any significance in our case here) and the arguments it was passed.
Chain of Responsibility Pattern
Behavioral design pattern.
Provides a chain of loosely coupled objects. Each of these objects can choose to act on or handle the request of the client.
Example: the event bubbling in DOM in which an event propagates through a series of nested DOM elements, one of which may have an “event listener” attached to listen and act on the event.
Example, create a class CumulativeSum
which can be instantiated with an optional initialValue
. It has a method add
that adds the passed value to the sum
attribute of the object and returns the object
itself to allow chaining of add
method calls.
Command Pattern
Behavioral design pattern.
Encapsulates actions or operations as objects.
Allows loose coupling of systems and classes by separating the objects that request an operation or invoke a method from the ones that execute or process the actual implementation.
Example, we have a class SpecialMath
that has multiple methods and a Command
class that encapsulates commands that are to be executed on its subject i.e. an object of the SpecialMath
class. The Command
class also keeps track of all the commands executed which can be used to extend its functionality to include undo and redo type operations.
Iterator Pattern
Behavioral design pattern.
A way to access the elements of an aggregate object sequentially without exposing its underlying representation.
We step through an ordered set of values one at a time by calling next()
until we reach the end. The introduction of Iterator and Generators in ES6 made the implementation of iterator pattern extremely straightforward.
Two examples below, first one IteratorClass
uses Iterator spec while the other one iteratorUsingGenerator
uses Generator functions.
The Symbol.iterator
( Symbol
- a new kind of primitive data type) is used to specify the default iterator for an object. It is must be defined for a collection to be able to use for...of
looping construct. In the first example, we define the constructor to store some collection of data and then define Symbol.iterator
which returns an object with next
method for iteration.
For the second case, we define a generator function passing it an array of data and return its elements iteratively using next
and yield
. A generator function is a special type of function that works as a factory for iterators and can explicitly maintain its own internal state and yield values iteratively. It can pause and resume its own execution cycle.
Mediator Pattern
Behavioral design pattern.
Encapsulates how a set of objects interact with each other. Provides the central authority over a group of objects by promoting loose coupling by keeping objects from referring to each other explicitly.
Example, we have TrafficTower
as Mediator that controls the way Airplane
objects interact with each other. All the Airplane
objects register themselves with a TrafficTower
object and it is the mediator class object that handles how an Airplane
object receives coordinates data of all the other Airplane
objects.
Observer Pattern
Behavioral design pattern.
Defines one-to-many dependencies between objects so that when one object (publisher) changes its state, all the other dependent objects (subscribers) are notified and updated automatically. Aka PubSub (Publisher/Subscribers) or Event Dispatcher/Listeners Pattern.
Example, create a simple Subject
class that has methods to add and remove objects of Observer
class from subscriber collection. Also, a fire
method to propagate any changes in the Subject
class object to the subscribed Observers. The Observer
class, on the other hand, has its internal state and a method to update its internal state based on the change propagated from the Subject
it has subscribed to.
State Pattern
Behavioral design pattern.
Allows an object to alter its behavior based on changes to its internal state. The object returned by a State pattern class seems to change its class. It provides state-specific logic to a limited set of objects in which each object type represents a particular state.
Example of a traffic light to understand this pattern. The TrafficLight
class changes the object it returns based on its internal state which is an object of Red
, Yellow
or Green
class.
Strategy Pattern
Behavioral design pattern.
Allows encapsulation of alternative algorithms for a particular task. It defines a family of algorithms and encapsulates them in such a way that they are interchangeable at runtime without client interference or knowledge.
Example, create a class Commute
for encapsulating all the possible strategies for commuting to work. Then, we define three strategies namely Bus
, PersonalCar
and Taxi
. Using this pattern we can swap the implementation to use for the travel
method of the Commute
object at runtime.
Template Pattern
Behavioral design pattern.
Defining the skeleton of the algorithm or implementation of an operation, but deferring some steps to subclasses. It lets subclasses redefine certain steps of an algorithm without changing the algorithm’s outward structure.
Example, we have a Template class Employee
that implements work
method partially. It is for the subclasses to implement responsibilities method to make it work as a whole. We then create two subclasses Developer
and Tester
that extend the Template class and implement the required method to fill the implementation gap.
Further reading (books)
Last updated