Behavioral Patterns – Thumb Rules

This post is directly copied from sourcemaking, just to make it easy for readers to access this piece of knowledge.

Rules for how and when to apply:

  1. Behavioral patterns are concerned with the assignment of responsibilities between objects, or, encapsulating behavior in an object and delegating requests to it.
  2. Chain of responsibility, Command, Mediator, and Observer, address how you can decouple senders and receivers, but with different trade-offs. Chain of responsibility passes a sender request along a chain of potential receivers.
  3. Command normally specifies a sender-receiver connection with a subclass.
  4. Mediator has senders and receivers reference each other indirectly. Observer defines a very decoupled interface that allows for multiple receivers to be configured at run-time.
  5. Chain of responsibility can use Command to represent requests as objects.
  6. Chain of responsibility is often applied in conjunction with Composite. There, a component’s parent can act as its successor.
  7. Command and Memento act as magic tokens to be passed around and invoked at a later time. In Command, the token represents a request; in Memento, it represents the internal state of an object at a particular time. Polymorphism is important to Command, but not to Memento because its interface is so narrow that a memento can only be passed as a value.
  8. Command can use Memento to maintain the state required for an undo operation.
  9. MacroCommands can be implemented with Composite.
  10. A Command that must be copied before being placed on a history list acts as a Prototype.
  11. Interpreter can use State to define parsing contexts.
  12. The abstract syntax tree of Interpreter is a Composite (therefore Iterator and Visitor are also applicable).
  13. Terminal symbols within Interpreter’s abstract syntax tree can be shared with Flyweight.
  14. Iterator can traverse a Composite. Visitor can apply an operation over a Composite.
  15. Polymorphic Iterators rely on Factory Methods to instantiate the appropriate Iterator subclass.
  16. Mediator and Observer are competing patterns. The difference between them is that Observer distributes communication by introducing “observer” and “subject” objects, whereas a Mediator object encapsulates the communication between other objects. We’ve found it easier to make reusable Observers and Subjects than to make reusable Mediators.
  17. On the other hand, Mediator can leverage Observer for dynamically registering colleagues and communicating with them.
  18. Mediator is similar to Facade in that it abstracts functionality of existing classes.
  19. Mediator abstracts/centralizes arbitrary communication between colleague objects, it routinely “adds value”, and it is known/referenced by the colleague objects (i.e. it defines a multidirectional protocol). In contrast, Facade defines a simpler interface to a subsystem, it doesn’t add new functionality, and it is not known by the subsystem classes (i.e. it defines a unidirectional protocol where it makes requests of the subsystem classes but not vice versa).
  20. Memento is often used in conjunction with Iterator. An Iterator can use a Memento to capture the state of an iteration. The Iterator stores the Memento internally.
    State is like Strategy except in its intent.
  21. Flyweight explains when and how State objects can be shared.
  22. State objects are often Singletons.
  23. Strategy lets you change the guts of an object. Decorator lets you change the skin.
  24. Strategy is to algorithm. as Builder is to creation.
  25. Strategy has 2 different implementations, the first is similar to State. The difference is in binding times (Strategy is a bind-once pattern, whereas State is more dynamic).
  26. Strategy objects often make good Flyweights.
  27. Strategy is like Template method except in its granularity.
  28. Template method uses inheritance to vary part of an algorithm. Strategy uses delegation to vary the entire algorithm.
  29. The Visitor pattern is like a more powerful Command pattern because the visitor may initiate whatever is appropriate for the kind of object it encounters.
Advertisements

Structural Pattern – Thumb Rules

This post is directly copied from sourcemaking, just to make it easy for readers to access this piece of knowledge.

Rules for how and when to apply:

  1. Adapter makes things work after they’re designed; Bridge makes them work before they are.
  2. Bridge is designed up-front to let the abstraction and the implementation vary independently. Adapter is retrofitted to make unrelated classes work together.
  3. Adapter provides a different interface to its subject. Proxy provides the same interface. Decorator provides an enhanced interface.
  4. Adapter changes an object’s interface, Decorator enhances an object’s responsibilities. Decorator is thus more transparent to the client. As a consequence,
  5. Decorator supports recursive composition, which isn’t possible with pure Adapters.
  6. Composite and Decorator have similar structure diagrams, reflecting the fact that both rely on recursive composition to organize an open-ended number of objects.
  7. Composite can be traversed with Iterator. Visitor can apply an operation over a Composite. Composite could use Chain of responsibility to let components access global properties through their parent. It could also use Decorator to override these properties on parts of the composition. It could use Observer to tie one object structure to another and State to let a component change its behavior as its state changes.
  8. Composite can let you compose a Mediator out of smaller pieces through recursive composition.
  9. Decorator lets you change the skin of an object. Strategy lets you change the guts.
  10. Decorator is designed to let you add responsibilities to objects without subclassing.
  11. Composite’s focus is not on embellishment but on representation. These intents are distinct but complementary. Consequently, Composite and Decorator are often used in concert.
  12. Decorator and Proxy have different purposes but similar structures. Both describe how to provide a level of indirection to another object, and the implementations keep a reference to the object to which they forward requests.
  13. Facade defines a new interface, whereas Adapter reuses an old interface.
  14. Remember that Adapter makes two existing interfaces work together as opposed to defining an entirely new one.
  15. Facade objects are often Singleton because only one Facade object is required.
  16. Mediator is similar to Facade in that it abstracts functionality of existing classes.
  17. Mediator abstracts/centralizes arbitrary communication between colleague objects, it routinely “adds value”, and it is known/referenced by the colleague objects. In contrast, Facade defines a simpler interface to a subsystem, it doesn’t add new functionality, and it is not known by the subsystem classes.
  18. Abstract Factory can be used as an alternative to Facade to hide platform-specific classes.
  19. Whereas Flyweight shows how to make lots of little objects, Facade shows how to make a single object represent an entire subsystem.
  20. Flyweight is often combined with Composite to implement shared leaf nodes.
  21. Flyweight explains when and how State objects can be shared.

Visitor Design Pattern [Behavioral]

The visitor allows adding common behavior to unrelated types. It should be used if we cannot or don’t want to change the source code of the given types.

  • It separates the new behavior into a dedicated visitor. The visitor must visit each object and performs required operations on that object’s state.
  • If a new functionality is required, you must only extend the visitor, or create a new visitor for that matter.
  • This pattern is useful if you need to traverse collections of heterogeneous objects and perform related operations.
  • The visitor works without subclassing.

The visitor lets us add new common behavior to unrelated types without changing their implementation.

Example: Suppose we have added a set of unrelated products, Book, Computer, and Car into a shopping cart of an online store. They have different properties for retrieving their price; itemPrice for Book, unitPrice for Computer, and stickerPrice for Car. So at the time of price calculations we need to check the product type and accordingly call its prie property. This is problematic when the product type list grows.

The visitor patterns solves this problem by extracting the new behavior into a visitor type. The visitor has visit methods that can handle each and every type. So, I declare the Visitor protocol with a visit method for each of the type Book, Computer, and Car. This interface for each type is defined in concrete class of visitor class.

Each product accepts a visitor and then the product calls the visit method of the visitor. This method knows how to calculate price of the product of particular type. Accepting a visitor and calling its visit method – this is called double dispatch.

Double dispatch:

The concrete visitor types need to operate on the subject state. To receive the right instance, the visitor pattern uses a technique called double dispatch. For double dispatch to work, each type has to define an accept visitor method.

The accept method takes a visitor instance as an input argument. Calling the accept method ensures that the current subject gets chosen. For each subject, I implement the accept visitor method.

Accept visitor calls the visitor’s visit method with self as argument. This calls the correct visitor subclass and ensures that the right operation gets executed. The process is called double dispatch because of calling the accept and the visit methods.

In simple words : The visitor should be used to add new behavior to unrelated types without the need of subclassing. No such pitfalls however the concrete visitor interface can grow if abstraction is not done right.

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.