Thursday, June 3, 2010

My Ducks can fly

We use inheritance or composition for extending a class behaviour .This example illustrates the difference between two methodologies -

Here I have three cases ---
1.My Duck would always fly[Fix behaviour] .

2.My ducks can fly [flying behaviour is decided at time of duck instantiation and never changed later].

3. My ducks can fly [flying behaviour of duck can changed anytime ,depending on events]

Case -1
A domain neutral Duck interface ---(just think about duck)

public interface Duck {
public void swim();
public void walk();
public void quack();

}

But if in a domain it is decided that your duck has to fly ( duck having same fix flying behaviour for example each and every duck has to fly just 10feet always ) then it is okay to have an interface like this -

public interface Duck {
public void swim();
public void walk();
public void quack();
public void fly();

}

Case 2 -

In this requirement some of your ducks can fly and some not.Some can fly 10 feet ,some can fly 20 feet .It is up to client to decide .Following design gives your client freedom to plug flying behaviour in duck .Then you might come up with this

public interface Flyable {
public void fly();
}

and
public interface Duck extends Flyable{
public void swim();
public void walk();
public void quack();


}

public class GameDuck implemets Duck {
private Flyable flyable;

public Duck(Flyable flyable){
this.flyable = flyable;
}
public void fly(){
flyable.fly();
}
public void swim(){
system.out.println("swimming ....");
}
public void walk(){
system.out.println("walking ....");
}
public void quack(){
system.out.println("quacking ....");
}

}

public class SimpleFly implements Flyable {
public void fly(){
system.out.println("can fly ....");
}
}

public class CanNotFly implements Flyable {
public void fly(){
system.out.println("can not fly ....");
}
}

A simple client for Duck

public class DuckClient (){

public static void main(String[] args){
/*
For creating duck you might consider to use factory -DuckFactory with different implementations
*/
Duck flyingDuck = new GameDuck(new SimpleFly());
flyingDuck.fly();

Duck naturalDuck = new GameDuck(new CanNotFly());
naturalDuck.fly();
}
}


One alternative might be to make A abstract Duck class -and each concrete class implementing its fly method.That is most obvious design that comes into
mind at first .But that has a big disadvantage - you can not change flying behaviour of duck ever, once it instantiated.Also you would have so many Duck classes depending on number of flying behaviours.And client has to know about the behaviour of each concrete duck class.
Let me explain it more in third case--

Case III-

For a game I initially want my duck not to fly ...after crossing a first river now duck can fly up to a height of 10 feet ....and after crossing second

river duck can fly up to a hight 10*2=20 (double of first)...after swimming third river 20*2 =40 feet and so on ...


lets make little changes to Duck interface ---

public interface Duck extends Flyable{
public void swim();
public void walk();
public void quack();

public void changeFlyingBehaviour(Flyable flyable);

}


public class GameDuck implemets Duck {
private Flyable flyable;

public Duck(Flyable flyable){
this.flyable = flyable;
}
public void fly(){
flyable.fly();
}
public void swim(){
system.out.println("swimming ....");
}
public void walk(){
system.out.println("walking ....");
}
public void quack(){
system.out.println("quacking ....");
}
//client is changing its state
public void changeFlyingBehaviour(Flyable flyable) {
this.flyable = flyable;
}

}


//A decorator for doubling flight

public class DoubleMyFlight implemets Flyable {
Flyable flyable ;
public DoubleMyFlight(Flyable flyable){
this.flyable = flyable;
}

public void fly(){
System.out.println("Doubling my flight" + flyable)
}
}

public class DuckClient (){

public static void main(String[] args){
//lets get a duck which can not fly
Duck gameDuck = new Duck(new CanNotFly());

//Duck crosses first river

gameDuck.changeFlyingBehaviour(new DoubleMyFlight(gameDuck));

//Duck crosses second river -again double flyig height
gameDuck.changeFlyingBehaviour(new DoubleMyFlight(gameDuck));
}
}

This design (named -Strategy) makes it very easy for client to change flying behaviour of a duck object (plunging algorithm).If you were having a Duck class implementing its own flying behaviour then client can not change it (plug it ) .