策略设计模式


什么是策略设计模式?

策略设计模式定义了算法族(行为族),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

为什么要用策略设计模式?

试想这样一种业务场景。 公司设计了一款与鸭子有关的游戏,游戏中有各种各样的鸭子在水里嬉戏。 现在游戏里一共有两种鸭子,分别是红头鸭与绿头鸭,且具备喊叫与游泳这两种行为。

duck.png-21.3kB

每种鸭子的外观都不同,所以 display() 是抽象方法。

/**
 * 策略设计模式
 * 鸭子超类
 */
public abstract class Duck {

    public void quack() {
        System.out.println("鸭子叫");
    }

    public void swim() {
        System.out.println("鸭子游泳");
    }


    public abstract void display();

}


/**
 * 绿头鸭
 */
public class MallardDuck extends Duck {

  @Override
    public void display() {
        System.out.println("绿头鸭");
    }

}


/**
 * 红头鸭
 */
public class RedHeadDuck extends Duck {

    @Override
    public void display() {
        System.out.println("红头鸭");
    }
}

随着市场竞争变得越来越大,公司高层经过讨论决定需要给鸭子加上一个会飞的功能。这个需求太简单了,我只要一分钟就可以解决。

/**
 * 策略设计模式
 * 鸭子超类
 */
public abstract class Duck {

    public void quack() {
        System.out.println("鸭子叫");
    }

    public void swim() {
        System.out.println("游泳");
    }

    public void fly() {
        System.out.println("飞翔")
    }

    public abstract void display();
}

新功能上线后的第二天,经理通知我咱们遇到了大麻烦。 原来公司游戏里除了红头、绿头鸭以外还有一种橡皮鸭。现在橡皮鸭在天空中飞来飞去。这该如何是好呢? 我毕竟是一个面向对象程序员,立刻就想到了使用 override 来解决这个问题。


/**
 * 橡皮鸭
 */
public class RubberDuck extends Duck {
    @Override
    public void quack() {
        //do nothing
    }

    @Override
    public void fly() {
        //do nothing
    }

    @Override
    public void display() {
        System.out.println("橡皮鸭");
    }
}

问题解决了。 可是,若是将来游戏里出现了诱饵鸭、机械鸭等等其他鸭子,每种鸭子的行为都有所不同,难道每次加入新的鸭子都去检查并覆盖duck中的方法吗? 看来使用继承来为所有的鸭子提供行为会造成很多麻烦,于是我想到了尝试用接口来解决这个问题。

屏幕快照 2016-11-06 下午2.40.27.png-41.5kB


public abstract class Duck {

//    public void quack() {
//        System.out.println("鸭子叫");
//    }

    public void swim() {
        System.out.println("鸭子游泳");
    }

//    public void fly() {
//        System.out.println("飞行");
//    }

    public abstract void display();

}

/**
 * 会飞的接口
 */
public interface Flyable {

    void fly();
}

/**
 * 会叫的接口
 *
 */
public interface Quackable {

    void quack();
}


/**
 * 绿头鸭
 */
public class MallardDuck extends Duck implements Flyable, Quackable {


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

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

     @Override
    public void display() {
        System.out.println("绿头鸭");
    }
}


/**
 * 橡皮鸭
 */
public class RubberDuck extends Duck {

    @Override
    public void display() {
        System.out.println("橡皮鸭");
    }

}

将容易产生变化的行为独立抽象成为接口,在需要这些行为的子类对象中分别实现不同的具体行为,这样就避免了每个子类都需要去检查并覆盖父类方法的工作! 然而,经理对这样的做法产生了疑问。由于将变化的行为声明为接口后,需要该行为的子类对象都需要去实现接口中的方法。 若有100支鸭子岂不是要实现100次行为?这无疑加大了工作量甚至会造成项目延期交付!该如何是好呢?

策略设计模式怎么用?

幸运的是,有一个设计原则刚好适用于此种情况:

找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

现在,根据此原则来重新设计这个鸭子游戏。 首先将容易产生变化的部分(quack 与 fly)抽取出来,重构为两组实现了 quackable 与 flyable 的类。(别误会,我不是让你使用多重继承) 接着想办法让鸭子把容易变化的 fly 与 quack委托(delegate)给别人处理。 最后为了提供更强的灵活性,我们需要在运行中也能给鸭子对象灵活的设置不同的行为模式。比如增加一个火箭发射器使橡皮鸭也能飞!

具体应该怎么样实现这样的设计呢?另一个设计原则发挥作用的时候到了

多用组合,少用继承。

Duck4.png-61kB

抽象变化的部分,将变化的部分独立出来封装成单独的模块。


/**
 * 飞行的行为接口
 */
public interface Flyable {

    void fly();

}


/**
 * 飞行行为类
 */
public class Fly implements Flyable {
    @Override
    public void fly() {
        System.out.println("飞翔");
    }
}

/**
 * 不能飞行为类
 */
public class FlyNoWay implements Flyable {
    @Override
    public void fly() {
        System.out.println("不能飞");
    }
}

/**
 * 火箭飞行行为类
 */
public class FlyWithRockets implements Flyable {
    @Override
    public void fly() {
        System.out.println("火箭飞行");

    }
}

/**
 * 呱呱叫行为接口
 *
 */
public interface Quackable {

    void quack();
}

/**
 * 沉默行为类
 */
public class MuteQuack implements Quackable {

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

/**
 * 呱呱叫行为类
 */
public class Quack implements Quackable {
    @Override
    public void quack() {
        System.out.println("呱呱叫");
    }
}

想办法让鸭子把容易变化的 fly 与 quack 委托(delegate)给别人处理。

/**
 * 策略设计模式
 * 鸭子超类
 */
public abstract class Duck {

    public Flyable flyable;

    public Quackable quackable;

    public void swim() {
        System.out.println("鸭子游泳");
    }

    public abstract void display();

    public void performFly() {

        flyable.fly();
    }

    public void performQuack() {
        quackable.quack();
    }

    public void setQuackable(Quackable quackable) {
        this.quackable = quackable;
    }


    public void setFlyable(Flyable flyable) {
        this.flyable = flyable;
    }

}

为了提供更强的灵活性,我们需要在运行中也能给鸭子对象灵活的设置不同的行为模式。比如增加一个火箭发射器使橡皮鸭也能飞!

/**
 * 绿头鸭
 */
public class MallardDuck extends Duck {

    public MallardDuck(Flyable flyable, Quackable quackable) {
        this.flyable = flyable;
        this.quackable = quackable;
    }

    @Override
    public void display() {
        System.out.println("绿头鸭");
    }
}

/**
 * 红头鸭
 */
public class RedHeadDuck extends Duck {

    public RedHeadDuck(Flyable flyable, Quackable quackable) {
        this.flyable = flyable;
        this.quackable = quackable;
    }

    @Override
    public void display() {
        System.out.println("红头鸭");
    }
}


/**
 * 橡皮鸭
 */
public class RubberDuck extends Duck {

    public RubberDuck(Flyable flyable, Quackable quackable) {
        this.flyable = flyable;
        this.quackable = quackable;
    }

    @Override
    public void display() {
        System.out.println("橡皮鸭");
    }
}

测试

/**
 * 策略设计模式测试类
 */
public class DuckGameTester {


    public static void main(String args[]) {
        // 创建行为类
        Flyable flyNoWay = new FlyNoWay();
        Flyable flyWithRockets = new FlyWithRockets();
        Flyable fly = new Fly();
        Quackable muteQuack = new MuteQuack();
        Quackable quack = new Quack();

        // 创建各种类型的鸭子对象
        MallardDuck mallardDuck = new MallardDuck(fly, quack);
        mallardDuck.performFly();
        mallardDuck.performQuack();
        mallardDuck.setQuackable(muteQuack);
        mallardDuck.performQuack();

        // 创建橡皮鸭对象
        RubberDuck rubberDuck = new RubberDuck(flyNoWay, muteQuack);
        rubberDuck.performFly();
        rubberDuck.performQuack();
        rubberDuck.setFlyable(flyWithRockets);
        rubberDuck.performFly();

        // 继续创建其他新鸭子对象,只需要将行为委托给对应的行为对象就可以
    }
}

通过合理运用前文所描述的2个设计原则,我们将变化部分的代码抽取封装与稳定部分独立开来,接着通过委托的方式使目标对象将变化的功能,委托给了抽取出来的行为(算法族)对象。并且在运行中我们还能随意的改变对象的行为。 这种通过组合的方式建立的系统具有很大的弹性,这就是策略设计模式的运用方式。


Copyright 2017/08/15 by Chuck Lin

若文章有幸帮到了您,您可以捐助我,以鼓励我写出更棒的作品!

alipay.jpg-17.7kBwechat.jpg-16.7kB

Copyright © chuck Lin 2017 all right reserved,powered by Gitbook该文件修订时间: 2018-07-15 10:39:59

results matching ""

    No results matching ""