next up previous
Next: 1.9 Anonymous Classes Up: 1. From Scheme to Previous: 1.7.4 Interface Types

  
1.8 The Command Pattern

In a finger exercise in Section 1.5.3, we extended the DeptDirectory program by writing a method findPhone(String name) to look up a person's phone number. We implemented findPhone(String name) in exactly the same way as findAddress(String name), replicating method code. A better strategy would be to implement a method

Entry findEntry(String name)
that returns the Entry matching a given name, and then to define both findPhone and findAddress in terms of findEntry.

In this section, we will explore a far more general technique for eliminating code replication called the command pattern. To accommodate returning different Entry fields, we will define a method

String findField(EntryOperation f, String name)
that takes an EntryOperation object as an extra argument specifying which field to return. This approach mimics the familiar ``code factoring'' process in Scheme: repeated code patterns are abstracted into functions that take parameters that ``customize'' the code appropriately. In many cases, these parameters are functions.

Code factoring involving functions as parameters cannot be directly implemented in Java because methods are not values that can be passed as arguments. Some object oriented languages such as SmallTalk and Self classify methods as data values, permitting code factoring to be implemented directly. Fortunately, it is not difficult to get around this restriction by explicitly representing methods as objects. All we have to do is introduce an appropriate abstract class Operation containing a single abstract method execute( ... ) and define a separate concrete subclass of Operation for each method that we want to pass as an argument. Each concrete subclass defines the abstract method execute appropriately. In the general case, the Operation subclasses may contain fields that correspond to the free variables appearing in procedural arguments in Scheme. These free variables must be bound when the Operation is constructed, exactly as they are in a language supporting procedures as data values.

In the object-oriented design literature, this technique is called the command pattern in homage to the dominant role that imperative operations have played in object-oriented computation. Here we are using this pattern in a purely functional fashion.

To illustrate the command pattern, let us continue our DeptDirectory example. If we independently write findPhone and findAddress, they differ only in the field name used in the return expression.

class Empty extends DeptDirectory {

  ...

  String findAddress(String name) {
    return null;
  }
  String findPhone(String name) {
    return null;
  }
}

class Cons extends DeptDirectory {

  ...

  String findAddress(String name) {
    if (name.equals(first.name)) 
      return first.getAddress();
    else return rest.findAddress(name);
  }
  String findPhone(String name) {
    if (name.equals(first.name)) 
      return first.getPhone();
    else return rest.findPhone(name);
  }
}

We can ``abstract out'' this difference by writing a single findField method embodying the common code in the methods findPhone and findAddress. To accommodate differing choices for the returned Entry field, the method takes an EntryOperation that performs the appropriate field extraction on the matched Entry.

interface EntryOperation {
  String execute(Entry e);  // implicity public and abstract
}

class GetAddress {
  public String execute(Entry e) { return e.getAddress(); }
}

class GetPhone {
  public String execute(Entry e) { return e.getPhone(); }
}

abstract class DeptDirectory {
  ...
  abstract String findField(EntryOperation c, String name);
  String findAddress(String name) {
    return findField(new GetAddress(), name);
  }
  String findPhone(String name) {
    return findField(new GetPhone(), name);
}

class Empty extends DeptDirectory {
  ...
  String findField(EntryOperation c, String name) {
    return null;
  }
}

class Cons extends DeptDirectory {
  ...
  String findField(EntryOperation c, String name) {
    if (name.equals(first.name)) return c.execute(first);
    else return rest.findField(c,name);
  }
}

The preceding code implements both findAddress and findPhone by passing the appropriate EntryOperation object to findField.


Finger Exercises:

1.
Load the IntList class (without OrdList) into the definitions window of DrJava. Add an Operation interface that has a single method
int execute(int i);
2.
Add a map method to your IntList class that takes an Operation and applies it to each element of this to produce a new IntList with exactly the same number of elements as this. Save your program in a file IntListMap.java.
3.
Assume that a vector

< a0 , a1 , a2, ... , an >

is represented by the list

\begin{displaymath}{\tt\small (} a_0 \; a_1 \; a_2 \; ... \; a_n {\tt\small )}.
\end{displaymath}

where the coefficient's ai are objects of type Int. Add a method
int squareNorm()
to IntList taht computes the squred norm by this.v by squaring the elements and adding the squares together. You can compute the vector of squares using map and the define a method double sum() to compute the sum.
4.
Assume that a polynomial

a0 x + a1 x + a2 x2 + ... + an xn

is represented by the list

\begin{displaymath}(a_0 \; a_1 \; a_2 \; ... \; a_n)
\end{displaymath}

where the coefficient's ai are numbers of type int. Write a method
int eval(int x)
to evaluate the polynomial at coordinate x. Use Horner' rule asserting that

\begin{displaymath}a_0 + a_1 x + a_2 x^2 + ... + a_n x^n = \\
a_0 + x \cdot (a_1 + x \cdot (a_2 + ... x \cdot a_n))
\end{displaymath}

Remember to use the structural design pattern for processing lists (and by association polynomials as we have represented them).


next up previous
Next: 1.9 Anonymous Classes Up: 1. From Scheme to Previous: 1.7.4 Interface Types
Corky Cartwright
2001-08-02