JavaScript in Plain English

New JavaScript and Web Development content every day. Follow to join our 3.5M+ monthly readers.

Follow publication

Solving Complex Filters with the Chain of Responsibility Design Pattern in JavaScript

Pedro Morais
JavaScript in Plain English
4 min readMay 26, 2021

--

I will use the Chain of Responsibility Design Pattern in a more functional approach to create a clean, maintainable, and clear design for important filters on base objects.

Summary

  • What is the Chain of Responsibility?
  • Understanding the problem
  • Hands-on
  • Conclusion

What is the Chain of Responsibility?

As described in the book Design Patterns: Elements of Reusable Object-Oriented Software, it is a behavioral pattern that allows you to pass a request (which can be a primitive parameter, an HTTP request, an object, etc) to a chain of manipulators (isolated functions or methods).

Upon receiving the request, the handler decides whether to process or move on to the next handler in the chain.

1.0 — Representation of the Chain of Responsibility Design Pattern.

As the representation above illustrates, we have the circle on the left as a request. The middle three squares are the handlers, deciding whether to treat the request or not. Finally, the triangle illustrates the result of the call.

Important to remember: Manipulators need to respect the same interface.

Understanding the problem

Let’s say that we are developing software for restaurants, and that there is a customerOrder object, containing the details of a customer purchase.

We need to filter a list containing several of these objects, and the filter will be based on precise business rules, which are:

  • Orders that were placed by a user who accessed the system via an external link.
  • Orders that were directed to the cashier, but are not yet paid.
  • Orders that were justified at the cashier — where the cashier operator closed the order, identifying the amount the customer paid for either cash, debit or credit card.

In addition to these first requirements listed above, they are likely to increase or change depending on stakeholder feedback.

Hands-on

2.0 — Data structure that will be used in the example.

In that first protocols.js file, we will have the data structure that will be filtered.

The byAccess property makes reference to the place where the request was made — by the native application or by an external link access.

Later on, closeLocal makes reference to the place where the customer will place the order — cashier’s mouth or payment by the app itself.

Finally, justifications is a property that contains an object, where the payment information is. If it is an object, you will have the amount of money paid in cash, debit or credit card. If the order is not paid yet, it will be null.

Below we have another dictionary file, containing some specifications of the numerics — just to make the implementation code more explanatory.

2.1 — Numeric property specifications.

And finally, we have the implementation itself. I will explain each point according to the line of code.

Note: At that time, if you want to save the three files to run the code, run in a Node.js environment with support for ES6 modules.

2.2 — Implementation of the Chain of Responsibility pattern.

The handles object [line 4] contains the rules for filtering. Each rule would be a handler described in the explanation of the pattern.

Note that we can see the rules as specifications, clearly understanding what you need for an object to be filtered in that function.

In official readings, examples tend to use methods (from object-oriented bias) that respect a single interface. The great idea in these contexts would be to link one method within the other to achieve the objective.

In our context, the manual passing of functions within each other is summarized by the filter.

In the function handleCustomerOrders [line 21] we have an implementation for the whole “sick” to work.

We convert the values of the handles object to an array of functions when we execute the Object.values method. After that, just hit the goal. With the some method, we check if any object that comes in the parameter meets any of the requirements.

Conclusion

Imagine that the rules were on top of a super complex object, with many child objects nested.

Or even in a larger context, where we could receive a request for a service, having to trigger several complex and different rules, which can be constantly evolving by the business team?

With the Chain of Responsibility we were able to leave these rules isolated from lower-level implementations, maintainable, unit-testable, and open to extension.

References

  • Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson Dr, and John Vlissides.

More content at plainenglish.io

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Published in JavaScript in Plain English

New JavaScript and Web Development content every day. Follow to join our 3.5M+ monthly readers.

Written by Pedro Morais

I write about Architecture and Software Engineering. https://github.com/pedromoraisf 🇧🇷

No responses yet

Write a response