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.
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 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 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();
}
} ///:~
[3] In the
Python language, all functions are already objects and so the Command
pattern is often redundant.
[4] Page
235.