Flutter Community
Published in

Flutter Community

Flutter Design Patterns: 20 — Chain of Responsibility

An overview of the Chain of Responsibility design pattern and its implementation in Dart and Flutter

Table of Contents

  • What is the Chain of Responsibility design pattern?
  • Analysis
  • Implementation
  • Other articles in this series
  • Your contribution

What is the Chain of Responsibility design pattern?

A chain of people responsible for neighbours’ freedom a.k.a. proud to be Lithuanian (source)

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

TL;DR: the Chain of Responsibility design pattern is an ordered list of message handlers that know how to do two things — process a specific type of message or pass the message along to the next message handler.

  • Some of these steps are optional based on the request. Well, let’s add some conditional blocks, nothing extraordinary.
  • Oops, we forgot validation… Hmm, the code starts bloating somehow.
  • A wild feature request appears: the order of the steps is different based on the request. Please, stahp…

Analysis

The general structure of the Chain of Responsibility design pattern looks like this:

Structure of the Chain of Responsibility design pattern (source)
  • BaseHandler — an abstract class that contains the boilerplate code common to all the handler classes and maintains a reference to the next handler object on the chain. Also, the class may implement the default handling behaviour e.g. it can pass the request to the next handler if there is one;
  • ConcreteHandlers — contain the actual code for processing the request. Upon receiving a request, the handler could either handle it or pass it along the chain. Usually, handlers are immutable once they are initialised;
  • Client — composes the chain(s) of handlers and later initiates the request to a ConcreteHandler object on the chain.

Applicability

The Chain of Responsibility design pattern should be used when the system is expected to process different kinds of requests in various ways, but neither the request types nor the handling sequence is defined at compile time. The pattern enables linking several handlers into one chain and allowing the client to pass requests along that chain. As a result, each handler will receive the request, process it, and/or pass it further. Also, to resolve the unknown handling sequence problem, handlers could provide setters for a reference field of the successor inside the handler classes — you will be able to add, delete or reorder handlers at run-time, hence changing the handling sequence of a request.

Implementation

  • Info — we want to see those logs locally, but also they should be stored and visible in the external logging service when the application is deployed;
  • Error — those logs must be visible locally and external logging service, but also we want to notify our development team by sending an e-mail when such a log appears.

Class diagram

The class diagram below shows the implementation of the Chain of Responsibility design pattern:

Class Diagram — Implementation of the Chain of Responsibility design pattern
  • logDebug() — logs the message with a log level of Debug;
  • logInfo() — logs the message with a log level of Info;
  • logError() — logs the message with a log level of Error;
  • log() — an abstract method to log the message (must be implemented by a specific logger).

LogLevel

A special kind of class — enumeration — to define different log levels. Also, there is a LogLevelExtensions defined where the operator <= is overridden to compare whether one log level is lower or equal to the other.

log_level.dart

LogMessage

A simple class to store information about the log entry: log level and message. Also, this class defines a private getter logLevelString to return the text representation of a specific log level and a private method getLogEntryColor() to return the log entry colour based on the log level. The getFormattedMessage() method returns the formatted log entry as a Text widget which is used in the UI.

log_message.dart

LogBloc

A Business Logic component (BLoC) class to store log messages and provide them to the UI through a public stream. New log entries are added to the logs list via the log() method while all the logs could be accessed through the public outLogStream.

log_bloc.dart

ExternalLoggingService

A simple class that represents the actual external logging service. Instead of sending the log message to some kind of 3rd party logging service (which, in fact, could be called in the logMessage() method), it just logs the message to UI through the LogBloc.

external_logging_service.dart

MailService

A simple class that represents the actual mail logging service. Instead of sending the log message as an email to the user, it just logs the message to UI through LogBloc.

mail_service.dart

LoggerBase

An abstract class for the base logger implementation. It stores the log level and a reference (successor) to the next logger in the chain. Also, the class implements a common logMessage() method that logs the message if its log level is lower than the current logger’s and then forwards the message to the successor (if there is one). The abstract log() method must be implemented by specific loggers extending the LoggerBase class.

logger_base.dart

Concrete loggers

  • DebugLogger — a specific implementation of the logger that sets the log level to Debug and simply logs the message to UI through the LogBloc.
debug_logger.dart
info_logger.dart
error_logger.dart

Example

First of all, a markdown file is prepared and provided as a pattern’s description:

chain_of_responsibility_example.dart

Your contribution

👏 Press the clap button below to show your support and motivate me to write better!
💬 Leave a response to this article by providing your insights, comments or wishes for the series.
📢 Share this article with your friends, colleagues on social media.
➕ Follow me on Medium.
⭐ Star the Github repository.

https://www.twitter.com/FlutterComm

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store