Reader small image

You're reading from  TypeScript 4 Design Patterns and Best Practices

Product typeBook
Published inSep 2021
Reading LevelIntermediate
PublisherPackt
ISBN-139781800563421
Edition1st Edition
Right arrow
Author (1)
 Theofanis Despoudis
Theofanis Despoudis
author image
Theofanis Despoudis

Theo Despoudis lives in Ireland, where he works as a Software Engineer for WP Engine and as a part-time tech practitioner for Fixate. He is the co-author of The React Workshop and Advanced Go Programming in 7 Days, Dzone Core Member, and maintains some open source projects on GitHub. Theo is available for conference talks, independent consulting, and corporate training services opportunities.
Read more about Theofanis Despoudis

Right arrow

Chapter 5: Behavioral Design Patterns

In this chapter, we will describe and analyze behavioral design patterns, which is the last category in the list of classical patterns. Behavioral design patterns try to solve the problem of assigning the precise responsibilities between objects in a way that is both decoupled and cohesive. You want to maintain the right balance between those two concepts so that the clients that interface with the objects won't have to know their internal connections.

In this chapter, we are going to cover the following main topics:

  • Behavioral design patterns
  • The Strategy pattern
  • Chain of Responsibility
  • The Command pattern
  • The Iterator pattern
  • The Mediator pattern
  • The Observer pattern
  • The Memento pattern
  • The State pattern
  • The Template method pattern
  • The Visitor pattern

By the end of this chapter, you will have amassed all the knowledge and skills required to make use of behavioral design patterns...

Behavioral design patterns

With behavioral design patterns, you define abstractions that deal with relationships and the responsibilities between objects. You want to measure and manage how and when two or more objects can communicate with each other by message passing or direct references.

Often, you cannot merely obtain certain object references and call their methods, but you would like to use their functionality. Or you may have diverse ways to define the behavior of an object but you don't want to hardcode many switch statements or draft repetitive code.

The solution to these aforementioned problems is to define helper classes and interfaces that encapsulate this object's behavior and use them according to each occasion. By utilizing the behavioral patterns, you gain several benefits, including increased flexibility, low coupling, and reusability.

I will explain what I mean in greater detail as we look at these patterns, starting with the Chain of Responsibility...

The Strategy pattern?

The Strategy pattern represents a pattern that deals with encapsulating modified algorithms in an interface and making them interchangeable. This means you have an interface that represents a specific process or business case and you interchange concrete implementations at runtime so that you can change its behavior.

This pattern conceptually represents the simplest abstraction because it's essentially an interface that accepts different implementors at runtime. The only complexity you need to consider is how and when to switch between the strategies depending on the current context.

We will explain when to use this pattern next.

When to use the Strategy pattern

You can use the Strategy pattern whenever you see the following patterns:

  • You have different variants of an algorithm and you want to interchange them at runtime: The variants of the algorithm might try different paths or conditions and you want the application to be able to switch...

Chain of Responsibility

Chain of Responsibility is a pattern that allows you to have a list of classes perform certain operations on a particular object in a series of processing events. The main idea here is to establish a chain of handlers that take an object as part of a request, perform some operation, and then pass on the object to the next handler.

An analogy of this pattern is where you have a group of friends and you pass along a message on a piece of paper. Each one of you has a chance to write something or change the message completely. At the end of the chain, you announce the final response.

The main benefit of this pattern is to avoid coupling all the logic into one function or a class and instead give the chance to several middleware handlers to employ their own distinct behaviors. You can say this pattern resembles a list of Decorator functions, each decorating the same object as it passes along the list.

When to use Chain of Responsibility?

You want to use...

The Command pattern

The Command pattern deals with creating special request objects that can be passed as parameters to receiver objects. The main idea of this pattern is that you want to separate a request action from handling each request in a way that both the request and receiver objects are decoupled. For instance, say you create an object that contains all the information necessary to perform actions such as triggering notifications or emails. This object will contain information such as the receiver's email address, name, or title. The receiver merely knows how to process the commands and employs the appropriate handler depending on the type of command. This way, you promote the single-responsibility principle, which is part of the SOLID principles, as you separate the responsibilities of the classes that perform actions and the classes that create actions.

An analogy of this pattern is where you have a microwave with buttons that represent actions. When you press a...

The Iterator pattern

Iterator is an entity that knows how to traverse a list of elements in a collection in an abstracted way. You can think of this pattern as an abstraction over for loops. The main idea is that you want to iterate over a data structure without knowing its inner details or how to access its elements in a particular order. You may want to traverse the elements in a direct or reversed order by simply requesting the right Iterator object.

An analogy of this pattern is when you have a saved list of favorite shows on your hard drive. Each of these videos is saved in a different folder, but you can iterate over them one by one from your UI view without knowing the details of their location in the disk.

We explain in detail when to use this pattern next.

When to use the Iterator pattern?

You want to consider using an Iterator for the following use cases:

  • Traversing a complex data structure: When you have a data structure that is not traversable via a...

The Mediator pattern

Mediator represents an object that acts as a delegator between multiple services, and its main job is to simplify the communication between them. This means it tries to encapsulate their complexities behind a simple interface so that you can change their implementation details without changing the client that uses the Mediator.

You can think of this pattern as an object that sits between one service and a complex subsystem of objects. The client will purely use the mediator service to interact with the other end so that it would not know their API or interfaces. This way, the Mediator works as a sole point of reference between them.

An analogy of this pattern is if you have to interact with a government agency but are located in a different region. To do that, you will have to hire a solicitor to get a power of attorney to perform the actions on your behalf instead of going directly to the agency.

You will learn the main reasons why you should use this...

The Observer pattern

Observer is a behavioral design pattern that establishes a subscription model of communication. In simple words, you use a publisher object that posts messages to a list of active subscribers either in regard to some events that occur or when a specific rule is activated. Those rules could be some points of interest. For example, say you want to send notifications after a user has registered or some part of your application needs to get an update from a different part of the application.

This way, you establish a one-to-many communication model between the publisher and the subscriber list. The publisher would not know what the subscriber list looks like. It only allows a reference to an interface that can push messages into it.

On the other side, the subscriber will receive events from the publisher that it subscribes to and has the choice of acting on or disregarding them. If the publisher somehow gets destroyed, subsequently it will remove any references...

The Memento pattern

Memento is a pattern that deals with saving and restoring an object's state across parts of the application without exposing any implementation details. You can think of it as a state management pattern that offers a simple way of storing data in a repository and then when needed, restores the previous data on demand.

There are three main components of this pattern. You have an object called the Caretaker to maintain a list of Memento objects that offer a simple interface to store and retrieve a state. The last component is the Originator object, which is the object that uses the state to perform its business logic. The Originator coordinates with the Memento object whenever it wants to save or restore its state. The two entities (Caretaker and Originator) do not depend on each other when managing this transition of a state as you abstract all the logic inside the Memento.

Let's explain in more detail when to use this pattern.

When to use the...

The State pattern

The State pattern deals with state management concerning a particular object and, more specifically, how to make an object behave differently based on its inner state. You have an object similar to the Originator object that you learned about in the Memento pattern. Then, at runtime, you change its internal state and the object will behave differently when used by the client.

You can think of this pattern as having a state machine that changes the behavior of an object when its internal state changes. Because you will be placing logic statements based on the object's state parameter, it is useful if you want to implement inheritance without actually defining subclasses.

We'll explain in detail when to use this pattern.

When to use the State pattern?

You can use the State pattern in the following cases:

  • You have an object that responds differently depending on its internal state: Say you have an object that operates internally based on...

The Template method pattern

The Template method pattern is used to define a basic template of an algorithm and have subclasses override specific steps at runtime. This means that you have a series of steps in an algorithm but you want to consider having placeholder calls that delegate the logic to subclasses. Depending on the result, the algorithm behaves differently, which means that this pattern leverages inheritance to provide specialization.

You mainly want to employ this pattern because it can get very repetitive to create similar methods that perform the same operation, such as checking a specific state variable, but differ in some aspects. Let's explain in detail when to use this pattern.

When to use the Template method pattern?

You want to use this pattern when faced with the following problems:

  • You need to keep parts of an algorithm or a process the same but have some methods implement the specialized behavior: The basic steps of the algorithm are the...

The Visitor pattern

The last pattern we are going to explore is the Visitor pattern. This pattern deals with applying customized behavior to an existing list of components that form a hierarchy (a tree or a linked list) but without changing their structure or having them implement an interface.

In practice, this means that you make your components have a method that accepts a reference of a Visitor object and passes its own instance as a parameter to this visitor. The visitor, on the other hand, will have access to each type of visited object's public methods and they can aggregate the state of each object it visits into a different result.

We'll now explain in more detail when to use this pattern.

When to use the Visitor pattern?

The primary use cases of this pattern are explained as follows. You want to use this pattern in the following cases:

  • You want to abstract some functionality for collecting the public state of a hierarchy of objects: You have...

Summary

This chapter demonstrated all the fundamental aspects of behavioral design patterns and how to efficiently utilize them in practice. Those patterns focus on the communication connections among objects and how they perform them without introducing unnecessary complexity in the process.

You started by practicing the basics of the Strategy pattern for developing interchangeable algorithms. Then you discovered the details of the Chain of Responsibility pattern and how to create middleware that processes a request through multiple handlers. You also explored how the Command pattern uses standalone objects that contain all the information about a particular request. Using the Iterator pattern, you learned how to access a traversable structure in a way that does not expose its internal details. Next, you explored how the Mediator pattern can simplify the direct communication between objects. After that, you understood the core principles of the Memento pattern for storing and...

Q&A

  1. How is Mediator different compared to the Observer pattern?

    Both patterns work in a similar manner; however, their goals are marginally different. The goal of the Mediator is to eliminate direct communication between system components. With a Mediator, you usually know the dependent structures and perform calls based on the events that it receives. With Observer, you are slightly more loosely coupled as the publisher does not identify the details of the subscriber list. Some subscribers might choose to ignore certain messages, and some may choose to respond to them.

  2. How is the Chain of Responsibility different compared to the Decorator pattern?  

    The Decorator pattern usually extends one object's behavior and does not try to block the flow of requests. With a Chain of Responsibility, you are allowed to break the flow under certain criteria.

  3. How is Visitor different compared to the Composite pattern?

    The Visitor pattern works together with the Composite...

Further reading

lock icon
The rest of the chapter is locked
You have been reading a chapter from
TypeScript 4 Design Patterns and Best Practices
Published in: Sep 2021Publisher: PacktISBN-13: 9781800563421
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Theofanis Despoudis

Theo Despoudis lives in Ireland, where he works as a Software Engineer for WP Engine and as a part-time tech practitioner for Fixate. He is the co-author of The React Workshop and Advanced Go Programming in 7 Days, Dzone Core Member, and maintains some open source projects on GitHub. Theo is available for conference talks, independent consulting, and corporate training services opportunities.
Read more about Theofanis Despoudis