MindView Inc.
[ Viewing Hints ] [ Revision History ] [ Report an Error ]
[ Free Newsletter ] [ Seminars ] [ Consulting ]

Thinking in Patterns with Java, Revision 0.3

©2000 by Bruce Eckel

[ Previous Chapter ] [ Short TOC ] [ Table of Contents ] [ Index ] [ Next Chapter ]

6: Function objects

In Advanced C++ (get full citation), Jim Coplien coins the term “functor” which is an object whose sole purpose is to encapsulate a function. The point is to decouple the choice of function to be called from the site where that function is called.

This term is mentioned but not used in Design Patterns. However, the theme of the functor is repeated in a number of patterns in that book.

Command

This is the functor in its purest sense: a method that’s an object[3]. By wrapping a method in an object, you can pass it to other methods or objects as a parameter, to tell them to perform this particular operation in the process of fulfilling your request.

//: c06:CommandPattern.java
import java.util.*;
import com.bruceeckel.test.*;

interface Command {
  void execute();
}

class Hello implements Command {
  public void execute() {
    System.out.print("Hello ");
  }
}

class World implements Command {
  public void execute() {
    System.out.print("World! ");
  }
}

class IAm implements Command {
  public void execute() {
    System.out.print("I'm the command pattern!");
  }
}

// A Command object that holds commands:
class Macro {
  private ArrayList commands = new ArrayList();
  public void add(Command c) { commands.add(c); }
  public void run() {
    Iterator it = commands.iterator();
    while(it.hasNext())
      ((Command)it.next()).execute();
  }
}

public class CommandPattern {
  public static class Test extends UnitTest {
    Macro macro = new Macro();
    public void test() {
      macro.add(new Hello());
      macro.add(new World());
      macro.add(new IAm());
      macro.run();
    }
  }
  public static void main(String args[]) {
    new Test().test();
  }
} ///:~

The primary point of Command is to allow you to hand a desired action to a method or object. In the above example, this provides a way to queue a set of actions to be performed collectively. In this case, it allows you to dynamically create new behavior, something you can normally only do by writing new code but in the above example could be done by interpreting a script (see the Interpreter pattern if what you need to do gets very complex).

Another example of Command is c12:DirList.java. The DirFilter class is the command object which contans its action in the method accept( ) that is passed to the list( ) method. The list( ) method determines what to include in its resut by calling accept( ).

Design Patterns says that “Commands are an object-oriented replacement for callbacks[4].” However, I think that the word “back” is an essental part of the concept of callbacks. That is, I think a callback actually reaches back to the creator of the callback. On the other hand, with a Command object you typically just create it and hand it to some method or object, and are not otherwise connected over time to the Command object. That’s my take on it, anyway. Later in this book, I combine a group of design patterns under the heading of “callbacks.”

Strategy

Strategy appears to be a family of Command classes, all inherited from the same base. But if you look at Command, you’ll see that it has the same structure: a hierarchy of functors. The difference is in the way this hierarchy is used. As seen in c12:DirList.java, you use Command to solve a particular problem—in that case, selecting files from a list. The “thing that stays the same” is the body of the method that’s being called, and the part that varies is isolated in the functor. I would hazard to say that Command provides flexibility while you’re writing the program, whereas Strategy’s flexibility is at run time. Nonetheless, it seems a rather fragile distinction.

Strategy also adds a “Context” which can be a surrogate class that controls the selection and use of the particular strategy object—just like State! Here’s what it looks like:

//: c06:StrategyPattern.java
import com.bruceeckel.util.*; // Arrays2.print()
import com.bruceeckel.test.*;

// The strategy interface:
interface FindMinima {
  // Line is a sequence of points:
  double[] algorithm(double[] line);
}

// The various strategies:
class LeastSquares implements FindMinima {
  public double[] algorithm(double[] line) {
    return new double[] { 1.1, 2.2 }; // Dummy
  }
}

class Perturbation implements FindMinima {
  public double[] algorithm(double[] line) {
    return new double[] { 3.3, 4.4 }; // Dummy
  }
}

class Bisection implements FindMinima {
  public double[] algorithm(double[] line) {
    return new double[] { 5.5, 6.6 }; // Dummy
  }
}

// The "Context" controls the strategy:
class MinimaSolver {
  private FindMinima strategy;
  public MinimaSolver(FindMinima strat) {
    strategy = strat;
  }
  double[] minima(double[] line) {
    return strategy.algorithm(line);
  }
  void changeAlgorithm(FindMinima newAlgorithm) {
    strategy = newAlgorithm;
  }
}

public class StrategyPattern {
  public static class Test extends UnitTest {
    MinimaSolver solver = 
      new MinimaSolver(new LeastSquares());
    double[] line = { 
      1.0, 2.0, 1.0, 2.0, -1.0, 
      3.0, 4.0, 5.0, 4.0 };
    public void test() {
      Arrays2.print(solver.minima(line));
      solver.changeAlgorithm(new Bisection());
      Arrays2.print(solver.minima(line));
    }
  }
  public static void main(String args[]) {
    new Test().test();
  }
} ///:~


Chain of responsibility

Chain of Responsibility might be thought of as a dynamic generalization of recursion using Strategy objects. You make a call, and each Strategy in a linked sequence tries to satisfy the call. The process ends when one of the strategies is successful or the chain ends. In recursion, one method calls itself over and over until a termination condition is reached; with Chain of Responsibility, a method calls the same base-class method (with different implementations) which calls another implementation of the base-class method, etc., until a termination condition is reached.

Instead of calling a single method to satisfy a request, multiple methods in the chain have a chance to satisfy the request, so it has the flavor of an expert system. Since the chain is effectively a linked list, it can be dynamically created, so you could also think of it as a more general, dynamically-built switch statement.

In StrategyPattern.java, above, what you probably want is to automatically find a solution. Chain of Responsibility provides a way to do this:

//: c06:ChainOfResponsibility.java
import com.bruceeckel.util.*; // Arrays2.print()
import com.bruceeckel.test.*;
import java.util.*;

class FindMinima {
  private FindMinima successor = null;
  public void add(FindMinima succ) {
    FindMinima end = this;
    while(end.successor != null)
      end = end.successor; // Traverse list
    end.successor = succ;
  }
  public double[] nextAlgorithm(double[] line) {
    if(successor != null)
      // Try the next one in the chain:
      return successor.algorithm(line);
    else 
      return new double[] {}; // Nothing found
  }
  public double[] algorithm(double[] line) {
    // FindMinima algorithm() is only the
    // start of the chain; doesn't actually try
    // to solve the problem:
    return nextAlgorithm(line);
  }
}

class LeastSquares extends FindMinima {
  public double[] algorithm(double[] line) {
    System.out.println("LeastSquares.algorithm");
    boolean weSucceed = false;
    if(weSucceed) // Actual test/calculation here
      return new double[] { 1.1, 2.2 }; // Dummy
    else // Try the next one in the chain:
      return nextAlgorithm(line);
  }
}

class Perturbation extends FindMinima {
  public double[] algorithm(double[] line) {
    System.out.println("Perturbation.algorithm");
    boolean weSucceed = false;
    if(weSucceed) // Actual test/calculation here
      return new double[] { 3.3, 4.4 }; // Dummy
    else // Try the next one in the chain:
      return nextAlgorithm(line);
  }
}

class Bisection extends FindMinima {
  public double[] algorithm(double[] line) {
    System.out.println("Bisection.algorithm");
    boolean weSucceed = true;
    if(weSucceed) // Actual test/calculation here
      return new double[] { 5.5, 6.6 }; // Dummy
    else
      return nextAlgorithm(line);
  }
}

// The "Handler" proxies to the first functor:
class MinimaSolver {
  private FindMinima chain = new FindMinima();
  void add(FindMinima newAlgorithm) {
    chain.add(newAlgorithm);
  }
  // Make the call to the top of the chain:
  double[] minima(double[] line) {
    return chain.algorithm(line);
  }
}

public class ChainOfResponsibility {
  public static class Test extends UnitTest {
    MinimaSolver solver = new MinimaSolver();
    double[] line = { 
      1.0, 2.0, 1.0, 2.0, -1.0, 
      3.0, 4.0, 5.0, 4.0 };
    public void test() {
      solver.add(new LeastSquares());
      solver.add(new Perturbation());
      solver.add(new Bisection());
      Arrays2.print(solver.minima(line));
    }
  }
  public static void main(String args[]) {
    new Test().test();
  }
} ///:~


Exercises

  1. Modify ChainOfResponsibility.java so that it uses an ArrayList to hold the different strategy objects. Use Iterators to keep track of the current item and to move to the next one. Does this implement the Chain of Responsibility according to GoF?
  2. Implement Chain of Responsibility to create an "expert system" that solves problems by successively trying one solution after another until one matches. You should be able to dynamically add solutions to the expert system. The test for solution should just be a string match, but when a solution fits, the expert system should return the appropriate type of ProblemSolver object. What other pattern/patterns show up

    [3] In the Python language, all functions are already objects and so the Command pattern is often redundant.

    [4] Page 235.

    here?

[ Previous Chapter ] [ Short TOC ] [ Table of Contents ] [ Index ] [ Next Chapter ]
Last Update:05/31/2000