Chapter 4

A Simple Payroll System (SPS)

The Company class is the main class of our simplified payroll system:

public class Company {
   private final int capacity = 100;
   private Employee[] staff = new Employee[capacity];
   private int staffSize = 0;
   public void hire(Employee e) {
      if (staffSize < capacity)
         staff[staffSize++] = e;
   }
   public void fire(Employee e) {
      // left as an exercise!
   }
   public void display() {
      for(int i = 0; i < staffSize; i++) {
         System.out.println("+++++++++++++++++");
         staff[i].display();
      }
   }
   // test harness
   public static void main(String[] args) {
      Company ibm = new Company();
      Employee emp;
      emp = new Employee("Bob Dobbs", 100000);
      ibm.hire(emp);
      emp = new Employee("Bill Ding", 75000);
      ibm.hire(emp);
      emp = new Employee("Gary Gray", 50000);
      ibm.hire(emp);
      ibm.display();
   }
}

An Employees encapsulate their names, salaries, and perhaps other personal data:

class Employee {
   private String name;
   private double salary;
   public Employee(String nm, double sal) {
      name = nm;
      salary = sal;
   }
   public void display() {
      System.out.println("Name = " + name);
      System.out.println("salary = $" + salary);
   }
}

Here is the program output:

+++++++++++++++++
Name = Bob Dobbs
salary = $100000.0
+++++++++++++++++
Name = Bill Ding
salary = $75000.0
+++++++++++++++++
Name = Gary Gray
salary = $50000.0

Constructors

The gap between object creation time and object initialization time is bug-infested. Programmers should always strive to initialize all instance variables at object creation time with an appropriate constructor. Accordingly, Our Employee class is provided with a constructor that initializes name and salary.

public Employee(String nm, double sal) {
   name = nm;
   salary = sal;
}

An odd thing happens when we do this. The default constructor disappears. We can no longer create objects as we have in previous chapters:

Employee emp = new Employee(); // error, no default constructor!

When the programmer provides his own constructor, it seems the default (i.e., parameterless) constructor provided by Java disappears!

Even if the programmer doesn't explicitly call a default constructor, there are places where a default constructor is implicitly called. For example, if we introduce a subclass of Employee, the default constructor of the subclass will implicitly call the default constructor of the super class:

class Programmer extends Employee { ... }
Programmer p = new Programmer(); // error, this calls Employee()

Overloading

This isn't a big problem, because Java methods can be overloaded. Overloading means several methods can share the same name as long as their parameter lists are different.

Overloading applies to constructors, as well. We might use this fact to equip our Employee class with several different methods for initializing Employee objects:

class Employee {
   private String name;
   private double salary;
   public Employee(String nm, double sal) {
      name = nm;
      salary = sal;
   }
   public Employee(String nm) {
      name = nm;
      salary = 0;
   }
   public Employee() {
      name = "unknown";
      salary = 0;
   }
   public void setName(String nm) { name = nm; }
   public void setSalary(double amt) { salary = amt; }
   // etc.
}

We now have several options for creating an initializing employees:

Employee emp = new Employee("Smith", 45000);
emp = new Employee("Jones"):
emp.setSalary(40000);
emp = new Employee();
emp.setName("Wong");
emp.setSalary(43000);

The Implicit Parameter

Let's take a closer look at Employee.display():

public void display() {
   System.out.println("Name = " + name);
   System.out.println("salary = $" + salary);
}

Notice that this method refers to the employee's name and salary. But how does the method know which employee's name and salary are being referred to? We can give a superficial answer to this question by pointing out that calls to this method are always qualified by the employee in question:

staff[i].display();

But how is this information passed to the Employee.display() method? The answer is that this information is passed by an invisible or implicit Employee reference parameter:

public void display(Employee this) {
   System.out.println("Name = " + this.name);
   System.out.println("salary = $" + this.salary);
}

We can understand the object qualifier that precedes calls to display() as an implicit argument with special syntax:

display(staff[i]); // this = staff[i]

This mechanism might not be of much interest to programmers except that the implicit parameter is always called this and is always available inside of every method. This provides programmers with a convenient way of referring to the implicit argument. For example, suppose the IRS provides us with a method that reports employees with unusually large incomes, and requires us to call it each time such an employee object is constructed:

class IRS {
   public static final double bigSalary = 100000;
   public static void report(Employee emp) {
      // audit this guy if 1040 too low
   }
}

In this case we need to modify our Employee constructor. But where will we get the reference to the employee required by IRS.report()? This is the perfect opportunity to use the implicit parameter:

public Employee(String nm, double sal) {
   name = nm;
   salary = sal;
   if (IRS.bigSalary <= salary)
      IRS.report(this); // sorry about that :(
}

Extending the Payroll System

Suppose version 2.0 of the Payroll System now differentiates between executives and secretaries. We can introduce two new subclasses of Employee. This will save us from needing to duplicate complex HR-related code that might be in the Employee super class:

The Executive class extends the Employee super class by adding a boolean flag indicating if recreational use of the company jet on weekends is permitted. Two references to the executive's one or two personal secretaries are also added. In addition, the Executive class redefines the display() method inherited from the Employee base class:

class Executive extends Employee {
   private boolean canUseJet = false;
   private Secretary sec1 = null, sec2 = null;
   public Executive(String nm, double sal, boolean jet) {
      super(nm, sal);
      canUseJet = jet;
   }
   public void setSecretary1(Secretary sec) {
      sec1 = sec;
      sec.setBoss(this);
   }
   public void display() {
      System.out.println("Executive");
      super.display();
      System.out.println("can use jet = " + canUseJet);
   }
}

The Secretary class extends the Employee super class by adding a field representing the secretary's typing speed in words per minute as well as a reference to the secretary's boss. In addition, the inherited display() method is redefined:

class Secretary extends Employee {
   private int wpm = 0; // typing speed in words/minute
   private Executive boss;
   public Secretary(String nm, double sal, int speed) {
      super(nm, sal);
      wpm = speed;
   }
   void setBoss(Executive exec) {
      boss = exec;
   }
   public void display() {
      System.out.println("Secretary");
      super.display();
      System.out.println("typing speed = " + wpm + "words/minute");
   }
}

Let's first look at the Secretary constructor. Note that a secretary object inherits name and salary attributes from the Employee super class:

Unfortunately, these fields were declared private, not protected, in the Employee super class. This means that although every secretary has a name and salary, the secretary cannot directly access these attributes. In particular, the Secretary constructor can't initialize these fields using ordinary assignment statements:

name = nm; // access denied
salary = sal; // access denied

Instead, the Secretary constructor must call the inherited Employee constructor to do the job. Every Java method has a second implicit parameter called super that is a reference to the object as an instance of the super class. I.e., something like:

super = (Employee)this;

A subclass constructor calls the desired super class constructor by placing the statement:

super(arg1, arg2, etc);

at the beginning of the constructor body. (Why not place:

Employee.Employee(nm, sal);

at the beginning of the Secretary constructor?)

We see this phenomenon again in the redefined display method. The author of the Secretary class was apparently unhappy with the inherited display() method. Perhaps it was too generic. That's fine. Programmers can always redefine inherited methods. Programmers can even have their cake and eat it too. In the case of the Secretary display method, the inherited display method is called as a subroutine. To avoid making Secretary.display() recursive, the call to the inherited display method is qualified by the implicit super reference:

public void display() {
   System.out.println("Secretary");
   super.display();
   System.out.println("typing speed = " + wpm + "words/minute");
}

executing the code:

Secretary smith = new Secretary("Smith", 30000, 60);
smith.display();

produces the output:

Secretary
Name = Smith
salary = $30000.0
typing speed = 60 words/minute

Polymorphism

A type system for a language L is a set of primitive types together with a set of rules for constructing, comparing, and naming types, as well as rules for binding types to the expressions and values of L. For example, the primitive types of Java include int, float, char, and boolean. Arrays and classes are examples of constructed types.

Instances of a monomorphic or uniform type all have the same representation, while instances of a polymorphic or multi-form type can have a variety of representations. For example, a monomorphic Real type might represent all real numbers using the IEEE floating point standard representation, but a polymorphic Complex type might represent some complex numbers using polar coordinate representation (reit) and others using rectangular coordinate representation (a+bi). A polymorphic type often can be viewed as a family of logically related subtypes.

All Java classes can be regarded as polymorphic types because instances of an extension can be treated as instances of the super class. For example, assume the following Shape class has been declared:

class Shape {
   public void draw(Graphics g) { /* no op */ }
   // etc.
}

Assume Triangle, Rectangle, and Circle extend the Shape class. For example:

class Circle extends Shape {
   public void draw(Graphics g) { g.drawOval(...); }
   // etc.
}

A reference to a Shape can actually refer to any instance of a Shape extension. For example:

Shape[] shapes = new Shape[3];
shapes[0] = new Circle();
shapes[1] = new Rectangle();
shapes[2] = new Triangle();

Even though the shape[i] reference is of type Shape, the actual object shape[i] refers to is a specific type of shape. The Java compiler doesn't decide which variant of draw() should be called. Instead, this decision is left to the Java virtual machine, which uses the type of the object, not the type of the reference to determine which draw() variant should be called. For example, the following statement draws a circle, rectangle, and triangle in the graphical context g:

Graphics g = myShapeWindow.getGraphics();
for(int i = 0; i < 3; i++) shape[i].draw(g);

Abstraction

Interfaces

When it is time to upgrade or replace a chip in a computer, the old chip is simply popped out of the motherboard, and the new chip is plugged in. It doesn't matter if the new chip and old chip have the same manufacturer or the same internal circuitry, as long as they both "look" the same to the motherboard and the other chips in the computer. The same is true for car, television, and sewing machine components. Open architecture systems and "Pluggable" components allow customers to shop around for cheap, third-party generic components, or expensive, third-party high-performance components.

An interface is a collection of operator specifications. An operator specification may include a name, return type, parameter list, exception list, pre-conditions, and post-conditions. A class implements an interface if it implements the specified operators. A software component is an object that is known to its clients only through the interfaces it implements. Often, the client of a component is called a container. If software components are analogous to pluggable computer chips, then containers are analogous to the motherboards that contain and connect these chips. For example, an electronic commerce server might be designed as a container that contains and connects pluggable inventory, billing, and shipping components. A control panel might be implemented as a container that contains and connects pluggable calculator, calendar, and address book components. Java Beans and ActiveX controls are familiar examples of software components.

Interfaces and Components in UML

Modelers can represent interfaces in UML class diagrams using class icons stereotyped as interfaces. The relationship between an interface and a class that realizes or implements it is indicated by a dashed generalization arrow:

Notice that the container doesn't know the type of components it uses. It only knows that its components realize or implement the IComponent interface.

For example, imagine that a pilot flies an aircraft by remote control from inside of a windowless hangar. The pilot holds a controller with three controls labeled: TAKEOFF, FLY, and LAND, but he has no idea what type of aircraft the controller controls. It could be an airplane, a blimp, a helicopter, perhaps it's a space ship. Although this scenario may sound implausible, the pilot's situation is analogous to the situation any container faces: it controls components blindly through interfaces, without knowing the types of the components. Here is the corresponding class diagram:

Notice that all three realizations of the Aircraft interface support additional operations: airplanes can bank, helicopters can hover, and blimps can deflate. However, the pilot doesn't get to call these functions. The pilot only knows about the operations that are specifically declared in the Aircraft interface.

We can create new interfaces from existing interfaces using generalization. For example, the Airliner interface specializes the Aircraft and (Passenger) Carrier interfaces. The PassengerPlane class implements the Airliner interface, which means that it must implement the operations specified in the Aircraft and Carrier interfaces as well. Fortunately, it inherits implementations of the Aircraft interface from its Airplane super-class:

An interface is closely related to the idea of an abstract data type (ADT). In addition to the operator prototypes, an ADT might also specify the pre- and post-conditions of these operators. For example, the pre-condition for the Aircraft interface's takeoff() operator might be that the aircraft's altitude and airspeed are zero, and the post-condition might be that the aircraft's altitude and airspeed are greater than zero.

Interfaces and Components in Java

Java allows programmers to explicitly declare interfaces:

interface Aircraft {
   public void takeoff();
   public void fly();
   public void land();
}

Notice that the interface declaration lacks private and protected members. There are no attributes, and no implementation information is provided.

A Pilot uses an Aircraft reference to control various types of aircraft:

class Pilot {
   private Aircraft myAircraft;
   public void fly() {
      myAircraft.takeoff();
      myAircraft.fly();
      myAircraft.land();
   }
   public void setAircraft(Aircraft a) {
      myAircraft = a;
   }
   // etc.
}

Java also allows programmers to explicitly declare that a class implements an interface:

class Airplane implements Aircraft {
   public void takeoff() { /* Airplane takeoff algorithm */ }
   public void fly() { /* Airplane fly algorithm */ }
   public void land() { /* Airplane land algorithm */ }
   public void bank(int degrees) { /* only airplanes can do this */ }
   // etc.
}

The following code shows how a pilot flies a blimp and a helicopter:

Pilot p = new Pilot("Charlie");
p.setAircraft(new Blimp());
p.fly(); // Charlie flies a blimp!
p.setAircraft(new Helicopter());
p.fly(); // now Charlie flies a helicopter!

It is important to realize that Aircraft is an interface, not a class. As such, it cannot be instantiated:

p.setAircraft(new Aircraft()); // error!

Java also allows programmers to create new interfaces from existing interfaces by extension:

interface Airliner extends Aircraft, Carrier {
   public void serveCocktails();
}

Although a Java class can only extend at most one class (multiple inheritance is forbidden in Java), a Java interface can extend multiple interfaces and a Java class can implement multiple interfaces. A Java class can even extend and implement at the same time:

class PassengerPlane extends Airplane implements Airliner {
   public void add(Passenger p) { ... }
   public void rem(Passenger p) { ... }
   public void serveCocktails() { ... }
   // etc.
}

Abstract Classes

Classes only inherit obligations from the interfaces they implement-obligations to implement the specified member functions. By contrast, an abstract class is a partially defined class. Classes derived from an abstract class inherit both features and obligations. For example, airplanes, blimps, and helicopters all have altitude and speed attributes. Why not declare these attributes, as well as their attending getter and setter functions, in the Aircraft base class:

abstract public class Aircraft {
   protected double altitude = 0, speed = 0;
   public Aircraft(double a, double s) {
      altitude = a;
      speed = s;
   }
   public Aircraft() {
      altitude = speed = 0;
   }
   public double getSpeed() { return speed; }
   public double getAltitude() { return altitude; }
   public void setSpeed(double s) { speed = s; }
   public double setAltitude(double a) { altitude = a; }
   abstract public void takeoff();
   abstract public void fly();
   abstract public void land();
}

Aircraft is no longer an interface, because it contains fields and implemented methods. But takeoff(), fly(), land() are abstract methods, which means users still won't be allowed to instantiate the Aircraft class.

Names of abstract classes and virtual functions are italicized in UML:

 

Polymorphism in the Payroll System

Let's return to the Simple payroll System described earlier. Recall that the Company class maintained an array of Employee references and a display method that traversed the array calling the display() method of each employee:

public class Company {
   private final int capacity = 100;
   private Employee[] staff = new Employee[capacity];
   private int staffSize = 0;
   public void hire(Employee e) {
      if (staffSize < capacity)
         staff[staffSize++] = e;
   }
   public void display() {
      for(int i = 0; i < staffSize; i++) {
         System.out.println("+++++++++++++++++");
         staff[i].display();
      }
   }
   // etc.
}

Also recall that Executive and Secretary were introduced as subclasses of Employee. Each class redefined the inherited display() method:

Let's alter the Company test harness so that Executives and Secretaries are hired instead of generic Employees:

public static void main(String[] args) {
   Company ibm = new Company();
   Employee emp;
   emp = new Executive("Bob Dobbs", 100000, true);
   ibm.hire(emp);
   emp = new Secretary("Bill Ding", 75000, 60);
   ibm.hire(emp);
   emp = new Secretary("Gary Gray", 50000, 30);
   ibm.hire(emp);
   ibm.display();
}

Thanks to polymorphism, staff[i].display() calls the redefined Secretary and Executive display methods:

+++++++++++++++++
Executive
Name = Bob Dobbs
salary = $100000.0
can use jet = true
+++++++++++++++++
Secretary
Name = Bill Ding
salary = $75000.0
typing speed = 60 words/minute
+++++++++++++++++
Secretary
Name = Gary Gray
salary = $50000.0
typing speed = 30 words/minute

Making Employee Abstract

Let's add a method to each Employee subclass that returns a string describing the employee's position:

class Executive extends Employee {
   public String getPosition() { return "Executive"; }
   // etc.
}

class Secretary extends Employee {
   public String getPosition() { return "Secretary"; }
   // etc.
}

This method might be called by a countStaff() method in the Company class:

public class Company {
   public int countStaff(String position) {
      int count = 0;
      for(int i = 0; i < staffSize; i++) {
         if (position.equals(staff[i].getPosition())) {
            count++;
         }
      }
      return count;
   }
   // test harness
   public static void main(String[] args) {
      Company ibm = new Company();
      Employee emp;
      emp = new Executive("Bob Dobbs", 100000, true);
      ibm.hire(emp);
      emp = new Secretary("Bill Ding", 75000, 60);
      ibm.hire(emp);
      emp = new Secretary("Gary Gray", 50000, 30);
      ibm.hire(emp);
      System.out.println(
         "# of secretaries = " + ibm.countStaff("Secretary"));
   }
   // etc.
}

Unfortunately, the Company class won't compile, because there is no position() method in the Employee base class. Adding such a position raises what at first seems like a philosophical position: what is the position of a generic employee? We could simply add a method to the Employee super class that returns a bogus value:

class Employee {
   public String getPosition() { return "generic"; }
   // etc.
}

Defining a method we hope never gets called is risky. Suppose a newly hired programmer is asked to extend the payroll system by adding a new subclass of Employee:

cloass GalleySlave extends Employee { ... }

Unfortunately, the novice programmer forgets to add the required getPosition() method. There is no compiler or runtime error here. Instead, a logic error results when we attempt to find the number of galley slaves working for IBM:

ibm.countStaff("GalleySlave"); // returns 0!

Of course the right solution is to make getPosition() and abstract method in the Employee class:

abstract class Employee {
   public abstract String getPosition();
   // etc.
}

Not providing an implementation of getPosition() in the GalleySlave class makes GalleySlave an abstract class. This results in a compile-time error when someone attempts to instantiate the GalleySlave class.

Control-Driven versus Data-Driven Programming

A devoted C programmer might offer the following objection at this point. Why bother with all of this fancy stuff. Why not simply add a type tag to the Employee class:

class Employee {
   protected String position;
   public String getPosition() { return position; }
   // etc.
}

Any method that needs to process the staff array can use a multi-way conditional to determine the actual type of the staff member and then performs an explicit downcast:

if (staff[i].getPosition().equals("GalleySlave")) {
   ((GalleySlave)staff[i]).display();
} else if (staff[i].getPosition().equals("Secretary")) {
   ((Secretary)staff[i]).display();
} else if (staff[i].getPosition().equals("Executive")) {
   ((Executive)staff[i]).display();
} etc.

Of course this solution doesn't make much sense in Java, where all methods are polymorphic by default, but C++ methods are monomorphic by default, so this type of a statement would make sense and would also improve efficiency, because the method called is determined in advance by the compiler rather than at runtime using a lookup table. But the problem is this: how many places in our clients code does this or a similar multi-way conditional exist? If we add a new subclass of Employee:

class FortuneTeller extends Employee { ... }

then we must locate every single multi-way conditional in the client's code and add a new clause.

} else if (staff[i].getPosition().equals("FortuneTeller")) {
   ((Executive)staff[i]).display(); // or something
}

If we miss one of these statements, then the client's code, which was tested bug-free yesterday, now has developed a bug!

Multi-way conditionals like the one above and like the notorious switch statement have become the symbol of the disease cured by the data-driven paradigm in much the same way as the goto statement became the symbol of the disease cured by the structured programming movement.

The data-driven paradigm is the pre-cursor to the object-oriented paradigm, which held that programmers should be in the business of specifying data rather than the flow of control. Thus, the flow of control that results from calling a polymorphic method like:

staff[i].display();

is determined by the object referenced by staff[i] rather than by a complex multiplexer inside of a single, monomorphic display method.

Frameworks

MFW: A Framework for Music Applications

Assume we are developing a music framework called MFW. MFW is a horizontal framework that can be customized into various musical applications such as score editors, virtual recording studios, virtual instruments, expert systems for composers, and interfaces to computer controlled instruments such as synthesizers.

One part of MFW defines various ensembles of musical instruments: bands, orchestras, trios, quartets, etc. Unfortunately, the types of musical instruments will be defined much later in the various customizations of the framework. How can MFW form ensembles without knowing the identity of their constituent instruments? There are two solutions: make Instrument an abstract class in MFW, or make Instrument an MFW interface.

Although the specific types of instruments may be unknown, MFW can define an Instrument interface:

interface Instrument {
   public void play();
}

MFW is unable to implement the play() member function, so it is declared as a pure virtual function. Implementation class programmers will be required to provide implementations. Even though MFW couldn't implement play(), it can still call play(). For example, here is the MFW definition of Trio. Notice that Trio.play() calls the play() method of each instrument:

class Trio {
   Instrument first, second, third;
   Trio(Instrument a, Instrument b, Instrument c) {
      first = a;
      second = b;
      third = c;
   }
   public void play() {
      if (first != null) first.play();
      if (second != null) second.play();
      if (third != null) third.play();
   }
}

Suppose a programmer customizing MFW wishes to create and play trios consisting of different combinations of horns, harps, and drums. The first step is to create Instrument implementation classes. For example:

class Harp implements Instrument {
   public void play() {
      System.out.println("plink, plink, plink");
   }
}

Here is how trios are created and played in the customization:

Trio t1 = new Trio(new Harp(), new Horn(), new Drum());
Trio t2 = new Trio(new Drum(), new Drum(), new Harp());
t1.play();
t2.play();

Heterogeneous Collections

A container or collection is an object that stores other objects. Stacks and queues are familiar examples of containers. A heterogeneous container stores objects of different types. For example, an orchestra can be regarded as a container of different types of instruments:

class Orchestra {
   private Instrument[] instruments = new Instrument[100];
   private int size = 0;
   public void add(Instrument i) throws MFWError {
      if (100 <= size) throw new MFWError("orchestra full");
      instruments[size++] = i;
   }
   public void play() {
      for(int i = 0; i < size; i++)
         instruments[i].play();
   }
}

Clients of the orchestra class can add a variety of instruments to orchestra instances:

Orchestra orch = new Orchestra();
orch.add(new Horn());
orch.add(new Harp());
orch.add(new Horn());
orch.add(new Harp());
orch.add(new Drum());
orch.play();

Virtual Factory Methods

Let's enhance MFW by adding an abstract Note class. Each note encapsulates a frequency and a duration:

abstract class Note {
   protected double freq; // in Hz
   protected double duration; // in mSec
   public Note(double f, double d) { freq = f; duration = d; }
   public abstract void play(); // quality? timbre?
}

Of course any musician will tell you that a note played on a guitar sounds quite different from the same note played on a horn. For this reason we have left the implementation of play() to MFW customizations; for example:

class HornNote extends Note {
   public HornNote(double f, double d) { super(f, d); }
   public void play() {
      System.out.println("honk");
   }
}

Now that we can talk about notes in MFW perhaps we can replace the Instrument interface by a concrete class:

class Instrument {
   public void play() {
      for(int i = 0; i < 3; i++) {
         Note note = makeNote();
         note.play();
      }
   }
}

But how can the Instrument class know what type of Notes to create? It would seem that creating an unknown type of object is more difficult than simply using an unknown type of object. How much memory should be allocated? How should the memory be initialized? Creating unknown objects is a common theme in programming. The various approaches to this problem are summarized by the Factory Method design pattern:

Factory Method [Go4]

Other Names

Virtual constructor.

Problem

A "factory" class can't anticipate the type of "product" objects it must create.

Solution

Provide the factory class with an ordinary member function that creates product objects. This is called a factory method. The factory method can be a virtual function implemented in a derived class, or a template function parameterized by a product constructor.

There are three variations of the pattern: virtual, smart, and template factory methods. Template factory methods are unavailable in Java, while smart factory methods will be discussed in Chapter ?.

An ordinary member function that creates and returns new objects is called a factory method. Although Java doesn't allow abstract constructors, factory methods can be abstract:

abstract class Instrument {
   public void play() {
      for(int i = 0; i < 3; i++) {
         Note note = makeNote();
         note.play();
      }
   }
   abstract public Note makeNote();
}

The obligation to implement the virtual factory method falls on the shoulders of extension classes:

class Horn extends Instrument {
   public Note makeNote() {
      return new HornNote(100, 100);
   }
}

One problem associated with virtual factory methods is that programmers must maintain two parallel inheritance hierarchies, one for factories, and one for products:

If a programmer creates a new type of note, he must remember to create the corresponding factory instrument that creates the note.

Factories in Java

Inner Classes

Java allows programmers to declare products to be inner classes of factories:

abstract class Instrument {

   abstract class Note {
      protected double freq; // in Hz
      protected double duration; // in mSec
      public Note(double f, double d) { freq = f; duration = d; }
      public abstract void play(); // quality? timbre?
   }
   // virtual factory method:
   abstract public Note makeNote();

   public void play() {
      for(int i = 0; i < 3; i++) {
         Note note = makeNote();
         note.play();
      }
   }
}

Declaring HornNote to be an inner class of Horn will discourage harps and drums from making horn notes:

class Horn extends Instrument {

   class HornNote extends Note {
      public HornNote(double f, double d) { super(f, d); }
      public void play() {
         System.out.println("honk");
      }
   }
   
   public Note makeNote() {
      return new HornNote(100, 100);
   }
}

Unlike C++, inner class Java objects are always created by outer class factories, but the syntax is weird:

Horn factory = new Horn();
Instrument.Note product = factory. new HornNote(100, 100);
product.play();

Of course this technique for constructing notes fails if HornNote is declared as a private inner class. In this case clients are forced to use our makeNote() factory method.

Inner class objects can access the public, protected, and private fields and methods of their factories, but the syntax is weird:

class Horn extends Instrument {

   class HornNote extends Note {
      public HornNote(double f, double d) { super(f, d); }
      public void play() {
         System.out.print("my instrument = ");
         Horn.this.show();
         System.out.println("honk");
      }
   }

   private void show() { System.out.println("Horn"); }
}

Note: this isn't true if the inner class is declared static.

UML doesn't have a notation for inner classes; we can use a stereotyped association:

Local Classes

Classes can even be defined inside of a method:

class Horn extends Instrument {
   
   public Note makeNote() {
      class HornNote extends Note {
         public HornNote(double f, double d) { super(f, d); }
         public void play() {
            System.out.println("honk");
         }
      }
      return new HornNote(100, 100);
   }
}

Anonymous Classes

We don't even have to name classes:

class Horn extends Instrument {

   public Note makeNote() {
      return new Note(100, 100) {
         public void play() {
            System.out.println("honk");
         }
      }
;
   }
}

The general syntax is:

new SuperType(constructor parameters) {
   inner class methods and data
}

Closures, Functors, and Thunks

A functor (also called functional or function object) is an object that behaves like a method. A thunk (also called a promise) is a special type of functor. It's special because a thunk invocation can be frozen (delayed). Later, the frozen thunk can be thawed (forced), causing it to compute and return a value. The interesting thing is that a thunk remembers its freezing environment, which it uses to produce its value when thawed, even if the thawing environment is different. (Thunks are closely related to closures. A closure is a method that remembers its defining environment.)

Every thunk must implement a thaw() method that returns the computed value as an Object:

interface Thunk {
   public Object thaw();
}

In this demo we want to convert the method ThunkDemo.foo() into a thunk. The foo() method simply computes the sum of two parameters and a local variable, but in reality, when called, foo() creates and returns an anonymous thunk that computes this value when thawed:

class ThunkDemo {

   public Thunk foo(final int x, final int y) {
      final int z = 10;
      return new Thunk() {
         public Object thaw() {
            return new Integer(x + y + z);
         }
      }
;
   }

The test driver defines a couple of local variables, a = 3 and b = 4, then freezes a call to foo(a, b). Much later, the frozen call is thawed and produces the value 3 + 4 + 10 = 17, even though the values of a and b have been changed!

   public static void main(String[] args) {
      ThunkDemo demo = new ThunkDemo();
      int a = 3, b = 4;
      Thunk thunk = demo.foo(a, b); // freeze demo.foo(a, b) call
      a = 12; b = 15; // a and b change
      System.out.println("tic toc tic toc ..."); // much time passes
      System.out.println("result = " + (Integer)thunk.thaw()); // = 17
   }
}

A memoization thunk caches its computed result so subsequent thaws don't need to re-compute anything. In this case Thunk becomes an abstract base class that provides the cache:

abstract class Thunk {
   protected Object cache = null;
   abstract public Object thaw();
}

public class ThunkDemo {

   public Thunk foo(final int x, final int y) {
      final int z = 10;
      return new Thunk() {
         public Object thaw() {
            if (cache == null) // only computed once:
               cache = new Integer(x + y + z);
            return cache;
         }
      };
   }

In this version of the test harness the thunk is thawed twice. The first time it computes the sum, the second time it simply returns the cached value:

   public static void main(String[] args) {
      ThunkDemo demo = new ThunkDemo();
      int a = 3, b = 4;
      Thunk thunk = demo.foo(a, b); // freeze demo.foo(a, b) call
      a = 12; b = 15; // a and b change
      System.out.println("tic toc tic toc ..."); // much time passes
      System.out.println("result = " + (Integer)thunk.thaw()); // = 17
      a = 100; b = -40; // a and b change again
      System.out.println("tic toc tic toc ..."); // more time passes
      System.out.println("result = " + (Integer)thunk.thaw()); // = 17
   }
}

Clearly this is an advantage if computing the value requires a lot of time.

Thunks are useful for representing streams. A stream is a potentially infinite sequence:

(2 3 5 7 11 13 17 19 ... )

A stream is represented as a pair:

class Stream {
   private Object car;
   private Thunk cdr;
   public Stream(Object a, Thunk b) {
      car = a;
      cdr = b;
   }
   public Object head() { return car; }
   public Stream tail() {
      return new Stream(cdr.thaw(), cdr);
   }
}

In the following demo, the tail of a stream representing the infinite sequence of integers always produces the next integer by incrementing the cache:

public class StreamDemo {

   public Thunk next() {
      final int x = 0;
      return new Thunk() {
         public Object thaw() {
            if (cache == null)
               cache = new Integer(0);
            else
               cache = new Integer(((Integer)cache).intValue() + 1);
            return cache;
         }
      };
   }

Here is a test harness that prints out the first 20 values of a potentially infinite stream, s:

   public static void main(String[] args) {
      StreamDemo demo = new StreamDemo();
      Stream s = new Stream(new Integer(0), demo.next());
      System.out.print('(');
      for(int i = 0; i < 20; i++) {
         System.out.print("" + (Integer)s.head() + ' ');
         s = s.tail();
      }
      System.out.println(')');
   }
}

Here's the program output:

(0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18)

Note: I'm not sure this example is very interesting, because one clould more easily accomplish the same thing in Java without using thunks.

Power Types as Factories

Meta-classes and power-classes allow modelers to separate class from type. Instead of representing types implicitly as classes with no runtime existence, we can represent types as objects that instantiate meta or power classes.

For example, there are many types of aircraft: airplanes, blimps. helicopters, space ships, etc. We could represent this situation by introducing blimp, helicopter, and airplane as subclasses of an aircraft base class:

Alternatively, we could define a single Aircraft class and represent the different types of aircraft by different instances of an AircraftType power class. In this case it's common for instances of the power class to act as a factories that create instances of the type of objects they represent, the same way a class would provide a constructor for creating instances:

How can we insure that users won't bypass the factory and directly create aircraft? This can be done in Java by defining Aircraft to be a private inner class of AircraftType. We begin by introducing an aircraft interface:

interface IAircraft {
   public AircraftType getType();
   public void takeoff();
   public void fly();
   public void land();
}

Next, we define the AircraftType class with a private inner class and a factory method:

class AircraftType {

   private String name;
   public AircraftType(String n) { name = n; }
   public String toString() { return name; }

   // factory method:
   public IAircraft makeAircraft() {
      return new Aircraft();
   }

   private class Aircraft implements IAircraft {
      public void takeoff() {
         System.out.println("An aircraft is taking off");
      }
      public void fly() {
         System.out.println("An aircraft is flying");
      }
      public void land() {
         System.out.println("An aircraft is landing");
      }
      public AircraftType getType() {
         return AircraftType.this;
      }
   } // Aircraft

} // AircraftType

Here is how a client might create an aircraft:

public class TheApp {
   public static void main(String[] args) {
      AircraftType blimp = new AircraftType("Blimp");
      AircraftType helicopter = new AircraftType("Helicopter");
      //IAircraft a = blimp.new Aircraft(); // ok if inner class is public
      IAircraft a = blimp.makeAircraft();
      a.takeoff();
      a.fly();
      a.land();
      System.out.println("a.getType() = "+ a.getType()); //prints Blimp
   }
}

Of course reflection is built into Java, so we can use the Java meta classes Class, Field, Method, etc. without writing extra code. For example, let's drop the getType() function from the IAircraft interface:

interface IAircraft {
   // public AircraftType getType();
   public void takeoff();
   public void fly();
   public void land();
}

Assume Helicopter and SpaceShip implement this interface:

class SpaceShip implements IntfAircraft {
   public void takeoff() {
      System.out.println("A spaceship is taking off");
   }
   public void fly() {
         System.out.println("A spaceship is flying");
   }
   public void land() {
         System.out.println("A spaceship is landing");
   }
}

Here is how a client might build and takeoff in an aircraft without knowing the type of aircraft:

import java.lang.reflect.*;

public class Test {
   public static void main(String[] args) {
      try {
          String className = "SpaceShip";
          Class c = Class.forName(className);
          IntfAircraft a = (IntfAircraft)c.newInstance();
          Method[] meths = c.getMethods();
          for(int i = 0; i < meths.length; i++)
            if (meths[i].getName().equals("takeoff"))
               meths[i].invoke(a, null);
      }
      catch(Exception e) {
         System.err.println("error = " + e);
      }
   }
}

Example: Unit as a Power Type

public class Quantity {

   private double amount;
   private Unit unit;
   
   Quantity(double amt, Unit u) {
      unit = u;
      amount = amt;
   }
   Quantity(Quantity q, Unit u) throws NoConversion {
      unit = u;
      amount = (u.makeQuantity(q)).getAmount();
   }
   public Unit getUnit() { return unit; }
   public double getAmount() { return amount; }
   public String toString() {
      return "" + amount + " " + unit + "(s)";
   }
   
   public boolean equals(Quantity q) {
      return amount == q.amount && unit.equals(q.unit);
   }
   
   public Quantity add(Quantity q) throws UnitMismatch {
      if (!unit.equals(q.getUnit()))
         throw new UnitMismatch();
      return unit.makeQuantity(amount + q.amount);
   }
   
   // etc.


   
   public static void main(String[] args) {
      try {
         Unit meter = new Unit("meter");
         Unit feet = new Unit("foot");
         meter.put(feet, 3);
         Quantity q1 = meter.makeQuantity(3);
         Quantity q2 = feet.makeQuantity(4);
         Quantity q3 = meter.makeQuantity(6.3);
         Quantity q5 = q1.add(q3);
         System.out.println("q5 = " + q5);
         Quantity q6;
         // q6 = q1.add(q2); // throws exp
         q6 = q1.add(q1.getUnit().makeQuantity(q2));
         System.out.println("q6 = " + q6);
         q6 = q2.add(q2.getUnit().makeQuantity(q1));
         System.out.println("q6 = " + q6);
      } catch (UnitMismatch u) {
         System.err.println("unit mismatch!");
      } catch (NoConversion u) {
         System.err.println("no conversion!");
      }
   }
}

class UnitMismatch extends Exception {

}

class NoConversion extends Exception {

}

class Unit {
   private String name;
   private Map conversions = new Hashtable(); // or HashMap()
   
   public void put(Unit unit, double factor) {
      conversions.put(unit, new Double(factor));
      unit.conversions.put(this, new Double(1/factor));
   }
   
   public double get(Unit unit) throws NoConversion {
      Object obj = conversions.get(unit);
      if (obj == null) throw new NoConversion();
      return ((Double) obj).doubleValue();
   }   
   
   // factory methods (not necessary):
   
   public Quantity makeQuantity(double amt) {
      return new Quantity(amt, this);
   }
   
   public Quantity makeQuantity(Quantity q) throws NoConversion {
      Unit u = q.getUnit();
      if (u.equals(this)) return q;
      double factor = get(u);
      double amt = q.getAmount();
      return makeQuantity(factor * amt);
   }
      
   
   public Quantity makeQuantity(Quantity q, double factor) {
      Unit u = q.getUnit();
      if (u.equals(this)) return q;
      double amt = q.getAmount();
      return makeQuantity(factor * amt);
   }
      
   public String toString() { return name; }
   public boolean equals(Unit u) {
      return name.equals(u.name);
   }
   public Unit(String nm) { name = nm; }
}

Generic Methods

Sometimes the general sequence of tasks a function must perform is the same across a variety of applications, suggesting that the function belongs in a common framework. However, the tasks performed by the function are application-specific, suggesting the function belongs in the various framework customizations.

The generic algorithm design pattern solves this problem. Move the function into the framework and declare the application-specific tasks to be pure virtual functions in the framework. A function like this is called a generic algorithm or a template method.

Generic Algorithm [Go4], [ROG]

Other Names

Template method

Problem

Parts of an algorithm are invariant across a family of framework customizations, while other parts are not.

Solution

Move the algorithm into the framework. Replace the non-invariant parts by calls to virtual functions that can be implemented differently in different customizations.

Generic algorithms follow the inverted control structure sometimes called the Hollywood principle: "Don't call us, we'll call you."

Let's start with a trivial example to clarify the idea; more serious examples will come later. Suppose we want to add a symphony class to our music framework, MFW. Symphonies are usually divided into four movements, so playing a symphony is easy: play the first movement, then the second, then the third, finally the fourth. Of course the similarity between symphonies ends here. The particulars of each movement vary from one symphony to the next, and will have to be specified in various customizations of the framework. We can still add an abstract symphony base class to our framework, but play() will have to be a generic algorithm:

abstract class Symphony {
   public void play() { // a generic algorithm
      doFirstMovement();
      doSecondMovement();
      doThirdMovement();
      doFourthMovement();
   }
   protected abstract virtual void doFirstMovement() = 0;
   protected abstract virtual void doSecondMovement() = 0;
   protected abstract virtual void doThirdMovement() = 0;
   protected abstract virtual void doFourthMovement() = 0;
}

Framework customizers will have to subclass Symphony and implement the do-functions. For example:

class TheFifth extends Symphony {
   protected void doFirstMovement() {
      System.out.println("dah, dah, dah, duh ...");
   }
   void doSecondMovement() {
      System.out.println("duh, duh, duh, dah ...");
   }
   void doThirdMovement() {
      System.out.println("dah, duh, duh, dah ...");
   }
   void doFourthMovement() {
      System.out.println("duh, dah, dah, duh ...");
   }
}

Singletons

Sometimes we want a class to have at most one instance. Why? Multiple instances might lead to confusion. For example, it would be confusing if an application had multiple user interfaces, simultaneously:

GUI gui1, gui2; // which one to use?

Multiple instances might lead to sharing problems. For example, changes to one instance of a company's budget must be reflected in other instances:

Budget budget1, budget2; // must remember to keep synchronized

In some situations, multiple instances might simply be illogical:

TheFifth s1, s2, s3; // how many Fifth Symphonies did he write?

The Singleton pattern solves this problem by making all constructors private. A public factory method is provided that allows users to create instances, but the factory method always returns pointers to the same hidden instance:

Singleton [Go4]

Problem

There must be at most one instance of a class.

Solution

Make all constructors private, including the default and copy constructors. Provide a static function that initializes and returns a private, static pointer to the sole instance of the class.

But doesn't this create a chicken and egg problem? How do we get the first instance of the class from which we can invoke the factory method:

y = x.makeSingleton(); // where does x come from?

This problem is solved by making the factory method static:

y = Singleton.makeSingleton(); // no implicit parameter needed

For example, let's apply the singleton pattern to the TheFifth class from the previous example:

class TheFifth extends Symphony {
   public static TheFifth makeTheFifth() {
      if (theFifth == null)
         theFifth = new TheFifth(); // constructor access ok here
      return theFifth;
   }
   // etc.
private static TheFifth theFifth; // the one and only
private TheFifth() {} // hide default constructor
}

Notice that the default constructor is private. The reference to the one and only instance is declared to be a private static variable.

Unsuspecting clients can call the factory method as many times as they want, but only one instances is ever created:

TheFifth s1 = TheFifth.makeTheFifth();
TheFifth s2 = TheFifth.makeTheFifth();
TheFifth s3 = TheFifth.makeTheFifth();
s1.play();
s2.play();
s3.play();

Any attempt to create instances using the new operator generates an error message:

s1 = new TheFifth(); // compile-time error

The Java Collections Framework

The following class diagram shows many of the interfaces and implementation classes in the new Java Collections Framework (located in java.util):

Basically, a set is an unordered collection with no duplicate elements, while a list is an ordered collection with duplicate elements allowed. A map is a table or dictionary. Readers should consult the online Java Tutorial for more details.

Assume a many-to-one relationship between an Airplane class and a Fleet class:

We could provide a Java Fleet with a set of Airplane references:

class Fleet {
   private Set members = new HashSet();
   public void add(Airplane a) { members.add(a); }
   public void rem(Airplane a) { members.remove(a); }
   public Iterator iterator() { return members.iterator(); }
}

A Java iterator is initialized so that it points to a "location" before the first element of its implicit argument, and provides methods for determining if there is a next element and for moving to and accessing the next element. For example, assume a fleet of three airplanes is created:

Fleet united = new Fleet();
united.add(new Airplane());
united.add(new Airplane());
united.add(new Airplane());

Here is how we would command each airplane in the fleet to takeoff:

Iterator it = united.iterator();
while(it.hasNext()) {
   Airplane a = (Airplane)it.next();
   a.takeoff();
}

The explicit downcast is necessary because Java collections are generic-- they hold Objects. Since every class implicitly extends the Java Object class, templates aren't needed.

Example: A Simple Application Framework

Java's platform independence means, among other things, that the Java VM doesn't care how characters and numbers are represented on the host platform. This makes writing a simple interactive program with a console user interface (CUI) a bit of a challenge. Since this is a common task, we now present the first of two CUI-based application frameworks. The entire framework consists of two classes: Console and AppError.

Calculator

As a demonstration of how to customize the framework, we present a simple calculator. Here are the basic commands:

C:\Pearce\JPOP\console>java Calculator
type "help" for commands
-> help
Console Help Menu:
about: displays application information
help: displays this message
quit: terminate application
ARG1 + ARG2 = sum of ARG1 and ARG2
ARG1 * ARG2 = product of ARG1 and ARG2
ARG1 - ARG2 = difference of ARG1 and ARG2
ARG1 / ARG2 = quotient of ARG1 and ARG2
Note 1: Spaces between tokens are required
Note 2: ARG1 & ARG2 are numbers.

The about command displays information about the framework and the customization:

-> about
Console Framework
copyright (c) 2001, all rights reserved

A Simple Calculator
Copyright (c) 2001, all rights reserved

Of course we can do arithmetic and small errors don't break the calculator:

-> -13 + 51
result = 38.0
-> 12 / 0
Application error, can't divide by 0
-> 13.8 * 3.1416
result = 43.35408
-> x + 2
Application error, ARG1 & ARG2 must be numbers
-> quit
bye

The Console Framework

Design

The console framework provides two classes: Console and AppError. All application-specific errors discovered in the customization are handled by throwing an AppError. The Console class supplies a control loop, and overridable help(), about() , handle(), and execute() methods. In fact, execute() is abstract, which means Console is an abstract class and extensions are required to implement an execute method.

Implementation

abstract public class Console {

   protected BufferedReader stdin;
   protected PrintWriter stdout;
   protected PrintWriter stderr;
   protected String prompt = "-> ";
   abstract protected String execute(String cmmd) throws AppError;

   public Console() {
      stdout = new PrintWriter(
         new BufferedWriter(
            new OutputStreamWriter(System.out)), true);
      stderr = new PrintWriter(
         new BufferedWriter(
            new OutputStreamWriter(System.out)), true);
      stdin = new BufferedReader(
         new InputStreamReader(System.in));
   }
   
   public void controlLoop() { ... }

   // overidables:
   protected void help() {
      stdout.println("Console Help Menu:");
      stdout.println(" about: displays application information");
      stdout.println(" help: displays this message");
      stdout.println(" quit: terminate application");
   }
   protected void about() {
      stdout.println("Console Framework");
      stdout.println("copyright (c) 2001, all rights reserved\n");
   }
   protected boolean handle(AppError exp) {
      stderr.println("Application error, " + exp);
      return true;
   }
}

The Control Loop

public void controlLoop() {
   
      boolean more = true;
      String cmmd = " ";
      String result = " ";
      String done = "done";
      stdout.println("type \"help\" for commands");

      while(more) {
         try {
            stdout.print(prompt);
            stdout.flush(); // force the write
            cmmd = stdin.readLine();
            
            if (cmmd.equals("quit")) {
               more = false;
            } else if (cmmd.equals("help")) {
               help();
            } else if (cmmd.equals("about")) {
               about();
            } else { // an application-specific command?
               result = execute(cmmd);
               stdout.println(result);
            }
         } catch(AppError exp) {
            more = handle(exp);
         } catch (IOException exp) {
            stderr.println("IO error, " + exp);
         } catch (Exception exp) {
            stderr.println("Serious error, " + exp);
            more = false;
         }
      } // while
      stdout.println("bye");
   } // controlLoop

Application Errors

public class AppError extends Exception {

   private String gripe;
   
   public String toString() {
      return gripe;
   }
   
   public AppError(String g) {
      super(g);
      gripe = g;
   }
   
   public AppError() {
      super("unknown");
      gripe = "unknown";
   }
}
   

The Calculator Extension

class Calculator extends Console {

   protected String execute(String cmmd) throws AppError { ... }

   protected void help() {
      super.help();
      stdout.println(" ARG1 + ARG2 = sum of ARG1 and ARG2");
      stdout.println(" ARG1 * ARG2 = product of ARG1 and ARG2");
      stdout.println(" ARG1 - ARG2 = diff of ARG1 and ARG2");
      stdout.println(" ARG1 / ARG2 = ratio of ARG1 and ARG2");
      stdout.println("Note 1: Spaces between tokens are required");
      stdout.println("Note 2: ARG1 & ARG2 are numbers.");
   }

   protected void about() {
      super.about();
      stdout.println("A Simple Calculator");
      stdout.println("Copyright (c) 2001, all rights reserved");
   }

   public static void main(String[] args) {
      Calculator calc = new Calculator();
      calc.controlLoop();
   }
} // Calculator

The execute() Method

protected String execute(String cmmd) throws AppError {
   
      double arg1, arg2;
      String op;
      StringTokenizer tokens = new StringTokenizer(cmmd);
      
      try {
      
         String digits = tokens.nextToken();
         arg1 = Double.valueOf(digits).doubleValue();
         op = tokens.nextToken();
         digits = tokens.nextToken();
         arg2 = Double.valueOf(digits).doubleValue();

         if (op.equals("+")) {
            return "result = " + (arg1 + arg2);
         } else if (op.equals("*")) {
            return "result = " + (arg1 * arg2);
         } else if (op.equals("-")) {
            return "result = " + (arg1 - arg2);
         } else if (op.equals("/")) {
            if (arg2 == 0)
               throw new AppError("can\'t divide by 0");
            return "result = " + (arg1/arg2);
         } else {
            throw new AppError("operator must be +, -, *. or /");
         }
      } catch (NumberFormatException e) {
         throw new AppError("ARG1 & ARG2 must be numbers");
      } catch (NoSuchElementException e) {
         throw new AppError("usage: ARG1 OP ARG2");
      }
   }