| Student: Evan Hecht Email: ehecht@calpoly.edu |
Topic: More Design Patterns (Ch. 10) |
public interface OptimusPrime
{
...
void punch(String side);
...
}
You also have a class you'd like to keep...
public class TruckOptimus
{
Side rightFront = new RightFront();
Side rightRear = new RightRear();
Side leftFront = new LeftFront();
Side leftRear = new LeftRear();
}
public interface Side
{
public BodyPart transform();
}
and you want that class to behave according to the interface.
public class Transformer implements OptimusPrime
{
TruckOptimus semi = new TruckOptimus();
public void punch(String side)
{
if(side.equals("left"))
{
((Arm)semi.leftFront.transform()).punch();
}
else
{
((Arm)semi.rightFront.transform()).punch();
}
}
}
public class OptimusRobotForm
{
public void RightArmPunch()
{
...
}
public void LeftArmPunch()
{
...
}
//Don't punch with the same arm twice in a row! You'll overheat!
}
The solution is treat them as objects. Have each command implement an interface containing the method you need, and store state information in the command object.
public interface Puncher
{
public void punch();
}
public class Arm implements Puncher
{
private Arm other;
private boolean lastPunch;
public Arm(Arm o)
{
other = o;
lastPunch = false;
}
public void punch()
{
if(lastPunch)
{
o.punch();
}
else
{
System.out.println("Pow!");
lastPunch = true;
o.enablePunch();
}
}
public void enablePunch()
{
lastPunch = false;
}
}
public class TruckOptimus
{
Side rightFront = new RightFront();
Side rightRear = new RightRear();
Side leftFront = new LeftFront();
Side leftRear = new LeftRear();
}
public interface BodyPart {}
Define a new interface with a method to return on object implementing the interface you need, and have concrete classes implement the new interface.
public interface Side
{
public BodyPart transform();
}
public class RightFront implements Side
{
public BodyPart transform()
{
return new Arm();
}
}
public class Arm implements BodyPart {}
public interface Pokemon
{
public Attack battle();
}
public class Trainer
{
...
Pokemon first;
public Trainer(Pokemon p)
{
first = p;
}
public void encounterEnemy(Enemy e)
{
e.attack(first.battle());
}
...
}
public class Pikachu implements Pokemon
{
...
public Attack battle()
{
return new Attack("Thundershock");
}
...
}
You can create a class that implements the required interface, and holds a reference to the 'real' class which is instantiated and used only when necessary.
public class PokeBall implements Pokemon
{
Pokemon contained;
public Attack battle()
{
if(contained == null)
{
contained = new Pikachu();
System.out.println("Pikachu! I choose you!");
}
return contained.battle();
}
}
public class PokeGame
{
public static void main(String[] args)
{
Trainer ash = new Trainer(new PokeBall());
...
}
}
public class Immortal
{
public boolean diesAt(int years)
{
return false;
}
//uh oh! this can't be good
}
Make the constructor private, and provide a Factory Method that returns the class's instance.
public class Immortal
{
public boolean diesAt(int years)
{
return false;
}
private Immortal() {}
private static Immortal highlander = new Immortal();
public static Immortal getImmortal() {return highlander;}
//there can be only one
}
public interface Soldier
{
public void accept(SoldierVisitor sv);
}
public class RebelSoldier implements Soldier
{
...
RebelSoldier bestFriend;
public void accept(SoldierVisitor sv)
{
sv.visitRebelSoldier(this);
}
...
}
public class Stormtrooper implements Soldier
{
...
public void accept(SoldierVisitor sv)
{
sv.visitStormtrooper(this);
}
...
}
public interface SoldierVisitor
{
public void visitRebelSoldier(RebelSoldier r);
public void visitStormtrooper(Stormtrooper s);
}
public class PrintAllegiance implements SoldierVisitor
{
public void visitRebelSoldier(RebelSoldier r)
{
System.out.println("Rebel Alliance");
r.bestFriend.accept(this);
}
public void visitStormtrooper(Stormtrooper s);
{
System.out.println("Empire");
//stormtroopers don't have friends
}
}
Remember, this is only useful when there is a known, limited number of subclasses, as the Visitor interface and each implementer must change when the number/type of subclasses changes.