Studyon Minte9.com
Head First (Patterns)




Strategy Pattern



1) Inheritance (normal way)

	DuckA and DuckB extends Duck class. Both can quack and swim, and look different 
        (display method is abstract in Duck class).

	%java
		package testexample;

		abstract class Duck {
		    public void quack() { System.out.println(" Quack, "); };
		    public void swim() { System.out.println(" Swim, "); };
		    public abstract void display();
		}

		class DuckA extends Duck{
		    public void display() { System.out.println("I'm a DuckA:"); }
		}

		class DuckB extends Duck{  
		    public void display() { System.out.println("I'm a DuckB:"); }
		}

		public class Testexample {
		    
		    public static void main(String[] args) {
			new Testexample();
		    }
		    
		    public Testexample() {
			
			Duck duckA = new DuckA();
			duckA.display();
			duckA.quack();
			duckA.swim();
			    // I'm a DuckA: Quack, Swim
			
			Duck duckB = new DuckB();
			duckB.display();
			duckB.quack();
			duckB.swim();
			    // I'm a DuckB: Quack, Swim
		    }
		}



2) Add new method

	We need new method fly, so we added in the Duck contructor, but we've got errors!

	%java
		package testexample;

		abstract class Duck {
		    public void quack() { System.out.println(" Quack, "); };
		    public void swim() { System.out.println(" Swim, "); };
		    public abstract void display();
		    
		    public void fly() { System.out.println(" Fly, "); };
		}

		class DuckA extends Duck{
		    public void display() { System.out.println("I'm a DuckA:"); }
		}

		class DuckNoFly extends Duck{
		    public void display() { System.out.println("I'm a DuckNoFly:"); }
		}

		public class Testexample {
		    
		    public static void main(String[] args) {
			new Testexample();
		    }
		    
		    public Testexample() {
			
			new DuckA().fly();
			    // I'm a DuckA: Fly
			
			new DuckNoFly().fly();
			    // I'm a DuckNoFly: Fly
			    // Ups, not ok!

			/* --- Look here --- */
		    }
		}


3) Wrong approach

	It seems that we need to add fly method only in DuckA and DuckB, but that's not ok, because this 
        way we need to alter code that already was tested. 
	And what if we have 24 Duck types?

	%java
		class DuckA extends Duck{
		    public void fly() { System.out.println(" Fly, "); };	
		    public void display() { System.out.println("I'm a DuckA:"); }
		}

		// ... 24 times (ups!)

		class DuckX extends Duck{
		    public void fly() { System.out.println(" Fly, "); };	
		    public void display() { System.out.println("I'm a DuckA:"); }
		}


4) Strategy pattern
	
	We know that fly() vary across ducks. So we encapsulate this behavior in another class.
	
	%java

		package testexample;

		abstract class Duck {

		    public FlyBehavior flyBehavior;

		    public void performeFly() {
			flyBehavior.fly();
		    }
		    
		    public abstract void display();
		}


		/* --- FlyBehavior classes ---*/

		interface FlyBehavior {
		    abstract void fly();
		}

		class FlyWithWings implements FlyBehavior {
		    
		    @Override
		    public void fly() {
			System.out.println("fly with wings");
		    }
		}

		class FlyNoWay implements FlyBehavior {
		    
		    @Override
		    public void fly() {
			System.out.println("no fly");
		    }
		}


		/* --- Duck classes ---*/

		class DuckA extends Duck{
		    
		    public DuckA() {
			flyBehavior = new FlyWithWings();
		    }
		    
		    @Override
		    public void display() { System.out.println("I'm a DuckA: "); }
		}

		class DuckB extends Duck{
		    
		    public DuckB() {
			flyBehavior = new FlyNoWay();
		    }
		    
		    @Override
		    public void display() { System.out.println("I'm a DuckB: "); }
		}


		/* --- Test example ---*/

		public class Testexample {
		    
		    public static void main(String[] args) {
			new Testexample();
		    }
		    
		    public Testexample() {
			
			DuckA duckA = new DuckA();
			duckA.display();
			duckA.performeFly();
			    // I'm a DuckA: fly with wings
			
			DuckB duckB = new DuckB();
			duckB.display();
			duckB.performeFly();
			    // I'm a DuckB: no fly
		    }
		}


5) Futher changes

	In this way if we need to add a new Dock with a behavior like FlyRocket, we'll not need to alter the 
        DuckA code: 

	%java
		package testexample;

		abstract class Duck {
		    public FlyBehavior flyBehavior;
		    public void performeFly() {
			flyBehavior.fly();
		    }
		    public abstract void display();
		}

		interface FlyBehavior {
		    abstract void fly();
		}
		class FlyWithWings implements FlyBehavior {
		    public void fly() { System.out.println("fly with wings"); }
		}

		/* --- Look Here ---*/

		class FlyRocket implements FlyBehavior {
		    public void fly() { System.out.println("fly like a rocket"); }
		}

		class DuckA extends Duck{
		    public DuckA() {
			flyBehavior = new FlyWithWings();
		    }
		    public void display() { System.out.println("I'm a DuckA: "); }
		}
		class DuckC extends Duck{
		    
		    /* --- Look Here ---*/

		    public DuckC() {
			flyBehavior = new FlyRocket();
		    }
		    public void display() { System.out.println("I'm a DuckC: "); }
		}


		public class Testexample {
		    public static void main(String[] args) {
			new Testexample();
		    }
		    public Testexample() {
			
			DuckA duckA = new DuckA();
			duckA.display();
			duckA.performeFly();
			    // I'm a DuckA: fly with wings
			
			/* --- Look Here ---*/

			DuckC duckC = new DuckC();
			duckC.display();
			duckC.performeFly();
			    // I'm a DuckB: fly like a rocket
		    }
		}


6) Setting behavior dinamically

	%java
		package testexample;

		abstract class Duck {
		    public FlyBehavior flyBehavior;
		    public void performeFly() {
			flyBehavior.fly();
		    }
		    public abstract void display();
		    
		    /* --- Look Here --- */

		    public void setFlyBehavior(FlyBehavior fb) {
			flyBehavior = fb;
		    }
		}

		interface FlyBehavior {
		    abstract void fly();
		}
		class FlyWithWings implements FlyBehavior {
		    public void fly() { System.out.println("fly with wings"); }
		}
		class FlyRocket implements FlyBehavior {
		    public void fly() { System.out.println("fly like a rocket"); }
		}

		class DuckA extends Duck{
		    public DuckA() {
			flyBehavior = new FlyWithWings();
		    }
		    public void display() { System.out.println("I'm a DuckA: "); }
		}

		public class Testexample {
		    public static void main(String[] args) {
			new Testexample();
		    }
		    public Testexample() {
			
			DuckA duckA = new DuckA();
	
			/* --- Look Here --- */

			duckA.setFlyBehavior(new FlyRocket());
			duckA.display();
			duckA.performeFly();
			    // I'm a DuckA: fly like a rocket
		    }
		}