Interfaces

An interface is like a class containing only public abstract methods. Use interfaces the same way you would use abstract classes. Although abstract classes can contain other types of members, a class can only extend one other class (multiple inheritance is not allowed in Java), but a class may implement multiple interfaces. Here's the basic syntax:

interface AAA { ... }
interface BBB { ... }
interface CCC { ... }
interface DDD extends BBB, CCC { ... }
class XXX { ... }
class YYY extends XXX implements AAA, DDD { ... }
A class that implements an interface must implement all methods specified in the interface.

Demonstrated Concepts

interfaces

classes implementing interfaces

interfaces extending multiple interfaces
references that have interface types
template methods
frameworks
factory methods
smart commands
interpreters

Example: An Adventure Game Framework

Interfaces

interface Character
{
   public int getHealth();
   public void setHealth(int h);
   public void flee();
   public String getName();
}

 

interface Lover
{
   public void kiss(Character c);
}

 

interface Hero extends Character, Lover
{
   public void fight(Attacker atk);
}

 

interface Biter
{
   public void bite(Hero h);
}

 

interface Stomper
{
   public void stomp(Hero h);
}

 

interface Attacker extends Character, Biter, Stomper
{
   public void attack(Hero h);
}

Places class Place
{
   public Place()
   {
      maxAttackers = 10;
      numAttackers = 0;
      attacker = new Attacker[maxAttackers];
   }

   public void enter(Hero hero) // a template method
   {
      for (int i = 0; i < numAttackers; i++)
      {
         attacker[i].attack(hero);
         hero.fight(attacker[i]);
         if (hero.getHealth() > attacker[i].getHealth())
            attacker[i].flee();
         else
         {
            hero.flee();
            break;
         }
      }
    }

   public void addAttacker(Attacker atk)
   {
      if (numAttackers < maxAttackers)
         attacker[numAttackers++] = atk;
      else
         System.err.println("Room is full!");
   }

   private int numAttackers, maxAttackers;
   private Attacker[] attacker;
}

A Medieval Implementation

Monsters

class Monster implements Attacker
{

   private int health;
   private Random generator;
   private String name;

   public Monster(String nm)
   {
      name = nm;
      health = 50;
      generator = new Random();
   }

   public int getHealth() { return health; }
   public void setHealth(int h) { health = h; }
   public String getName() { return name; }
   public void flee()
   {
      System.out.println(name + " is limping from the room!");
   }

   public void stomp(Hero h)
   {
      System.out.println(name + " is stomping " + h.getName());
   }

   public void bite(Hero h)
   {
      System.out.println(name + " is biting " + h.getName());
   }

   public void attack(Hero h)
   {
      stomp(h);
      bite(h);
      stomp(h);
      bite(h);
      h.setHealth(generator.nextInt() % h.getHealth());
   }
}

Knights class Knight implements Hero
{

   private int health;
   private Random generator;
   private String name;

   public Knight(String nm)
   {
      name = nm;
      health = 50;
      generator = new Random();
   }

   public int getHealth() { return health; }
   public void setHealth(int h) { health = h; }
   public String getName() { return name; }
   public void flee()
   {
      System.out.println(name + " is limping from the room!");
   }
   public void kiss(Character c)
   {
      System.out.println(name + " is kissing " + c.getName());
   }
   public void fight(Attacker atk)
   {
      System.out.println(name + " is fighting " + atk.getName());
      atk.setHealth(generator.nextInt() % atk.getHealth());
   }
}

The Game class Game
{

   public static void main(String[] args)
   {
      Place dungeon = new Place();
      dungeon.addAttacker(new Monster("Godzilla"));
      dungeon.addAttacker(new Monster("Mothra"));
 
      Knight xena = new Knight("Xena");
      Knight connan = new Knight("Connan");

      dungeon.enter(xena);
      dungeon.enter(connan);

      if (connan.getHealth() > 0 && xena.getHealth() > 0)
         connan.kiss(xena);
   }
}

Program Output C:\Pearce\Java\Projects>javac Game.java

C:\Pearce\Java\Projects>java Game
Godzilla is stomping Xena
Godzilla is biting Xena
Godzilla is stomping Xena
Godzilla is biting Xena
Xena is fighting Godzilla
Godzilla is limping from the room!
Mothra is stomping Xena
Mothra is biting Xena
Mothra is stomping Xena
Mothra is biting Xena
Xena is fighting Mothra
Mothra is limping from the room!
Godzilla is stomping Connan
Godzilla is biting Connan
Godzilla is stomping Connan
Godzilla is biting Connan
Connan is fighting Godzilla
Connan is limping from the room!

 

C:\Pearce\Java\Projects>java Game
Godzilla is stomping Xena
Godzilla is biting Xena
Godzilla is stomping Xena
Godzilla is biting Xena
Xena is fighting Godzilla
Xena is limping from the room!
Godzilla is stomping Connan
Godzilla is biting Connan
Godzilla is stomping Connan
Godzilla is biting Connan
Connan is fighting Godzilla
Godzilla is limping from the room!
Mothra is stomping Connan
Mothra is biting Connan
Mothra is stomping Connan
Mothra is biting Connan
Connan is fighting Mothra
Mothra is limping from the room!
Connan is kissing Xena

C:\Pearce\Java\Projects>

A UML Class Diagram

A Space Age Implementation

Killer Robots

class Robot implements Attacker
{

   private int health;
   private Random generator;
   private String name;

   public Robot(String nm)
   {
      name = nm;
      health = 100;
      generator = new Random();
   }

   public int getHealth() { return health; }
   public void setHealth(int h) { health = h; }
   public String getName() { return name; }
   public void flee()
   {
      System.out.println(name + " has blown a fuse!");
   }

   public void stomp(Hero h)
   {
      System.out.println(name + " is crushing " + h.getName());
   }

   public void bite(Hero h)
   {
      System.out.println(name + " is lasering " + h.getName());
   }

   public void attack(Hero h)
   {
      stomp(h);
      bite(h);
      bite(h);
      bite(h);
      h.setHealth(generator.nextInt() % h.getHealth());
   }
}

Space Men class SpaceMan implements Hero
{

   private int health;
   private Random generator;
   private String name;

   public SpaceMan(String nm)
   {
      name = nm;
      health = 50;
      generator = new Random();
   }

   public int getHealth() { return health; }
   public void setHealth(int h) { health = h; }
   public String getName() { return name; }
   public void flee()
   {
      System.out.println(name + " is limping to the escape pod!");
   }
   public void kiss(Character c)
   {
      System.out.println(name + " is kissing " + c.getName());
   }
   public void fight(Attacker atk)
   {
      System.out.print(name + " is shooting beams at ");
      System.out.println(atk.getName());
      atk.setHealth(generator.nextInt() % atk.getHealth());
   }
}

The Game class Game
{

   public static void main(String[] args)
   {
      Place spaceShip = new Place();
      spaceShip.addAttacker(new Robot("R2D2"));
      spaceShip.addAttacker(new Robot("Robbie"));
 
      SpaceMan buzz = new SpaceMan("Buzz");
      SpaceMan barb = new SpaceMan("Barbarella");

      spaceShip.enter(buzz);
      spaceShip.enter(barb);

      if (buzz.getHealth() > 0 && barb.getHealth() > 0)
         buzz.kiss(barb);
   }
}

Program Output C:\Pearce\Java\Projects>javac Game.java

C:\Pearce\Java\Projects>javac Game.java

C:\Pearce\Java\Projects>java Game
R2D2 is crushing Buzz
R2D2 is lasering Buzz
R2D2 is lasering Buzz
R2D2 is lasering Buzz
Buzz is shooting beams at R2D2
Buzz is limping to the escape pod!
R2D2 is crushing Barbarella
R2D2 is lasering Barbarella
R2D2 is lasering Barbarella
R2D2 is lasering Barbarella
Barbarella is shooting beams at R2D2
Barbarella is limping to the escape pod!

 

C:\Pearce\Java\Projects>java Game
R2D2 is crushing Buzz
R2D2 is lasering Buzz
R2D2 is lasering Buzz
R2D2 is lasering Buzz
Buzz is shooting beams at R2D2
R2D2 has blown a fuse!
Robbie is crushing Buzz
Robbie is lasering Buzz
Robbie is lasering Buzz
Robbie is lasering Buzz
Buzz is shooting beams at Robbie
Buzz is limping to the escape pod!
R2D2 is crushing Barbarella
R2D2 is lasering Barbarella
R2D2 is lasering Barbarella
R2D2 is lasering Barbarella
Barbarella is shooting beams at R2D2
Barbarella is limping to the escape pod!
Buzz is kissing Barbarella

C:\Pearce\Java\Projects>

Another Stack Calculator

Interpreters are an important design pattern because all interactive programs that don't have GUIs use them. A good interpreter framework is an essential RAD (rapid development) tool. Our previous interpreter was a "dumb command" interpreter. Commands were merely token strings. It was the job of the execute method to parse and execute the command. The next interpreter is a "smart command" interpreter. A smart command knows how to execute itself and how to undo itself:

// Command.java
package pearce.java.util;
import java.io.*; // for IOException

public interface Command
{
   public String execute(Context c) throws IOException;
   public String undo(Context c) throws IOException;
}

(You will have to read ahead in the on-line notes to find out about exceptions.)

A context can be nothing or anything: a stack, an environment, a store, a file:

// Context.java
package pearce.java.util;
public interface Context {}
Interpreter

To be reusable, the interpreter declares an abstract method for creating commands. An ordinary method that creates abstract products is called a factory method or virtual constructor. The method is used in the control loop to make commands out of the tokens entered by the user. Each command is pushed onto a command stack maintained by the interpreter. The undo mechanism pops the last command pushed onto the stack and asks it to undo itself. The interpreter also maintains a protected context, which must be defined in extensions of the interpreter.

// interpreter2.java
package pearce.java.util;
import java.io.*;
import java.util.*;
 
abstract public class Interpreter2
{
   protected String prompt = "-> ";
   private Stack cmmdStack;
   protected Context context; // must define in extension

   public Interpreter2()
   {
      cmmdStack = new Stack();
   }

   // factory method, must define in extension
   abstract protected Command makeCommand(StringTokenizer tokens)
      throws IOException;

   public void controlLoop() { ... }

} // Interpreter2

The control loop prompts the user for an input expression (a String). If the expression is a meta command ("quit" or "undo" so far), the interpreter executes it directly. Otherwise, the abstract makeCommand() is called to turn the expression into a concrete command (thus, it is essentially a parser). The interpreter asks the command to execute itself, the prints the result.

Note that the control loop passes the context to the command's execute() and undo() methods. Naturally, we would expect command execution, and therefore undoing a command, to be context sensitive, i.e., to be dependent on a particular context.

Creating, executing, and undoing commands is risky. Many application-specific errors are possible that the interpreter can't know anything about. Therefore, when the interpreter creates a command, asks a command to undo itself, or asks a command to execute itself, it does so inside a try block. Any exceptions thrown by the factory method or the commands themselves will be caught and handled in the control loops catch blocks.

public void controlLoop()
{
   boolean more = true;
   String exp = " ";
   String result = " ";

   while(more)
   {
      Tools.cout.print(prompt);
      Tools.cout.flush(); // force the write
      try
      {
         exp = Tools.cin.readLine();
      }
      catch(IOException ioe)
      {
         Tools.cerr.println("Error: " + ioe.toString());
      }
 
      if (exp.equals("quit"))
      {
         more = false;
         Tools.cout.println("bye\n");
      }
      else if (exp.equals("undo"))
      {
         if (!cmmdStack.empty())
            try
            {
               Command cmmd = (Command) cmmdStack.pop();
               result = cmmd.undo(context);
               Tools.cout.println(result);
            }
            catch(IOException e)
            {
               Tools.cerr.println("Error: " + e);
            }
         else
            Tools.cerr.println("No commands to undo");
      }
      else
         try
         {
            StringTokenizer tokens = new StringTokenizer(exp);
            Command cmmd = makeCommand(tokens);
            cmmdStack.push(cmmd);
            result = cmmd.execute(context);
            Tools.cout.println(result);
         }
         catch(IOException e)
         {
            Tools.cerr.println("Error: " + e);
         }
   } // while
} // controlLoop

Problem and Sample Output

Your job is to extend the interpreter to create another stack calculator. This calculator combines doubles instead of rationals, which means you will have to become familiar with the pre-defined Double class. Here is a sample output of the program. Note how undo meta commands are executed:

C:\Pearce\Java\Projects>javac Calc2.java

C:\Pearce\Java\Projects>java Calc2
-> push 2
done
-> push 3
done
-> push 4
done
-> add
done
-> top
7.0
-> mul
done
-> top
14.0
-> undo
top undone
-> undo
mul undone
-> top
7.0
-> undo
top undone
-> undo
top undone
-> undo
add undone
-> top
4.0
-> quit
bye

C:\Pearce\Java\Projects>

 

Hints

Your job will be to define a calculator extension of the interpreter class:

class Calculator2 extends Interpreter2
{

   Calculator2()
   {
      context = ???;
   }

   protected Command makeCommand(StringTokenizer tokens)
      throws IOException
   { ??? }
}

It is the job of the calculator to define the protected context inherited from the interpreter. The object created must implement the empty Context interface, and it must extend the pre-defined Stack class (don't use your own stacks): class NumStack extends Stack implements Context {} You will need to define classes of smart commands for each valid input expression: push, pop, add, mul, top, etc. class PushCommand implements Command { ??? }
class PopCommand implements Command { ??? }
class AddCommand implements Command { ??? }
class TopCommand implements Command { ??? }
etc.
For example: class NoOpCommand implements Command
{
   public String execute(Context c)
   {
      return "done";
   }

   public String undo(Context c)
   {
      return "noOp undone";
   }
}

The Calc2 class provides main(): public class Calc2
{
   public static void main(String[] args)
   {
      Calculator2 calc = new Calculator2();
      calc.controlLoop();
   }
}