现在开发了一个模拟鸭子的游戏,里面有各种鸭子出现,它们会一边游泳,一边嘎嘎叫,但是每种鸭子外观是不同的
继承方式
这里定义一个超类Duck,共同的行为swim和quack可以放在超类方法里,而不同的鸭子的颜色外表不同,可以定义个抽象方法display,子类进行覆盖
Duck.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:45 */ public abstract class Duck { public void quack() {}; public void swim() {}; public abstract void display(); }
MallardDuck.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:45 */ public class MallardDuck extends Duck { @Override public void display() { //绿色 } }
RedheadDuck.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:45 */ public class RedheadDuck extends Duck { @Override public void display() { //红色 } }
如此一来,通过简单的继承就能满足需求,调用方法可以通过多态来完成
现在,又来了一个新需求,需要游戏里的鸭子飞起来玩耍
一想到,鸭子飞起来这个动作都一样,因此直接在超类里定义一个fly方法
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:45 */ public abstract class Duck { public void quack() {}; public void swim() {}; public void fly() {}; public abstract void display(); }
这时候问题来了,游戏里面一堆橡皮鸭子也飞起来了,这显然是不合常理的,因此直接超类里面添加是远远不够的
能想到的办法就是子类橡皮鸭里也定义一个fly方法进行覆盖
RubberDuck.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:46 */ public class RubberDuck { @Override public void quack() { //一按就叫 } @Override public void display() { //橡皮做的 } @Override public void fly() { //覆盖,不能飞 } }
与此同时,如果又有其它的鸭子,比如诱饵木头鸭子,又没法叫又没法飞,这样覆盖的又不一样了
DecoyDuck.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:48 */ public class DecoyDuck { @Override public void quack() { //覆盖,不能叫 } @Override public void display() { //诱饵,木头鸭 } @Override public void fly() { //覆盖,不能飞 } }
这样就导致一个问题,每一个继承的子类,行为方法都不一样,而且有的一样的方法又没法复用,说不定有的方法有没有考虑到,
接口方式
考虑到有的行为方法是某些类特定才有的,那么完全可以将这些方法从超类里面拿出来,然后分别定义成接口,那么能实现该方法的类可以实现这个接口,比如Flyable接口,只有能飞的鸭子可以实现,Quackable接口,只有能叫的鸭子类可以实现
首先是超类Duck.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:45 */ public abstract class Duck { public void swim() {}; public abstract void display(); }
接口Flyable,会飞的鸭子类来实现
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:46 */ interface Flyable { public void fly(); }
接口Quackable,会叫的鸭子类来实现
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:46 */ interface Quackable { public void quack(); }
比如MallardDuck能飞,能叫,绿色的鸭子,因此可以实现独特的接口
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:45 */ public class MallardDuck extends Duck implements Flyable, Quackable{ @Override public void display() { //绿色 } @Override public void fly() { //能飞 } @Override public void quack() { //能叫 } }
虽然这种方式能够保证每个鸭子类正确地执行它能进行的行为和方法,但是方法没法复用的问题依旧存在,比如如果要修改飞行的动作或者姿势,那么就必须要修改所有子类里的fly()方法的实现
变化和不变分离,针对接口而不是实现编程
这里每一个行为创建一个接口,然后不同的类实现这个接口完成不同的动作
这样行为都被分开到不同的类里,类只为实现行为接口
飞这个行为相关的:
FlyBehavior.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:49 */ interface FlyBehavior { void fly(); }
FlyWithWings.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/27 01:52 */ public class FlyWithWings implements FlyBehavior { @Override public void fly() { //实现飞 } }
FlyNoWay.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/27 01:54 */ public class FlyNoWay implements FlyBehavior { @Override public void fly() { //实现fly,但没法飞 } }
叫这个行为相关的:
QuackBehavior.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/27 01:56 */ interface QuackBehavior { void quock(); }
Quack.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/27 01:57 */ public class Quack implements QuackBehavior { @Override public void quack() { //实现嘎嘎叫 } }
Squeak.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/27 01:58 */ public class Squeak implements QuackBehavior { @Override public void quack() { //实现吱吱叫 } }
MuteQuack.java
package Pattern.Strategy; /** * @author lihui * @date 2018/3/27 01:59 */ public class MuteQuack implements QuackBehavior { @Override public void quack() { //实现不会叫 } }
这样之后显然各种飞和各种叫都能够被复用了,因为是单独的类,而且与鸭子超类毫无关系
再来考虑Duck类,这里定义上面接口类型的引用变量,
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:45 */ public abstract class Duck { public void swim() {}; public abstract void display(); FlyBehavior flyBehavior; QuackBehavior quackBehavior; public void performFly() { flyBehavior.fly(); } public void performQuack() { quackBehavior.quoak(); } }
其它比如MallardDuck类
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:45 */ public class MallardDuck extends Duck { @Override public void display() { //绿色 } public MallardDuck() { quackBehavior = new Quack(); flyBehavior = new FlyWithWings(); } }
这种玩法可真绕,捋一捋
1:Duck里定义了两个变量flyBehavior和quackBehavior,为接口类型,也就是定义的各种行为的接口
2:不直接定义fly,quack,而是定义方法performFly和performQuack,里面再通过上面的接口类型的变量去调用fly和quack,而这两个方法已经在实现接口的类里
3:设定接口类型的实例变量
还是很蒙,试下完整的源码,以及测试代码
首先看下main函数
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 21:51 */ public class Main { public static void main(String[] args) { Duck mallard = new MallardDuck(); mallard.performFly(); mallard.performQuack(); } }
这里指向子类的父类引用,多态,看下Duck类
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:45 */ public abstract class Duck { public void swim() { System.out.println("Duck swimming ..."); }; public abstract void display(); FlyBehavior flyBehavior; QuackBehavior quackBehavior; public void performFly() { flyBehavior.fly(); } public void performQuack() { quackBehavior.quack(); } }
调用的其实是flyBehavior.fly(),看看FlyBehavior类
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:49 */ interface FlyBehavior { void fly(); }
具体是调用了哪个接口的实现类的fly方法,看下MallardDuck类
package Pattern.Strategy; /** * @author lihui * @date 2018/3/26 20:45 */ public class MallardDuck extends Duck { @Override public void display() { //绿色 } public MallardDuck() { quackBehavior = new Quack(); flyBehavior = new FlyWithWings(); } }
可以看到构造函数,在创建对象的时候,flyBehavior指向的是FlyWithWings,因此调用的fly方法来自这里
看看FlyWithWings类
package Pattern.Strategy; /** * @author lihui * @date 2018/3/27 01:52 */ public class FlyWithWings implements FlyBehavior { @Override public void fly() { //实现飞 System.out.println("Flying with wings ..."); } }
最终打印Flying with wings …
如此一来,假如你又有一个新的飞行动作需要完成,只需要添加一个Flyxxxx类来实现FlyBehavior接口,然后再在Duck的子类xxxxDuck里构造函数里flyBehavior指向这个新的Flyxxxx类对象,就能完成需求
感觉好复杂,需要再好好理解理解