August 1, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Pattern Summaries: Chain of Responsibility

  • August 7, 2003
  • By Mark Grand
  • Send Email »
  • More Articles »

Chain of Responsibility

Suppose that you are writing software to monitor a security system. Physically, the security system consists of sensing devices (motion detectors, smoke detectors...) that transmit status information to a computer. The computer's job is to log all status information, maintain a display showing current status information and transmit alarms in the event of an emergency.

One of the goals for the monitoring software is that it should be highly scalable. It should be able to work for a small retail store, an office building, a warehouse or a multi-building complex. That goal has implications for the way that you design the monitoring software.

To keep things simple, your monitoring program should instantiate an object for every sensor it is to monitor. This provides a simple way to model each sensor's state. To ensure scalability, an object responsible for an individual sensor should not assume anything about its environment, except that it is at the bottom level of a hierarchical organization.

The organization will include objects corresponding to such real world things such as rooms, areas, floors and buildings. Directly modeling the real world provides a straightforward way to display the status of different parts of buildings. It also allows the interpretation of a sensor's state to be based on its environment. For example, if the temperature of a closed room exceeds 180°F then you may want the fire sprinklers in just that room to turn on. If the temperature in an open area of a warehouse exceeds 150°F you may want to turn on the fire sprinklers over that area and the adjacent areas. On the other hand, if the temperature in a freezer exceeds 30°F, to may want to sound an alarm to let people know that that freezer is getting too warm.

In all these cases, the object that models a sensor does not decide what to do with the state of the sensor. Instead, it delegates that decision to an object at a higher level of the hierarchy that has more contextual knowledge. Such objects either decide what to do about a notification or pass it on to the object that is organizationally above it.

Figure 1 shows an example of  objects organized in this hierarchical way:



Figure 1. Physical Security Object Organization.

For example, when a TemperatureSensor object contained in an area of a warehouse receives a notification of the current temperature from the physical sensor, it passes that notification to the Area object that contains it. Rather than decide the significance of the temperature, it passes the notification to the Warehouse object that contains the Area object. The Warehouse object determines the meaning of the temperature. If the temperature is above 150°F, the Warehouse object decides that there is a fire. It turns on the sprinklers in the area that notified it and the surrounding areas. The Warehouse object does not pass on the temperature notification.

Figure 2 is a class diagram showing the general organization of the Chain of Responsibility pattern:



Figure 2. Chain of Responsibility Pattern.

Below are explanations of the roles these classes play in the Chain of Responsibility pattern:
 

CommandSender  Instances of a class in this role send commands to the first object in a chain of objects that may handle the command. It sends a command by calling the first CommandHandlerIF object's postCommand method.
CommandHandlerIF All objects in a chain of objects that may handle a command must implement an interface in this role. It defines two methods.
  • It defines a handleCommand method to handle whatever commands an implementing class is expected to handle. The handleCommand method returns true if it handled a command or false if it did not.
  • It defines a postCommand method that calls the handleCommand method. If the handleCommand method returns false and there is a next object in the chain, it calls that object's postCommand method. If the handleCommand method returns true, that means there is no need to pass the command on to the next object in the chain.
AbstractCommandHandler Classes in this role are abstract classes that implement the postCommand method. The purpose of this is to provide the convenience of a common implementation of postCommand for classes in the ConcreteCommandHandler role. It is very unusual for classes to want anything other than the default logic for the postCommand method. Classes in the CommandSender role should only refer to objects in a chain of responsibility through the CommandHandlerIF interface and not as instances of the AbstractCommandHandler class. The AbstractCommandHandler class is an implementation detail. Though unusual, it is possible to have classes that implement the CommandHandlerIF interface that are not subclasses of the AbstractCommandHandler class.
ConcreteCommandHandler1, ConcreteCommandHandler2 Instances of classes in this role are objects in a chain of objects that can handle commands.

It is common for a chain of CommandHandler objects to be part of a larger structure. This is the case in the example shown in Figure 1.

Now let's look at an example of a design than incorporates the Chain of Responsibility pattern. Figure 3 shows a class diagram based on the physical security example that was discussed at the beginning of this article.



Figure 3. Physical Security Classes.

In Figure 3, the classes that extend the Sensor class call the notify method they inherit from it to report a measurement to the object that is responsible for handling its measurements. Classes that extend the AbstractSecurityZone class are responsible for handling measurements from the appropriate kind of Sensor object.

Below is some code for the classes shown in Figure 3. Firstly, here is code for the TemperatureSensor class. Notice that the TemperatureSensor class does nothing with a reading from a temperature sensor, but pass it on.
 

class TemperatureSensor extends Sensor {
    private SecurityZone zone;
...
    /**
     * When the temperature sensor associated with this object observes a
     * different temperature this method is called.
     */
    void notify(int measurement) {
        zone.notify(measurement, this);
    } // notify(int)
} // class TemperatureSensor
Here is the code for the SecurityZone class, which is the superclass of all of the classes that form the chains of responsibility in this example:
 
abstract class SecurityZone {
    private SecurityZone parent;
...
    /**
     * Return this object's parent zone.
     */
    SecurityZone getParent() {
        return parent;
    } // getParent()

    /**
     * Call this method to notify this zone of a new sensor
     * measurement.
     */
    void notify(int measurement, Sensor sensor) {
        if (!handleNotification(measurement, sensor)
             && parent != null) {
            parent.notify(measurement, sensor);
        } // if
    } // notify(int, Sensor)

    /**
     * This method is called by the notify method so that
     * this object can have a chance to handle measurements.
     */
    abstract boolean handleNotification(int measurement,
                                        Sensor sensor);

    /**
     * This method is called by a child zone to report a fire.
     * It is expected that the child zone has turned on
     * sprinklers or taken other measures to control the fire
     * within the child zone. The purpose of this method is to
     * be overridden by subclasses so it can take any
     * necessary actions outside of the child zone.
     */
    void fireAlarm(SecurityZone zone) {
        // Turn on sprinklers
...
        if (parent != null)
          parent.fireAlarm(zone);
    } // fireAlarm(SecurityZone)
} // class SecurityZone
Here are the subclasses of SecurityZone that were discussed previously:
class Area extends SecurityZone {
...
    /**
     * This method is called by the notify method so that this
     * object can have a chance to handle measurements.
     */
    boolean handleNotification(int measurement, Sensor sensor) {
        if (sensor instanceof TemperatureSensor) {
            if (measurement > 150) {
                fireAlarm(this);
                return true;
            } // if
        } // if
...
        return false;
    } // handleNotification(int, Sensor)
} // class Area
class Warehouse extends SecurityZone {
    ...
    /**
     * This method is called by the notify method so that this
     * object can have a chance to handle measurements.
     */
    boolean handleNotification(int measurement, Sensor sensor) {
...
        return false;
    } // handleNotification(int, Sensor)
    void fireAlarm(SecurityZone zone) {
        if (zone instanceof Area) {
            // Turn on sprinklers in surrounding areas
            ...
            // Don't call super.fireAlarm because that will turn on the
            // sprinkler for the whole warehouse.
            if (getParent() != null)
              getParent().fireAlarm(zone);
            return;
        } // if
...
        super.fireAlarm(zone);
    } // fireAlarm(SecurityZone)
} // class Warehouse

About the author

Mark Grand is the author of a series of books titled Patterns in Java. He is currently the chief architect of an application framework for e-commerce called EBox. Mark Grand can be contacted at mgrand@mindspring.com.




Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel