设计数据结构时,有句经典的哲理叫做:多用组合,少用继承。相比较继承,组合能提供更高的灵活性。策略模式通过使用组合代替继承的方法将不同的算法族整理成在主类之外的其他类,并在主类中添加算法族的对象,达到了一种组合的效果,这种方法使得主类与算法族耦合性降低,主类的方法只需要调用算法族对象中固定的方法即可,而不用再关心算法族具体是如何实现的。这也是面向接口编程的思想。下面结合具体的例子来分析。
一个父类Duck用于描述所有的鸭子,红鸭子、绿鸭子、橡皮鸭、木头鸭都从这个类中继承得到。
在使用策略模式之前,我们往往会这样设计数据结构。在Duck中拥有fly方法表示鸭子飞,speak方法表示鸭子叫,display方法表示鸭子的颜色,但是有些鸭子不会飞,比如橡皮鸭和木头鸭;有的鸭子不会叫,比如木头鸭。这些特点体现了个体之间的差异性,客观的说,我们使用继承的形式来实现各种鸭子是没问题的,但是在上面的情况中,我们需要为每一个不会飞或者不会叫的鸭子单独实现不同的方法,这样效率很低。
总结我们遇到的问题:不同的鸭子的fly情况可能有很多种,可能有的不会飞,有的会飞,或者还有其他的飞翔方式;不同的鸭子的speak情况也有很多种,有的会呱呱叫,有的只会吱吱叫(橡皮鸭),有的不会叫。
解决方法:我们将继承后有一部分相同,但又不是完全相同可能发生变化的部分从主类中分离出去,我们将飞行的状态FlyBehavior和叫的状态QuackBehavior完全分离出主类,创建两个接口,并用不同的类来实现这两个类。在主类中用组合的方式,添加这两个接口的对象作为字段,在主类的fly方法和speak方法中调用这两个对象调用fly与speak的方法。这样一来,主类Duck类完全不用关心fly和speak是怎么被调用的,因为它只是调用了字段中的对象的相应方法,只要我们在子类中将字段的引用指向合适的对象即可自动工作。
package StrategyP;
public abstract class Duck {
FlyBehavior fb;
QuackBehavior qb;
public void fly(){
fb.fly();
}
public void quack(){
qb.quack();
}
public abstract void display();
}
package StrategyP;
public interface FlyBehavior {
public void fly();
}
package StrategyP;
public interface QuackBehavior {
public void quack();
}
package StrategyP;
public class GreenDuck extends Duck{
GreenDuck(){
fb = new CanFly();
qb = new CanNotQuack();
}
public void display(){
System.out.println("I am green duck.");
}
}
package StrategyP;
public class CanFly implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("I can fly!");
}
}
package StrategyP;
public class CanNotQuack implements QuackBehavior {
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("I can't quack!");
}
}
package StrategyP;
public class Main {
public static void main(String[] args){
Duck duck = new GreenDuck();
duck.display();
duck.fly();
duck.quack();
}
}