Assumptions

What is a Chain of Responsibility?

A chain of responsibility in software development is a design pattern that allows dynamic processing of objects. Instead of using a series of if ... elif ... else statements, you can use a linked-list structure to pass data along to the next processing object in the chain. With this design, each object in the chain can perform it's own checks on the data, or even modify it before sending data along to the next object in the chain.

<div style="width:100%; margin:auto;text-align:center;"><img src="https://www.devmaking.com/img/topics/designpatterns/ChainOfResponsibilityPattern_01.png" alt="chain of responsibility UML diagram" style="max-width:95%;"> </div>

Key elements:

  • Handler: an interface that defines a standard request operation.
  • ConcreteHandler: an implementing class that maintains a reference to the next Handler in the chain.

One way to visualize the chain of responsibility is passing an unfamiliar problem up a corporate "chain" of command. If an employee faces a problem they don't know how to solve, they can call up to their manager for a solution. If the manager doesn't feel they can solve the problem, they go to their manager, and so on. This can potentially continue until the CEO hears of the problem!

Conceptualization

One place in the programming world that the chain of responsibility pattern might pop up is in an email priority system. When an email comes through the server, it will be checked for spammy content, then checked for if it's part of a mailing list, and so on until it reaches the normal mailbox.

This is a good place for dynamic processing because some user's might want to add additional mailboxes and filters!

<div style="width:100%; margin:auto;text-align:center;"><img src="https://www.devmaking.com/img/topics/designpatterns/ChainOfResponsibilityPattern_02.png" alt="email chain UML diagram" style="max-width:95%;"> </div>

Let's get started with our interface:

// chain interface:
interface EmailPriorityChain {
   // Set's the next object in the chain.
   void setNext(EmailPriorityChain next);
   // inspects the email, and might send it to the next filter.
   void inspect(EmailData email);
 }

Notice that we've defined a datatype here called EmailData. This data structure might look like this in a real implementation:

// Example class of a basic email data:
class EmailData {
   String from;
   String to;
   String title;
   String body;
 }

With our interface setup, we can start implementing our filters! The first that we'll define is our SpamFilter. This will do some of its own inspections of the email to determine if it's spam. If not, we should send it along to the next object in the chain:

class SpamFilter implements EmailPriorityChain {
   // Next reference:
   private EmailPriorityChain next;
   
    void setNext(EmailPriorityChain next) {
     this.next = next;
   }
   
   void inspect(EmailData email) {
     if(isSpam(email)) {
       // Send the mail to the spam mailbox.
     }
     else if(next is not null) {
         // Pass it along to the next priority filter:
         next.inspect(email);
     }
   }
   
   private boolean isSpam(EmailData email) {
     // Checks if the email is spam...
   }
 }

These days it seems as if we get subscribed to mailing lists without ever realizing. Assuming the email wasn't initially marked as spam, we can create a MailingListFilter to help keep our inbox nice and clean:

class MailingListFilter implements EmailPriorityChain {
   private EmailPriorityChain next;
   
    void setNext(EmailPriorityChain next) {
     this.next = next;
   }
   
   void inspect(EmailData email) {
     if(isFromMailingList(email)) {
       // Send the mail to the mailing list mailbox.
     }
     else if(next is not null) {
         // Pass it along to the next priority filter:
         next.inspect(email);
     }
   }
   
   private boolean isFromMailingList(EmailData email) {
     // Checks if the email is from a mailing list...
   }
 }

If the incoming mail passes these two filter tests, we can send it off to the inbox! Before that, though, what if we wanted one last filter to check if the mail should be considered important. For instance, maybe your boss emailed a reminder that halloween is not a costume occasion in the office; you wouldn't want to have to explain all day why you look like a :clown_face:! To avoid confusion, we'll want the email to show up in both the important mailbox and the regular mailbox:

class ImportantMailFilter implements EmailPriorityChain {
   private EmailPriorityChain next;
   
    void setNext(EmailPriorityChain next) {
     this.next = next;
   }
   
   void inspect(EmailData email) {
     if(isImportant(email)) {
       // Send the mail to the important mailbox AND the regular mailbox.
       next.inspect(email);
     }
     else if(next is not null) {
         // Pass it along to the next priority filter:
         next.inspect(email);
     }
   }
   
   private boolean isImportant(EmailData email) {
     // Checks if the email is important...
   }
 }

Finally, whether it is a real email or clever spam in disguise, we can send it off to the main inbox:

class RegularInbox implements EmailPriorityChain {
   private EmailPriorityChain next;
   
    void setNext(EmailPriorityChain next) {
     this.next = next;
   }
   
   void inspect(EmailData email) {
     // Add the email to the regular mailbox.  
   }
 }

With all of our classes setup, we can setup our email server to handle the incoming data:

static void main(String[] args) {
 EmailData email = new EmailData();
 email.from = args[0];
 email.to = args[1]; 
 /* and so on */
 
 // Instantiate our filters:
 EmailPriorityChain spam = new SpamFilter();
 EmailPriorityChain mailingList = new MailingListFilter();
 EmailPriorityChain important = new ImportantMailFilter();
 EmailPriorityChain inbox = new RegularInbox();
 
 // Set the chain of responsibility:
 spam.next = mailingList;
 mailingList.next = important;
 important.next = inbox;
 
 // Finally, inspect the email:
 spam.inspect(email);
 
 // Done!
} 

Chain of Responsibility vs a Decorator

There is sometimes confusion between the Chain of Responsibility and Decorator design patterns. Though they are very similar in structure, objects in a decorator list don't have control over whether or not to stop the chain early. In the Chain of Responsibility, as we've shown with our email filters, any object in the chain has the ability to choose whether or not to pass the object along to the next.