I just want a quick lookover that I implemented the different fly strategies correctly.
The program simply consists of a duck class that uses an interface for its fly method. The interface has different implementations (namely SimpleFly and NoFly), and a switch statement chooses the correct method based on the specie enum.
As I understand, the strategy pattern is meant to avoid duplicate code between child classes at the same level, which decreases maintainability and extensibility. So instead we abstract out the related algorithms to interfaces and choose them as needed.
CODE:
package DesignPatterns;
//Here we will separate the fly method of ducks into implementations of an internface and then instantiate ducks with those attributes
interface IFly {
public void fly();
}
//These are called strategies
class SimpleFly implements IFly {
@Override
public void fly() {
System.out.println("Quack Quack i am flying in the air");
}
}
class NoFly implements IFly {
@Override
public void fly() {
System.out.println("I cannot fly.");
}
}
//Now the base class just has to implement one of these strategies
class Duck {
private IFly flyType;
public enum SPECIE {
WILD, CITY, RUBBER
}
public Duck(SPECIE specie) {
switch(specie) {
//Here just select the algorithms you want to assign to each type of DUCK. More flexible than horizontal code between species.
case WILD:
case CITY:
this.flyType = new SimpleFly();
break;
case RUBBER:
this.flyType = new NoFly();
break;
default:
//If a new enum is defined but no definition yet, this stops code from breaking
this.flyType = new SimpleFly();
}
}
public void fly() {
flyType.fly();
}
}
The output is correct as in this example:
Duck rubberDuck = new Duck(Duck.SPECIE.RUBBER);
Duck normalDuck = new Duck(Duck.SPECIE.WILD);
rubberDuck.fly();
normalDuck.fly();
Yields:
I cannot fly.
Quack Quack i am flying in the air
Thank you in advance and please let me know about any gaps in my knowledge, Sshawarma
I would point out a couple issues of semantics and terminology.
fly
that can be implemented as not flying. Naming the method tryToFly
or documenting the method as merely an attempt are two ways of addressing this confusion. The software principle to reference here is Liskov Substitution.Duck
should accept an instance of IFly
directly in its constructor (or a setter method) rather than switching on an enum. Another goal of the Strategy pattern is to avoid branching logic.The essence of the pattern is that you've avoided creating multiple subclasses of Duck
by instead creating multiple implementations of IFly
. This has the advantage that those IFly
implementations can be reused without a complex inheritance hierarchy, e.g. WILD
and CITY
can share one strategy.
As mentioned in the comments, strategies also have the advantage that a Duck
could change its strategy at runtime. For example, IFly
might be implemented by Soar
and Glide
so that a Duck
would switch between these different strategies depending on the wind.