今天来看下策略模式
以下内容大部分来自《设计模式》一书,仅做了整理。
定义:策略模式定义了算法族,分别封装起来,让他们之间可以相互替换。
我们来设计一个场景:一个关于鸭子的游戏,鸭子在水里边游泳,边呱呱叫着。
超类:Duck.class
/** * 鸭子超类 */ public abstract class Duck { /** * 鸭子会呱呱叫 */ public void qucak(){}; /** * 鸭子会游泳 */ public void swim(){}; /** * 鸭子的外观,子类的鸭子都要自己去实现自己的外观,所以display方法是抽象的 */ public abstract void display(); }而鸭子的子类需要继承Duck类,就可以实现呱呱叫,和游泳能力,只需要自己实现自己的外观就可以了。
绿头鸭:MallardDuck.class
/** * 绿头鸭 */ public class MallardDuck extends Duck{ private static String TAG=MallardDuck.class.toString(); @Override public void display() { Log.i(TAG,"----------这是绿头鸭---------"); } }红头鸭:RedHeadDuck.class
/** * 红头鸭 */ public class RedHeadDuck extends Duck { private static String TAG = RedHeadDuck.class.toString(); @Override public void display() { Log.i(TAG, "----------这是红头鸭---------"); } }如果现在需求有改动说是这些鸭子要会飞,我们只需要在超类里添加一个fly方法就可以了,而子类并不需要做改动
/** * 鸭子超类 */ public abstract class Duck { /** * 鸭子会呱呱叫 */ public void qucak(){}; /** * 鸭子会游泳 */ public void swim(){}; /** * 鸭子的外观,子类的鸭子都要自己去实现自己的外观,所以display方法是抽象的 */ public abstract void display(); /** * 飞行 */ public void fly(){}; }然后产品经理跟你说我们需要向里面添加一种鸭子,叫橡皮鸭。现在问题就出现了,橡皮鸭是玩具,并不会飞,也不会呱呱叫。
橡皮鸭:RubberDuck.class
/** * 橡皮鸭 */ public class RubberDuck extends Duck{ private static String TAG = RubberDuck.class.toString(); /** * 橡皮鸭并不会呱呱叫,所以重写父类呱呱叫方法 */ @Override public void qucak() { Log.i(TAG, "----------橡皮鸭不会呱呱叫---------"); } @Override public void display() { Log.i(TAG, "----------这是橡皮鸭---------"); } /** * 橡皮鸭也不会飞,重写父类飞行方法 */ @Override public void fly() { Log.i(TAG, "----------橡皮鸭不会飞---------"); } }这样看似是一个可行的方案,可是鸭子的种类越来越多,鸭子会叫的、不会叫的、会游泳的、不会游泳的。。。。,也可能会增加一个鸭子属性,而已有的鸭子对这个属性又有不同的属性,我们就必须再去每一个鸭子子类里去重写父类的这个方法,这样一直在重写父类的方法,导致代码重复,工作量越来越大,这并不是我们想要看到的方案。
如果能有一种能够建立软件的方法,能够让我们对既有的代码影响最小的方式来改动软件,我们就可以花更少的时间重做代码,节省大量的工作量、工作时间那该多好。
不管一个软件设计的多好,一段时间后,需求总会做改动。
现在可以看出继承(extends)并不能很好的解决这个问题,因为你不知道在未来子类会新冒出一个什么属性。
所有我们将引出“针对接口编程”概念。
“针对接口编程”的真正意思是“针对超类型(supertype)编程”
这里所谓的“接口”有多个含义,接口是一个“概念”,也是一种Java的interface构造。你可以在不涉及Java interface的情况下,“针对接口编程”,关键就在多态。利用多态,程序可以针对超类型编程,执行时会根据实际状况执行到真正的行为,不会被绑死在超类型的行为上。“针对超类型编程”这句话,可以明确的说成“变量的声明类型应该是超类,通常是一个抽象类或者是一个接口,如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量。这也意味着,声明类时不用理会以后执行时的真正对象类型!”
---------------------------------------------------------------------下面才是正文------------------------------------------------------------------------------------------
上面这些话是书里说的,理解不了就别看了,简单的说就是把易变的部分做成接口(interface)形式,在父类里声明一个接口变量,在子类里给这个变量赋值。
下面上代码:
鸭子的呱呱叫和飞行行为是易变的,我们就把呱呱叫和飞行行为做成接口形式。
呱呱叫接口:QuackInterface.class
/** * 呱呱叫行为接口 */ public interface QuackInterface { /** * 鸭子会呱呱叫 */ void qucak(); }飞行行为接口:FlyInterface.class
/** * 飞行行为接口 */ public interface FlyInterface { /** * 飞行 */ void fly(); }
以前的Duck.class重新写
/** * 鸭子超类 */ public abstract class Duck { /** * 为行为接口声明两个引用变量 */ QuackInterface quackInterface;//呱呱叫行为接口 FlyInterface flyInterface;//飞行行为接口 /** * 鸭子会游泳 */ public void swim(){}; /** * 鸭子的外观,子类的鸭子都要自己去实现自己的外观,所以display方法是抽象的 */ public abstract void display(); /** * 呱呱叫行为委托给呱呱叫接口 */ public void perfromQuack(){ quackInterface.qucak(); } /** * 飞行行为委托给飞行接口 */ public void perfromFly(){ flyInterface.fly(); } }绿头鸭类重写:MallardDuck.class
/** * 绿头鸭 */ public class MallardDuck extends Duck implements QuackInterface,FlyInterface{ private static String TAG=MallardDuck.class.toString(); @Override public void display() { Log.i(TAG,"----------这是绿头鸭---------"); } @Override public void qucak() { Log.i(TAG,"----------绿头鸭呱呱叫---------"); } @Override public void fly() { Log.i(TAG,"----------绿头鸭会飞---------"); } }橡皮鸭:RubberDuck.class
/** * 橡皮鸭 */ public class RubberDuck extends Duck implements QuackInterface,FlyInterface{ private static String TAG = RubberDuck.class.toString(); @Override public void display() { Log.i(TAG, "----------这是橡皮鸭---------"); } @Override public void qucak() { Log.i(TAG, "----------橡皮鸭不会呱呱叫---------"); } @Override public void fly() { Log.i(TAG, "----------橡皮鸭不会飞---------"); } }我们创建这些类之后,就可以使用了
Duck duck=new MallardDuck(); duck.perfromQuack(); duck.perfromFly();
Duck duck=new RubberDuck(); duck.perfromQuack(); duck.perfromFly();这样就可以创建出会飞、会呱呱叫的绿头鸭和不会飞、不会叫的橡皮鸭了。
-------------------------------------------------------------动态设定行为,下面的是更好的方案---------------------------------------------------------------------------------------------------
先拿一个飞行属性举例。
在Duck.class中加入一个方法:
public void setFlyInterface(FlyInterface flyInterface) { this.flyInterface = flyInterface; }完整Duck.class
/** * 鸭子超类 */ public abstract class Duck { /** * 为行为接口声明引用变量 */ FlyInterface flyInterface;//飞行行为接口 /** * 鸭子会游泳 */ public void swim(){}; /** * 鸭子的外观,子类的鸭子都要自己去实现自己的外观,所以display方法是抽象的 */ public abstract void display(); /** * 飞行行为委托给飞行接口 */ public void perfromFly(){ flyInterface.fly(); } public void setFlyInterface(FlyInterface flyInterface) { this.flyInterface = flyInterface; } }新增两个类
/** * 这个鸭子不会飞 */ public class NoFlyClass implements FlyInterface{ private static String TAG=NoFlyClass.class.toString(); @Override public void fly() { Log.i(TAG, "----------我不会飞---------"); } }
/** * 这个鸭子会飞 */ public class FlyClass implements FlyInterface{ private static String TAG=FlyClass.class.toString(); @Override public void fly() { Log.i(TAG, "----------我会飞---------"); } }重写绿头鸭类:MallarDuck.class
/** * 绿头鸭 */ public class MallardDuck extends Duck{ private static String TAG=MallardDuck.class.toString(); @Override public void display() { Log.i(TAG,"----------这是绿头鸭---------"); } }重写橡皮鸭类:RubberDuck
/** * 橡皮鸭 */ public class RubberDuck extends Duck{ private static String TAG = RubberDuck.class.toString(); @Override public void display() { Log.i(TAG, "----------这是橡皮鸭---------"); } }使用的时候
Duck mallardDuck=new MallardDuck(); mallardDuck.setFlyInterface(new FlyClass()); mallardDuck.perfromFly();
Duck rubberDuck=new RubberDuck(); rubberDuck.setFlyInterface(new NoFlyClass()); rubberDuck.perfromFly();这篇文章到这就结束了。
如有错误或纰漏的地方,欢迎指正。