简析设计模式之策略模式

来源:互联网 发布:代理商查询系统源码 编辑:程序博客网 时间:2024/06/02 06:10

首先,我们从鸭子说起,鸭子都会叫和游泳,但不同的鸭子有不同的外观:
先写一个父类:

public abstract class Duck {    public void quack(){        System.out.println("I'm duck, I can quack!");    }    public void swim(){        System.out.println("I'm duck, I can swim!");    }    public abstract void display();}

绿头鸭:

public class MallardDuck extends Duck {    @Override    public void display() {        System.out.println("I'm green head duck");    }}

红头鸭:

public class RedHeadDuck extends Duck {    @Override    public void display() {        System.out.println("I'm red head duck");    }}

但是,有了新的需求,我们想让鸭子飞:在父类写个fly()方法,所有的子类都继承该方法,但是,有的鸭子不会飞的,例如橡胶鸭子。这显然不是我们想要的。或许你会说,我们可以在子类中覆盖该方法,让它什么都不做,但是,如果有越来越多的其它类型的鸭子需要添加进来那又该如何?显然,会有越来越多重复的代码在子类中出现,而且如果父类稍微有点改动都会涉及到子类。继承,显然不是我们推崇的。

那么你会说,用接口来抽离出鸭子飞行和叫法的行为又怎样?

是的,这样是解决了一部分问题,起码不会出现会飞的假鸭子,但是,如果很多鸭子是会飞行以及叫法一样的,是不是又造成代码无法复用的问题?接口不具备实现功能,所以无法达到代码的复用。

但是,问题总是得解决的。我们只能从涉及原则中寻找答案了:
把可能变化的代码抽离并封装起来,好让其它部分不受影响。这是一个很简单的概念,也几乎是每个设计模式背后的精神所在。

好的,是时候对duck进行改造了,记住上面的原则:把可能变化的代码抽离并封装起来,这里的quack()和fly()是会随着鸭子的不同而改变的。
先把fly和quack抽离出来:

public interface FlyBehavior {    void fly();}
public interface QuackBehavior {    void quack();}

接下来,需要飞行或不需要飞行的都实现FlyBehavior接口,叫或不叫以及叫法不同的实现QuackBehavior 接口:

public class FlyWithWings implements FlyBehavior {    @Override    public void fly() {        System.out.println("I can fly");    }}
public class FlyNoWay implements FlyBehavior {    @Override    public void fly() {        System.out.println("I can't fly");    }}

类似地,quackBehavior同样如此实现。

如此一来,变化的部分已经和duck完全无关了,但是我们接下来要做的就是把他们联系起来,让不同的鸭子飞或不飞,叫或不叫:

public abstract class Duck {    FlyBehavior flyBehavior;    QuackBehavior quackBehavior;    public void performQuack(){        quackBehavior.quack();    }    public void performFly(){        flyBehavior.fly();    }    public void swim(){        System.out.println("I'm duck, I can swim!");    }    public abstract void display();}

这样,就把具体实现都留给了具体行为的实现类,现在,我们来看看子类的改造:

public class MallardDuck extends Duck {    public MallardDuck() {        quackBehavior = new Quack();        flyBehavior = new FlyWithWings();    }    @Override    public void display() {        System.out.println("I'm green head duck");    }}

最后来个测试类看看效果:

public class Main {    public static void main(String[] args) {        Duck greenDuck = new GreenHeadDuck();        greenDuck.disPlay();        greenDuck.performFly();        greenDuck.performQuack();        Duck redDuck = new RedHeadDuck();        redDuck.disPlay();        redDuck.performFly();        redDuck.performQuack();    }}

在此,我们需要记住一个原则:针对接口编程,而不是针对实现编程。由始至终,我们的程序都是尽量地遵循这个原则的。

为了使这个应用更加地弹性,我们还可以在Duck中添加set方法,动态地改变鸭子的行为,万一它哪天会飞了呢?

public void setFlyBehavior(FlyBehavior fb){        this.flyBehavior = fb;    }    public void setQuackBehavior(QuackBehavior qb){        this.quackBehavior = qb;    }

Main方法里:

Duck redDuck = new RedHeadDuck();        redDuck.disPlay();        redDuck.performFly();        redDuck.performQuack();        redDuck.setFlyBehavior(new FlyWithSwings());        redDuck.performFly();

其实,这就是另外一个设计原则:少用继承,多用组合。如同本例,当你将两个以上的类结合使用,就是组合。鸭子的行为不是继承过来的,而是适当地与行为对象组合而来的。

软件开发总是要花大量的时间在维护与升级上,而好的设计模式往往能帮我们减少大量的时间的消费。多用组合,少用继承。

最后,也是时候揭晓谜底了,这就是策略模式:
所谓策略模式就是定义了算法簇,分别封装起来,让它们之间可以互相替换。此模式让算法的变化独立于使用算法的客户。