你看那里有只小鸭哟,你看那里又只小鸭哟~小鸭嘎嘎嘎-ga-哟~小鸭嘎嘎嘎-ga-哟~j_0003.gif

    j_0012.gif矮油我艹,居然被这神曲给洗脑了。现在一看到小动物就莫名其妙的会哼唱起来j_0013.gif,那么问题来了,长颈鹿怎么叫?“好长~好长~”j_0011.gif是这样叫的么?这个问题真的很逗逼耶……j_0009.gif

    好啦,不扯淡了,原归正传。我要讲的主题就是小鸭子!哦sorry,准确的应该说是策略模式。因为策略模式中最经典例子莫过于鸭子模型的设计啦。之前有很多大神都写过文章讲解,讲得都非常深入和细致。但我这篇文章中,只是大略的以个人理解思维去阐述一个故事,如有讲得不足之处还望见谅。

    故事的开头是这样,会议上老板说:“最近接了个玩具厂的项目,要我们做个鸭子程序。没啥需求,就是要有大黄鸭、青头鸭、丑小鸭、唐老鸭……它们都会叫会飞!好啦,这种小项目就交给刚来实习的小林去做吧。”我胸有成足的接下了这个任务,心想这小儿科so easy ~

    先做个鸭子接口:

public interface Ducks {

    /**
     * 外貌
     */
    abstract void display();

    /**
     * 叫声
     */
    abstract void quack();

    /**
     * 飞行
     */
    abstract void fly();

    /**
     * 游泳
     */
    public void swim();

}

    在写鸭子接口的实现类:

public class Green implements Ducks {

    @Override
    public void display() {
        System.out.println("我是青头鸭~~");
    }

    @Override
    public void quack() {
        System.out.println("gaga~叫声很好听");
    }

    @Override
    public void fly() {
        System.out.println("飞得老高了我~~");
    }

    @Override
    public void swim() {
        System.out.println("游的老快了~");
    }

}

    …………   

    最后呢,在 if-else if- 来判断我要调用的是那个模型的鸭子:

public void selectDuck(String type) {
        if (type.equals("yellow")) {
            Ducks duck = new Green();
            duck.quack();
        } else if (type.equals("green")) {
            Ducks duck = new Yellow();
            duck.fly();
        }
    }

    恩,很快的项目完成了。我高高兴兴的把第一版本给提交了。老板哪会管你是如何实现的,只要你能完成预期的功能效果就ok。正当我还在得意洋洋之时,问题就来了。玩具厂的说要让有的鸭子不会飞,有的鸭子不会叫,有的鸭子不仅会叫还要回唱歌,唱歌还不够,要给鸭子分等级,等级高的还会讲故事……

    呵呵,我勒个擦!那我岂不是要在每个鸭子接口和实现类中添加好多方法?还得在调用selectDuck中多做好多else if判断?今天改了这些歌需求,万一明天又突然给我加过别的需求,我不是又要重新改一遍代码了么?忧伤的我,一筹莫展,对着电脑发呆。我该怎么办才好呢,让我别每次都改动原来写好的代码,有新的需求的时候只要多添加个类就好了?

    “今天怎~~~么不开心~?”老周问我:“不用担心,时间会给你答案~我的滑板鞋时尚时尚最时尚~”我吧事情跟老周详细讲了一遍后,老周笑道:“骚年,你还太年轻!这完全可以用策略模式去解决啊!”

    于是乎老周就开始很装逼的给我云云:


一、所谓的策略模式呢就是:

    定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。“亲,您能说的直白点吗?”“哦,抱歉。我向来都是这么专业的。说白了,就是你不是有很多鸭子吗?每个鸭子要有不同的功能,比如说飞行的算法,叫声的算法,游泳的算法,你都统统把它们一个一个的封装起来。到时候具体需要什么样的鸭子,就给客户自己去选择咯。”


二、那到底要怎么去实现呢?

    1. 对策略对象定义一个公共接口(FlyBehavior、QuackBehavior……接口;把所有的行为都做成接口方便以后对策略的扩展)
    2. 编写具体策略类,该类实现了上面的接口(FlyNoWay、FlyWithWings、Quack、MuteQuack……以后要是有什么新功能,就只需要去实现行为接口,添加新的策略类咯)
    3. 在使用策略对象的类(即:环境角色)中保存一个对策略对象的引用(Duck;最为关键的就是这个引用对象了,既然我们是要创建鸭子,当然就要用鸭子去引用策略啦。)
    4. 在使用策略对象的类中,实现对策略对象的set和get方法(注入)或者使用构造方法完成赋值(引用的小技巧就是用传说中的"组合")
    5. 客户端进行调用 (调用不同的鸭子时就分配给它相应的策略咯)

    扯了半天还是云里雾里的感觉。老周啊,你妹图扯个蛋呀!结果老周不服气,马上画了张图:

wKioL1R4Gy6C31F4AAEIY8RUODs915.jpg

    抽象策略角色:策略类,通常由一个接口或者抽象类实现。

    具体策略角色:包装了相关的算法和行为。

    环境角色:持有一个策略类的引用,最终给客户端用的。


三、终于开窍了,老周啊,你真是中国好同事哟。按照老周说的硬是把代码给敲出来了:

    首先,鸭子喊叫和飞行的公共接口。

package com.strategy.quack;

/**
 * 鸭子叫的接口
 * @author linhy
 *
 */
public interface QuackBehavior {

    public void quack();
}
package com.strategy.fly;

/**
 * 鸭子飞行接口
 * @author linhy
 *
 */
public interface FlyBehavior {

    public void fly();

}

    然后,就是这两个接口的实现类。

package com.strategy.quack;

/**
 * 会叫的鸭子类
 * @author linhy
 *
 */
public class Quack implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println("嘎嘎嘎~听得到我在叫你么……");
    }

}
package com.strategy.quack;

/**
 * 不会叫的鸭子类
 * @author linhy
 *
 */
public class MuteQuack implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println("我是一只安静的没鸭子,不会叫~");
    }

}
package com.strategy.fly;

/**
 * 会飞的鸭子类
 * @author linhy
 *
 */
public class FlyWithWings implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("我是在用翅膀飞翔,带你装B,带你飞……");
    }

}
package com.strategy.fly;

/**
 * 不会飞的鸭子类
 * @author linhy
 *
 */
public class FlyNoWay implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("超人不会飞,鸭子也不会飞……");
    }

}

    恩,策略都已经准备好了,接下来要开始引用它了。用什么引用?当然是鸭子啦!但是,鸭子又有好多种类型的哦,所以要先把鸭子抽象下,方便以后再创建新类型鸭子……

package com.strategy.duck;

import com.strategy.fly.FlyBehavior;
import com.strategy.quack.QuackBehavior;

/**
 * 鸭子抽象类
 * @author linhy
 *
 */
public abstract class Duck {
    //------------组合方式引用鸭子策略--------------------
    //引入鸭子的飞行行为对象
    private FlyBehavior flyBehavior;
    //引入鸭子的叫唤行为对象
    private QuackBehavior quackBehavior;

    //通过子类来设置鸭子的飞行行为
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    //通过子类来设置鸭子的叫唤行为
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }

    public void performFly() {
        flyBehavior.fly();
    }

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

    // 鸭子的外貌不一样哦,所以我要抽象~
    abstract void display();

    //鸭子都会游泳哦,游泳的方式就暂且都一样吧……
    public void swim() {
        System.out.println("All ducks float, even decoys");
    }

}

    抽象的鸭子已经设计好咯,就要写具体继承它的子类咯。

package com.strategy.duck;

import com.strategy.fly.FlyBehavior;
import com.strategy.quack.QuackBehavior;

/**
 * 青头鸭
 * @author linhy
 *
 */
public class GreenDuck extends Duck {
    //通过构造方法来设置引用的鸭子对象的行为
    public GreenDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
        super.setFlyBehavior(flyBehavior);
        super.setQuackBehavior(quackBehavior);
    }

    @Override
    public void display() {
        System.out.println("我是一只美丽动人的青头鸭~~");
    }

}
package com.strategy.duck;

import com.strategy.fly.FlyBehavior;
import com.strategy.quack.QuackBehavior;

/**
 * 小黄鸭
 * @author linhy
 *
 */
public class YellowDuck extends Duck {
    //通过构造方法来设置引用的鸭子对象的行为
    public YellowDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
        super.setFlyBehavior(flyBehavior);
        super.setQuackBehavior(quackBehavior);
    }

    @Override
    public void display() {
        System.out.println("我是一只美丽动人的小黄鸭~~");
    }

}

    这下鸭子模型准备好了,策略方法也写好了。那么就开始调用测试下看看呗。

package test;

import com.strategy.duck.GreenDuck;
import com.strategy.duck.YellowDuck;
import com.strategy.fly.FlyNoWay;
import com.strategy.fly.FlyWithWings;
import com.strategy.quack.MuteQuack;
import com.strategy.quack.Quack;

/**
 * 给不同类型的鸭子设置不同行为的策略
 * @author linhy
 *
 */
public class TestDuck {

    public static void main(String[] args) {
        //设置小黄鸭会飞和叫
        YellowDuck yellowDuck = new YellowDuck(new FlyWithWings(), new Quack());
        //设置青头鸭不会飞,也不会叫
        GreenDuck greenDuck = new GreenDuck(new FlyNoWay(), new MuteQuack());
        System.out.println("***************************************");
        yellowDuck.display();
        yellowDuck.performFly();
        yellowDuck.performQuack();
        yellowDuck.swim();
        System.out.println("*****************************************");
        greenDuck.display();
        greenDuck.performFly();
        greenDuck.performQuack();
        greenDuck.swim();

    }

}

    结果是这样的:

wKiom1R4FH-zKPBYAAGjzLUqgPw503.jpg

    嘿嘿,是这样的哦。别看上去好像感觉要写那么多类啊,接口啊……感觉好麻烦。当你要去维护,去新增功能的时候,你就会体会到一个好的设计模式是多麽的方便。好比,下次老板要是提出:小林,我要一只超级鸭(多功能的鸭子),会讲故事、会跳舞……我只需要新增个些策略和超级鸭子类,鸭子抽象类稍微修改下就可以。不要像以前那种蠢方法,耦合度太高,要修改太多地方,代码看上去也不整洁。

    老周最后来了一句:图羊图森破~。不装逼你会死呀,心里暗骂到。但还是忘分感谢……毕竟成长的道路上就是要在不断的挫折中学习。


结语:

    啧啧,故事终于讲完了,我知道自己讲的相当烂,有好多东西没说清。特附上一篇策略模式的好文章:《研磨设计模式之策略模式》