你看那里有只小鸭哟,你看那里又只小鸭哟~小鸭嘎嘎嘎-ga-哟~小鸭嘎嘎嘎-ga-哟~
矮油我艹,居然被这神曲给洗脑了。现在一看到小动物就莫名其妙的会哼唱起来
,那么问题来了,长颈鹿怎么叫?“好长~好长~”
是这样叫的么?这个问题真的很逗逼耶……
好啦,不扯淡了,原归正传。我要讲的主题就是小鸭子!哦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. 客户端进行调用 (调用不同的鸭子时就分配给它相应的策略咯)
扯了半天还是云里雾里的感觉。老周啊,你妹图扯个蛋呀!结果老周不服气,马上画了张图:
抽象策略角色:策略类,通常由一个接口或者抽象类实现。
具体策略角色:包装了相关的算法和行为。
环境角色:持有一个策略类的引用,最终给客户端用的。
三、终于开窍了,老周啊,你真是中国好同事哟。按照老周说的硬是把代码给敲出来了:
首先,鸭子喊叫和飞行的公共接口。
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();
}
}
结果是这样的:
嘿嘿,是这样的哦。别看上去好像感觉要写那么多类啊,接口啊……感觉好麻烦。当你要去维护,去新增功能的时候,你就会体会到一个好的设计模式是多麽的方便。好比,下次老板要是提出:小林,我要一只超级鸭(多功能的鸭子),会讲故事、会跳舞……我只需要新增个些策略和超级鸭子类,鸭子抽象类稍微修改下就可以。不要像以前那种蠢方法,耦合度太高,要修改太多地方,代码看上去也不整洁。
老周最后来了一句:图羊图森破~。不装逼你会死呀,心里暗骂到。但还是忘分感谢……毕竟成长的道路上就是要在不断的挫折中学习。
结语:
啧啧,故事终于讲完了,我知道自己讲的相当烂,有好多东西没说清。特附上一篇策略模式的好文章:《研磨设计模式之策略模式》
转载于:https://blog.51cto.com/linhongyu/1584007