Template Design Pattern [Behavioral]

Context: The template method pattern is useful when you need to allow third parties to provide or replace some steps of an algorithm while other parts remain fixed.

  • The fixed functionality is provided by the base type and can be overwritten.
  • The base type can provide so-called hook methods.
  • The hook methods may have default or empty implementations in the base type.
  • Clients can extend or override the behavior provided by the hook methods.
  • The hook operations get called at specific points.

The template method pattern allows extensions only at these predefined point so that clients can override certain steps of an algorithm without modifying its original structure.

The template method allows specific steps in an algorithm to be replaced by clients without modifying its original structure.

The template method defines the steps of an algorithm and allows some of the required steps to be modified.

Example: An employee of the organisation. The employee class would be the base template class. This template class will define hooks like check-in, break, work, check-out etc., with default implementations for them. Then there will be subclasses like Trainee, Software Developer, Lead, Architect, Manager. The work hook can be overridden by each subclass as per their job needs.

Unlike the strategy, this pattern is not about replacing the entire algorithm, but rather about changing certain steps of an algorithm. The fixed functionality is kept in the base type and cannot be modified by subclasses or conforming types. For the steps that can vary, there are extension points also known as hook operations.

Don’t use this pattern if you need to allow changing the entire algorithm. For the latter, use the strategy pattern instead.

Strategy Design Pattern [Behavioral]

The strategy pattern allows us to change the behavior of a type by switching algorithms.

The pattern works by defining a common interface for a family of algorithms. Each algorithm gets encapsulated in its own strategy type. The client only depends on the interface, so it can vary the algorithm by simply switching the concrete strategy object. There is a context, which delegates the client request to its strategy. The strategy is a protocol; it defines a common interface for all concrete algorithms. The concrete strategy types implement a specific algorithm. Clients instantiate the context with a concretestrategy object. The algorithm can then be updated by applying a different strategy instance to the context.

  1. Identify a behavior that the client would prefer to access.
  2. Specify the signature for that algorithm in an interface.
  3. Bury the alternative implementation details in derived classes.
  4. Clients of the algorithm couple themselves to the interface.

The strategy pattern is about decoupling the algorithm implementation details from the type that uses it. This allows changing the object’s behavior at runtime by switching algorithms.

Example:

Modes of transport to travel to one place to another would be a trivial example but suits aptly IMHO. I can choose my own car or a public transport – bus or cab or I can choose flight. I can choose any of these whenever I want to commute.

Logger system with options like logging to Console or logging to file or logging in memory. User can choose the strategy at the time of usage.

So the strategy should be used if you need to implement algorithms that need to be used interchangeably. The algorithm is decoupled from the type that uses it. The context only sees the protocol, which allows clients to easily switch between the various implementations. Another benefit of the strategy pattern is that it lets us add new algorithms without modifying the class that uses them.

The strategy pattern is so straightforward that there are no pitfalls I could mention.

State Design Pattern [Behavioral]

The core idea is to create an object-oriented state machine. The state design pattern can be applied successfully to remove complex conditional logic from a software system.

  • The conditional behavior gets encapsulated into separate types.
  • There will be dedicated types which results in a more flexible system.
  • Makes our code easier to understand.
  • Adding new states or updating existing ones will require less effort.

There are 3 parts: The context, State interface, Concrete types.

The context exposes the public interface to the callers; it delegates requests to its state. The state protocol defines the common interface for all concrete states. Finally, the concrete state types implement the behavior associated with the state of the context. Each state type provides its own implementation for a request, thus the context behavior will be different whenever its state changes.

The state design pattern allows an object to behave differently when its internal state changes.

Example:

  1. States when user withdraws money from an ATM machine
  2. States during the process of user buying something on an e-commerce platform

The state design pattern allows objects to behave differently as their internal state changes. It can be applied successfully to remove monolithic conditional logic from a software system.

To implement the state pattern, you need to identify the steps that cause changes in the behavior of an object. Then, extend these steps and encapsulate them into dedicated state types.

Disadvantage of State Pattern is exposing the states to clients. The state types should be used exclusively by the context, whereas clients should interact with the interface provided by the context.

Observer Design Pattern [Behavioral]

What is Observer Pattern? It’s a way of notifying a group of objects about state changes in another object. The observers subscribe to receive notifications and the subject updates them when its state changes. The observer achieves this while keeping the subscribers independent from the subject’s implementation. All of these objects remain loosely coupled.

  • There is an object referred to as the subject. This subject sends the notifications.
  • The subjet maintains a list of subscribed objects otherwise know as observers and notifies them of changes in its state.
  • The observers need to subscribe to receive notifications from the subject.
  • There is no tight coupling between the subject and the observers.

The observer allows subscribers to get notified about changes in another objectwithout being tightly coupled to the sender.

Example:

Lets take Auctioning system as an example. Here the subject is represented by the auctioneer. The bidders are the observers. The auctioneer updates the bidders when the new bid is accepted or when the reserve price has been met. The auctioneer is independent from the bidder.

Another example is Notification system in software. There are apps which subscribe or observe certain events in the operating system. These events can be user generated events or OS generated events. OS is the subject which on event generation notifies the all the observers about the event.

The observer design pattern allows objects to subscribe to receive notifications about changes in another object. The Notification Center, also known as subject, updates the observers when its state changes. The subject only knows that the receivers adopt the observer protocol. No further implementation details are needed. This loosely coupled design lets us easily add or remove observers. There is no need to modify the subject when creating new observer types. The only requirement is to make the new type conform to the Observer protocol.

Disadvantages: One of the potential issues is related to observers being kept alive by the subject. When an observer is about to be released, we should detach it from the subject. Otherwise, the subject will keep the observer alive and it’s going to update it long after it should have been released. This may cause undesired side effects and all kinds of weird bugs. Another pitfall with this pattern is allowing the observers and the subject to become tightly coupled. The observer pattern is implemented correctly if the subject only caused the observer’s update method.

Memento Design Pattern [Behavioral]

What?  – The memento lets us take a snapshot of an object’s important state.

When implementing this pattern, it is important to keep the safe state external to the object. The object’s internal state is not exposed, thus we don’t break encapsulation. The captured state is kept in a memento object. Later we can restore the object with this saved state.

There are three distinct roles in this pattern:

DP-Memento.jpg

  1. The originator, this is the object that we want to restore to a given state. It has two methods:
    1. The createMemento returns a memento instance, which holds the information needed to reset the originator to that state.
    2. The restore method, which takes the memento parameter that includes the previously saved state. The restore method resets the originator to this state.
  2. The memento is quite simple, it has a state. This can be a built-in or a custom type that can hold the originator’s state; it doesn’t contain any further logic. The caretaker provides the save and restore functionality. It asks the originator to save its state or to return to a previously saved state.
  3. The caretaker keeps track of all the previously created mementos.

The memento captures the important state of an object without exposing its internal data. The saved state can be used to reset the object to a previous state if needed. The memento can be used to implement undo and redo operations.

Example: Game states are saved when player hits a checkpoint. The state of the game can include anything from number of enemies or coins collected etc. When player quits and starts the game later the same state at last checkpoint is restored.

Another typical example where Memento can be used in Undo mechanism used.

The memento pattern captures the important state of an object into a memento. This memento can be used to restore the object at a later date. The pattern is implemented correctly if the originator’s internal state is not exposed to the client.

Disadvantages: Saving and restoring the state of a complex object may be time consuming. You should carefully consider performanceimplications when implementing the memento pattern. Another frequent problem is exposing the originator’s internal details, thus breaking encapsulation. The client should rely solely on the caretaker to save and restore the state of the originator.

Mediator Design Pattern [Behavioral]

The mediator design pattern simplifies the communication between objects. It does so by introducing the middleman. In other words, the mediator.

  • Each object communicates only with the mediator.
  • The letter contains all the logic that defines how the objects interact.
  • The mediator pattern promotes loose coupling. The objects must not refer to each other directly. By removing the dependencies between the peers, we get a better, more flexible design.
  • Since the objects are not connected, they can function independently. We can easily reuse or modify them as they are not tightly coupled with other types, whereas if the number of connections between objects is high, we get the monolithic system. Such systems are hard to maintain.

The mediator encapsulates how objects interact and promotes loose coupling by keeping objects from referring to each other explicitly.

Example:

Let’s look at a Chat application of an organization as an example. Our chat app has several users. The users of the org can communicate with any one in that org. Each user will have their own user-id.

To chat with each other these user must be connected with each other. When deploying the chat app we can configure each user with the list of all other users in the org. Now this works fine. But as we add new users we need to keep updating every users’ user-list. This makes it difficult to make any changes or fix potential issues. Here’s where an intermediary can help us. Instead of linking every user one by one, we introduce a middle man. All the users communicate with this mediator, and they are now totally independent without having to maintain a user list. The user when needs to communicate with another user asks the mediator to get connection. The mediator encapsulates the logic for searching for the other user and if exists connects else provides error accordingly.  When new users are added to the org, the necessary logic will be added to the mediator. The mediator removed the tightly coupling between the user objects that need to communicate. So this was a practical example which demonstrates the benefits of the mediator pattern.

Another example would be Smart Home System, where multiple sensors and appliances are connected to a central hub which acts as a medaitor.

To summarize everything up the mediator promotes loose coupling. By reducing the dependencies between our types, they can be reused easily. All the objects should communicate with the mediator instead of talking to each other directly. The mediator encapsulates all the required control logic.

Disadvantage: Since the mediator contains all the control logic for the entire system, its code may become overly complex. We can prevent this with the proper design and by applying further patterns.

Command Design Pattern [Behavioral]

The command pattern encapsulates method invocation in an object. It packages the requests, the receiver, and all the details needed to execute this request into a command object.

The command object has usually only one method, execute. We can then use the command object or pass it to other components in the system.

Callers trigger the execute method on the command object to invoke the actions on the receiver.

The command pattern is useful if you want to decouple components that require specific functionality from the method invocation details and the object which actually executes the actions.

The command pattern can be used to implement support for undo operations. Another frequent application of the pattern is creating macros.

A macro is a command which executes multiple commands on different receiver objects. Now let’s see how the command pattern works using the sequence diagram.

command.jpgFirst, the client creates the command object. The command object packages the receiver and the set of actions required to execute a given request. Next, the client sets the command on the invoker by calling the invoker’s setCommand method. When the invoker needs to perform the given request, it simply invokes the command object’s execute method. After that, the invoker may discard the command or it can keep it for further use. Calling the command object’s execute method causes the invocation of the actions on the receiver.

The command design pattern encapsulates a request into an object. Invokers can call this command object to perform a request without knowing the details required to performthat operation or the receiver that will execute the operation.

  1. Define an interface with a method signature like execute().
  2. Create derived classes that encapsulate subset of a “receiver” object, the method to invoke and the arguments to pass.
  3. Instantiate a Command object for each deferred execution request.
  4. Pass the Command object from the client to the invoker.
  5. The invoker decides when to perform action – execute().

Summary: The command encapsulates method invocation, including all the details needed to perform the action. Callers can trigger the action at a later time without knowing the receiver or the actions that get executed behind the scenes. With this pattern, we decoupled the object that makes the request from the object that actually receives and executes the request. The command pattern is easy to implement.

Disadvantage: Exposing details of the receiver object or any information needed to perform the action to the component that is using the command object. You should also protect your command objects from concurrent access, as they may be used by multiple components and from different threads.

Chain of Responsibility Design Pattern [Behavioral]

Goal: The primary objective is to decouple the sender of an event or request from potential receivers.

The receiving objects form a chain. Basically, they form a linked list so that each object can reference the next one. The request travels through this chain. Each receiving object can decide whether it processes the request.

If an object doesn’t handle the request, it forwards it to the next handler in the chain. The request travels until one of the responder objects handles it.

If none of the objects processes the request, it will reach the end of the chain and nothing happens.

Sometimes you may need to forward the request even if it was processed by one of the responders. What happens if none of the responders takes ownership of the request? The request keeps traveling through the chain until it reaches the end of the chain.

The chain of responsibility organizes potential request processors in a sequence and decouples responders from callers. The request travels until one of the responders processes it or until itreaches the end of the chain.

Example flow: The client fires the request, which reaches the first responder object in the chain. This object doesn’t handle the request, so it forwards it to the next responder. The second responder doesn’t handle the request either. The request travels further through the chain of responders. Now the third responder object decides to handle the request. The request propagation stops here. The next responders are not notified about the request.

The chain-of-responsibility design pattern stands at the core of the responder chain in iOS and macOS.

  • This pattern organizes request processors in a chain.
  • Responders may or may not process the request.
  • Each responder may handle the request, forward it to the next responder, or forward the request after it processes it.
  • The request gets propagated through the chain until it reaches the last responder in the chain.

It is a widely-used pattern that can solve various problems that require passing the request betweenobjects organized in a sequence.

Disadvantage: exposing details of any responder from the chain to other objects in the chain or to the caller.

Proxy Design Pattern [Structural]

The core idea behind the proxy design pattern is controlling access to objects. We define the placeholder or proxy object which represents another object or a remote service. Clients use this proxy, not the real object. The proxy forwards the client request to the actual subject as necessary.

  • The proxy pattern acts as a surrogate for another object and controls access to it.
  • The proxy may also perform optimizations, defer the instantiation of the underlying object, orprotect the resource from unauthorized access.
  • We can use proxies in the following situations:
    • Remote proxies allow access to remote resources like RESTful services. The remote proxy acts as a local placeholder for a remote object. Clients can call methods of this local representative, which in turn forwards the request to the remote object.
    • Virtual proxies can be used to manage the creation of expensive resources and the execution of computationally intensive operations. We can improve performance and memory usage by postponing the costly operations until they are actually needed.
    • Protective proxies control access to sensitive objects. The surrogate object checks whether the caller has the appropriate permissions required to use the protected resource.

The proxy pattern provides a placeholder for another object to control the way the underlying resource is accessed. The proxy introduces an additional level of interaction to support controlled, remote, or delayed access.

Example: Consider if you have to fetch and display data from network only when user does a particular action. Create a NetworkDataProxy class object set the URL and completion handler. This by itself should not download / fetch the remote content. Only when the particular user action is trigger the proxy object will fetch. This defers the downloading of content only on need basis.

Same can be done with an ImageProxy class which can be used to download image only on demand rather than pre-downloading before knowing the user needs.

Useful scenarios:

  • Allow access to remote resources like RESTful services. The remote proxy acts as a local placeholder for a remote object. Clients can call methods of this local representative which in turn forwards the request to the remote object.
  • Manage the creation of expensive resources and the execution of computationally intensive operations.
  • We can improve performance and memory usage by postponing the creation of expensive resources until they are first accessed.
  • Control access to sensitive objects. The surrogate object checks whether the caller has the appropriate permissions required to use the protected resource.

You should not use the proxy pattern if none of these situations apply.

Disadvantages:

The proxy object should be used to perform all the required operations on the resource it represents. The benefits of the proxy pattern are rendered useless if clients can bypass the surrogate and use the underlying types directly.

Flyweight Design Pattern [Structural]

Intent: The flyweight pattern is used to reduce the memory usage and the creation costs for similar objects that rely on the same data.

  • Instead of creating multiple objects, the flyweight pattern shares one instance to represent multiple objects.
  • Each flyweight object is divided into two pieces, the state-dependent extrinsic part, and the state-independent intrinsic part.
  • The intrinsic state is stored in the flyweight object.
  • To implement the flyweight pattern we must identify the immutable state free intrinsic set of properties from the state-dependent extrinsic state.
  • The extrinsic part should be managed by clients and passed to the flyweight instance, whereas the immutable state of the flyweight should be protected from changes.

The flyweight pattern allows the sharing of common objects that can be used identically saving memory by reducing the number of object instances at runtime.

Example from sourcemaking.com:

The Flyweight uses sharing to support large numbers of objects efficiently. Modern web browsers use this technique to prevent loading same images twice. When browser loads a web page, it traverse through all images on that page. Browser loads all new images from Internet and places them the internal cache. For already loaded images, a flyweight object is created, which has some unique data like position within the page, but everything else is referenced to the cached one.

  • The flyweight pattern should be used to support a large number of similar objects via sharing.
  • Instead of creating multiple objects, the flyweight pattern shares one instance to represent multiple objects.
  • To implement the flyweight pattern we must separate the immutable set of properties from the state-dependent part.
  • The intrinsic part should be stored in the flyweight object whereas the state-dependent extrinsic part should be managed by clients. The extrinsic state is stored or computed by client objects and passed to the flyweight when its operations are revoked.

Disadvantages:

Common thing is clients can change the immutable state of the flyweight. The intrinsic state of the flyweight objects must not be modified by callers.

Another wrong implementation is not protecting the flyweight from concurrent access. A shared flyweight instance may be accessed in parallel from several different strats. Therefore, threat safety is imperative.