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.
Advertisements

Creational 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. Sometimes creational patterns are competitors: there are cases when either Prototype or Abstract Factory could be used profitably. At other times they are complementary: Abstract Factory might store a set of Prototypes from which to clone and return product objects, Builder can use one of the other patterns to implement which components get built. Abstract Factory, Builder, and Prototype can use Singleton in their implementation.
  2. Abstract Factory, Builder, and Prototype define a factory object that’s responsible for knowing and creating the class of product objects, and make it a parameter of the system. Abstract Factory has the factory object producing objects of several classes. Builder has the factory object building a complex product incrementally using a correspondingly complex protocol. Prototype has the factory object (aka prototype) building a product by copying a prototype object.
  3. Abstract Factory classes are often implemented with Factory Methods, but they can also be implemented using Prototype.
  4. Abstract Factory can be used as an alternative to Facade to hide platform-specific classes.
  5. Builder focuses on constructing a complex object step by step. Abstract Factory emphasizes a family of product objects (either simple or complex). Builder returns the product as a final step, but as far as the Abstract Factory is concerned, the product gets returned immediately.
  6. Builder is to creation as Strategy is to algorithm.
  7. Builder often builds a Composite.
  8. Factory Methods are usually called within Template methods.
  9. Factory Method: creation through inheritance. Prototype: creation through delegation.
  10. Often, designs start out using Factory Method (less complicated, more customizable, subclasses proliferate) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, more complex) as the designer discovers where more flexibility is needed.
  11. Prototype doesn’t require subclassing, but it does require an Initialize operation. Factory Method requires subclassing, but doesn’t require Initialize.
  12. Designs that make heavy use of the Composite and Decorator patterns often can benefit from Prototype as well.

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.