Chapter 5. Behavioral Patterns

In the last chapter we looked at structural patterns that describe ways in which objects can be constructed to ease interaction.

In this chapter we'll take a look at the final, and largest, grouping of GoF patterns: behavioral patterns. These patterns are ones that provide guidance on how objects share data or, from a different perspective, how data flows between objects.

The patterns we'll look at are as follows:

  • Chain of responsibility
  • Command
  • Interpreter
  • Iterator
  • Mediator
  • Memento
  • Observer
  • State
  • Strategy
  • Template method
  • Visitor

Once again there are a number of more recently identified patterns that could well be classified as behavioral patterns. We'll defer looking at those until a later chapter, instead keeping to the GoF patterns.

Chain of responsibility

We can think of a function call on an object as sending that object a message. Indeed this message passing mentality is one that dates back to the days of Smalltalk. The chain of responsibility pattern describes an approach in which a message tickles down from one class to another. A class can either act on the message or allow it to be passed on to the next member of the chain. Depending on the implementation there are a few different rules that can be applied to the message passing. In some situations only the first matching link in the chain is permitted to act. In others, every matching link acts on the message. Sometimes the links are permitted to stop processing or even to mutate the message as it continues down the chain:

Chain of responsibility

Let's see if we can find a good example of this pattern in our go-to example: the land of Westeros.

Implementation

There is very little in the way of a legal system in Westeros. Certainly there are laws and even city guards who enforce them but the judicial system is scant. The law of the land is really decided by the king and his advisors. Those with the time and money can petition for an audience with the king who will listen to their complaint and pass a ruling. This ruling is law. Of course any king who spent his entire day listening to the complaints of peasants would go mad. For this reason many of the cases are caught and solved by his advisors before they reach his ears.

To represent this in code we'll need to start by thinking about how the chain of responsibility would work. A complaint comes in and it starts with the lowest possible person who can solve it. If that person cannot or will not solve the problem it tickles up to a more senior member of the ruling class. Eventually the problem reaches the king who is the final arbiter of disputes. We can think of him as the default dispute solver who is called upon when all else fails. The chain of responsibility is visible in the following diagram:

Implementation

We'll start with an interface to describe those who might listen to complaints:

export interface ComplaintListener{
  IsAbleToResolveComplaint(complaint: Complaint): boolean;
  ListenToComplaint(complaint: Complaint): string;
}

The interface requires two methods. The first is a simple check to see if the class is able to resolve a given complaint. The second listens to and resolves the complaint. Next we'll need to describe what constitutes a complaint:

var Complaint = (function () {
  function Complaint() {
    this.ComplainingParty = "";
    this.ComplaintAbout = "";
    this.Complaint = "";
  }
  return Complaint;
})();

Next we need a couple of different classes which implement ComplaintListener and are able to solve complaints:

class ClerkOfTheCourt {
  IsInterestedInComplaint(complaint) {
    //decide if this is a complaint which can be solved by the clerk
    if(isInterested())
      return true;
    return false;
  }
  ListenToComplaint(complaint) {
    //perform some operation
    //return solution to the complaint
    return "";
  }
}
JudicialSystem.ClerkOfTheCourt = ClerkOfTheCourt;
class King {
  IsInterestedInComplaint(complaint) {
    return true;//king is the final member in the chain so must return true
  }
  ListenToComplaint(complaint) {
    //perform some operation
    //return solution to the complaint
    return "";
  }
}
JudicialSystem.King = King;

Each one of these classes implements a different approach to solving the complaint. We need to chain them together making sure that the king is in the default position. This can be seen in this code:

class ComplaintResolver {
  constructor() {
    this.complaintListeners = new Array();
     this.complaintListeners.push(new ClerkOfTheCourt());
     this.complaintListeners.push(new King());
  }
  ResolveComplaint(complaint) {
    for (var i = 0; i < this.complaintListeners.length; i++) {
      if         (this.complaintListeners[i].IsInterestedInComplaint(complaint)) {
        return this.complaintListeners[i].ListenToComplaint(complaint);
      }
    }
  }
}

This code will work its way through each of the listeners until it finds one that is interested in hearing the complaint. In this version the result is returned immediately, halting any further processing. There are variations of this pattern in which multiple listeners could fire, even allowing the listeners to mutate the parameters for the next listener. The following diagram shows multiple listeners configured:

Implementation

Chain of responsibility is a highly useful pattern in JavaScript. In browser-based JavaScript the events that fire fall through a chain of responsibility. For instance you can attach multiple listeners to the click event on a link and each of them will fire and then, finally, the default navigation listener. It is likely that you're using chain of responsibility in much of your code without even knowing it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.107.85