More Design Patterns!

More Design Patterns!!

More Design Patterns!!!

Student: Evan Hecht
Email:    ehecht@calpoly.edu
Topic: More Design Patterns (Ch. 10)

Key points


Presentation and Exercise

Adapter

Let's say you have an interface, with behavior you need to keep.

	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.

With an adapter, you can implement the behavior using that class.

	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();
	        }
	    }
	}

Command

So, you have operations/functions/methods you'd like to perform, but they need to keep track of how/when/if they're used.

	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;
	    }
	}

Factory Method

Sometimes you need an object that implements a particular interface, but you don't know what, if any, constructor you can safely call.

	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 {}

Proxy

Resources are limited. You may have an object that a program 'needs', but is never actually used...

	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());
	        ...
	    }
	}

Singleton

Occasionally, it only makes sense to have one instance of a particular class.

	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
	}

Visitor

When you want to define an operation that will apply to a superclass, and will treat each subclass differently, use the Visitor pattern.

	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.
-
  1. Exercise
  2. Solution

Give two short exam questions (and solutions) about your topic.

Helpful resources:


CSC 305 Home