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:
Demonstrated Concepts
classes implementing interfaces
interfaces extending multiple
interfaces
references that have interface
types
template methods
frameworks
factory methods
smart commands
interpreters
Interfaces
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);
}
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;
}
Monsters
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());
}
}
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());
}
}
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);
}
}
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 Space Age Implementation
Killer Robots
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());
}
}
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());
}
}
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);
}
}
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>
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:
public interface Command
{
public String execute(Context
c) throws IOException;
public String undo(Context
c) throws IOException;
}
A context can be nothing or anything: a stack, an environment, a store, a file:
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.
public Interpreter2()
{
cmmdStack = new Stack();
}
// factory method,
must define in extension
abstract protected
Command makeCommand(StringTokenizer tokens)
throws IOException;
public void controlLoop() { ... }
} // Interpreter2
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.
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
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>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>
Your job will be to define a calculator extension of the interpreter class:
Calculator2()
{
context = ???;
}
protected Command
makeCommand(StringTokenizer tokens)
throws IOException
{ ??? }
}
public String undo(Context
c)
{
return "noOp undone";
}
}