Design Pattern Classifications Part 3— Behavioral Pattern

Avelon Pang
7 min readJun 1, 2021

Behavioral Patterns

In my previous articles we introduced the creational and structural design patterns. By now you know design patterns are considered best practices by experienced object-oriented software developers and how they are classified by intent. For this article we will focus on the behavioral design pattern, which defines manners of communication between classes and objects as well as interactions between the objects.

With 10 (classic) behavioral patterns to review, we will go ahead and dive right in!

Template Method

This design pattern defines the skeleton of an algorithm in the superclass, but allows subclasses to override specific steps of the algorithm without changing its structure. Consider using this method when you have several classes that contain almost identical algorithms with some minor differences. If this is the case, you may need to consider modifying all classes when the algorithm changes. This could also be useful when you want to let clients extend only particular steps of an algorithm, not the entire algorithm itself or its structure.

Pros

  • You have control by letting clients override only certain parts of a large algorithm, which makes them less likely to be affected by changes that happen to other parts of the algorithm

Cons

  • Increasing difficulty as more steps are added
  • Violation concerns of the Liskov Substitution Principle by suppressing a default step implementation
  • Algorithm’s skeleton may create client limitations

Chain of Responsibility (CoR)

This pattern lets you pass requests along a chain of handlers. When a request is received, each handler decides either to process the request or to pass it to the next handler in the chain. This design relies on transforming particular behaviors into stand-alone objects, called handlers. Handlers process requests and pass them further along the chain until all handlers have had a chance to process them. They also have the ability to decide not pass the request further down the chain, which would effectively stop any further processing.

This pattern could be effective when it’s essential to execute several handlers in a particular order or when your program is expected to process different kinds of requests in various ways (with the condition that the exact types of requests and their sequences are known beforehand).

Pros

  • Control over the order of request handling
  • Single Responsibility Principle — option to decouple classes that invoke operations from classes that perform operations
  • Open/Closed Principle — Introduction of new handlers into the app without breaking existing code.

Cons

  • Risks of some requests being unhandled

Command

The intent of this pattern is to turn a request into a stand-alone object that contains all information about the request. This transformation allows you to pass requests as method arguments, queue a request’s execution, delay and support operations. This pattern is helpful when you want to implement reversible operations or parametrize objects with operations. This is also an effective approach to queue operations, schedule their execution or execute them remotely.

Pros

  • Single Responsibility Principle — duplication of classes that invoke operations from classes that perform these operations
  • Open/Closed Principle — introduction of new commands will not interfere with existing code or break the app.
  • Implementation of undo/redo and ability to assemble a set of simple commands into a complex one

Cons

  • Introduction of new senders and receivers risks complicated code

Iterator

This pattern’s intent is to make elements of a collection accessible without exposing its underlying representation. This pattern can be used efficiently to reduce duplication of the traversal code within the app or to hide the complexity of the data structure from clients for increased security. It is suggested to know these types of structures or have the intention for your code to traverse various data structures prior to implementing them.

Pros

  • Each iterator object contains its own iteration state, which allows you to decide if you want to delay or continue an iteration.
  • Single Responsibility Principle — Ability to extract traversal algorithms into separate classes
  • Open/Closed Principle — New implementations will not compromise existing code

Cons

  • Proceed with caution when implementing if the app only contains simple collections

Mediator

This pattern is designed to force collaboration between objects by reducing their chaotic dependencies and restricting direct communication. By converting ‘many-to-many’ relationships to ‘one-to-many’, we simplify communication, making it easier to understand. This pattern is usually suggested when tightly coupled classes are difficult to change or if there’s an inability to reuse components in different programs due to their dependencies on other components.

Pros

  • Single Responsibility Principle — extraction of communication between various components
  • Open/Closed Principle — new mediators will not change the actual components
  • The reuse of independent components and reduction of coupling between them could simplify your code

Cons

Momento

This is also known as a ‘Snapshot Pattern” because it allows you to undo the changes or errors made. By making the object itself responsible for creating the snapshot of its state, it makes the data safe and secure. This makes it possible to restore a previous sate of the object without violating its encapsulation.

Pros

  • Snapshots of an object’s states provides an easy recovery technique

Cons

  • Creation of too many momentos could increase the risk of over-consuming the RAM and increase maintenance costs
  • Not tracking the originator’s lifecycle could lead to obsolete momentos
  • With dynamic programming languages, there is no guarantee that the state within the mommento will not be altered

Strategy

The strategy pattern allows you to switch the strategy or algorithm based upon the situation. This pattern is suggested if you want to be able to switch from one algorithm to another during runtime or if you want similar classes to have different ways of executing some behavior. To use this pattern, your class should have a massive conditional operator that switches between different variants of the same strategy.

Pros

  • Open/Closed Principle — New strategies do not require a change in context
  • Flexibility in swapping algorithms used inside an object at runtime

Cons

  • Risk of complicating existing code if there are only a few algorithms
  • Must be careful to select the correct algorithm for each switch

State

The intent of the state pattern is to let an object alter its behavior when its internal state changes, which makes that object appear to have changed its class. The state pattern is often considered when there is a large number of states or if the state-specific code changes frequently. This pattern gives you the ability to extract common code into abstract base classes and allows you to create hierarchies of state classes that reduce code duplication.

Pros

  • Single Responsibility Principle — Increase organization of code
  • Open/Closed Principle — new states will not change existing state classes

Cons

  • Risk of over-complicating your code if it only has a few states

Last but not least….

Visitor

The purpose of the visitor pattern is to let you separate algorithms from the objects on which they operate. This approach is effective if you need to perform an operation on all elements on an object tree or have an application that manages several products because it will allow you to have a hierarchy of objects to modify their behavior without changing the source code.

Pros

  • Open/Closed Principle
  • Single Responsibility Principle — move multiple versions of the same behavior into the same class
  • Can accumulate some useful information while working with various objects

Cons

  • Tasked with constantly updating all visitors each time a class is introduced or removed from the element hierarchy
  • May not be able to access private fields or methods

Conclusion

That was a long one, but we did it! We reviewed the applicability, benefits and drawbacks of all 10 behavioral patterns. That was a lot of information to take in all at once, so I suggest you come back to them after a walk or a quick break. Since this was intended to be a brief overview, I have included resources below that expand on these patterns in greater depth.

Previous Articles:

Additional Resources:

--

--

Avelon Pang

Full stack software developer with a passion for applying new technologies and a zest for technical problem solving. Bilingual in English and Mandarin.