第一章:策略模式

我们要创建一个鸭子世界,这里的鸭子会飞还会叫(嘎嘎嘎)。当然鸭子的种类也有很多,红头的、绿头的等等。

第一个设计方案:继承

我是OO程序员,我在基类中实现fly、quack和display方法。子类继承基类的方法,这样代码还可以得到复用。看起来真是个好主意。

image

 

public class Duck {
    public void quack() {
        System.out.println("鸭子嘎嘎叫");
    }
    
    public void display() {
        System.out.println("一只鸭子");
    }
    
    public void fly() {
        System.out.println("鸭子在飞");
    }
}

public class RedheadDuck extends Duck {
    @Override
    public void display() {
        System.out.println("一直红头鸭子");
    }
}

糟糕:橡胶鸭来到了鸭子世界

image

橡胶鸭也是鸭子,那很自然地继承鸭子类。

image

结果你会发现橡胶鸭也会飞?这显然是不合适的,并不是所有鸭子都会飞。你是个OO程序员,你应该想到了override,让我们试试:

//橡胶鸭继承自Duck,橡胶鸭不会飞但是会叫
public class RubberDuck extends Duck {
    @Override
    public void display() {
        System.out.println("一直橡胶鸭子");
    }
    
    @Override
    public void fly() {
        //什么都不做
    }
}

 

第二个设计方案:用接口

image

看起来是个不错的设计。

绿头鸭会飞会叫所以实现了这两个接口。

橡胶鸭只会叫,所以实现了“叫”这个接口。

木制鸭既不会飞又不会叫,所以一个接口都没有实现。

代码复用率也太低了吧

我们看下Java代码,观察一下quack()方法重复了好多次。要是以后修改了quack()方法,想想你是要改多少个地方。。。

//红头鸭
public class RedheadDuck extends Duck implements Flyable, Quackable {
    @Override
    public void display() {
        System.out.println("一直红头鸭子");
    }

    @Override
    public void quack() {
        System.out.println("嘎嘎叫");
    }

    @Override
    public void fly() {
        System.out.println("飞啊飞");
    }
}

//橡胶鸭
public class RubberDuck extends Duck implements Quackable{
    @Override
    public void display() {
        System.out.println("一直橡胶鸭子");
    }

    @Override
    public void quack() {
        System.out.println("嘎嘎叫");
    }
}

第三个设计方案:策略模式

image

这个设计一眼看去很奇怪,我们把飞和叫这种行为也定义成类了。类不应该是对现实实体的抽象吗?飞是一种动作,把它定义成类总觉得奇怪。

看这个UML图的时候请注意以下几点:

  • 基类(鸭子类)是面向接口的,它的两个字段(flyBehavior和quackBehavior)的类型都是接口。
  • 鸭子可能有很多种飞行的方式,“用翅膀”或者“坐火箭”。未来可能会增加其它方式,比如:用竹蜻蜓飞。策略模式可以很好地应对这种扩展。
  • 以后可能要修改"用翅膀飞"这个方法,那么我们只需要修改一处代码就可以。

光看这个类图你一定还是不理解策略模式到底在做什么,在下面列出的Java代码中请注意构造函数的实现。

public class RedHeadDuck extends Duck {
    public RedHeadDuck() {
        flyBehavior = new FlyWithWing();
        quackBehavior = new Quack();
    }
    
    @Override
    public void display() {
        System.out.println("一直红头鸭子");
    }
}

public class RubberDuck extends Duck {
    public RubberDuck() {
        //橡胶鸭不会飞
        flyBehavior = null;
        //叫起来想猫一样(似乎是一家奇葩的厂商生产的橡胶鸭)
        quackBehavior = new QuackLikeCat();
    }
    
    @Override
    public void display() {
        System.out.println("一只橡胶鸭");
    }
}

public class Duck {
    protected FlyBehavior flyBehavior;
    protected QuackBehavior quackBehavior;
    
    public void display() {
    }
    
    public void performQuack() {
        if (flyBehavior != null) {
            flyBehavior.fly();
        }
    }
    
    public void performFly() {
        if (quackBehavior != null) {
            quackBehavior.quack();
        }
    }
}

public interface FlyBehavior {
    public void fly();
}

public class FlyWithWing implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("两只翅膀,啪嗒啪嗒飞");
    }
}

public class FlyWithRocket implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("乘坐火箭,嗖一下飞过去了");
    }
}

public interface QuackBehavior {
    public void quack();
}

public class QuackLikeCat implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("喵喵叫");
    }
}

public class Quack implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("嘎嘎叫");
    }
}

练习

如果你认为已经看懂了策略模式,那么试试下面的练习,会帮助你更好理解。

  • 把Duck改成抽象类,并将display改成抽象方法。
    创造一个会坐火箭飞的鸭子。
  • 把FlyWithWing类的fly方法进行更改(改成什么随便你),留心代码复用的问题。
  • 增加一个使用竹蜻蜓飞行的行为,看看是不是很容易拓展呀。
  • 我们现在必须在构造函数中指定Duck的Fly和Quack行为。现在让我们在Duck中增加set方法,使之能动态改变Fly行为和Quack行为。
posted @ 2015-12-01 19:08  FJNU陈东  阅读(307)  评论(0编辑  收藏  举报