先从简单的鸭子游戏说起
jack为公司做了一套相当牛逼的鸭子游戏,鸭子能戏水,能呱呱叫。此系统内部设计使用了标准的oo技术,设计了一个鸭子超类,并让鸭子继承此超类
去年由于公司竞争压力加剧,在为期一周的高尔夫假期兼头脑风暴之下,公司主管认为是创新的时候了,他们要在下周互联网大会上展示一些牛逼哄哄的东西振奋人心。
现在我们让鸭子能飞
主管们相信,会飞的鸭子能将竞争者远远抛在后面。在这个时候,jack拍拍胸脯说,“领导放心,这个飞鸭我一个星期就能搞定”,毕竟,我是一个oo程序员,一点问题没有。
jack在想,我只要在duck类中加上fly()方法,所有继承的鸭子就都能飞了。这是我大显身手的时候了,绽放我的oo才华吧!哈哈哈
但是,可怕的问题发生了
领导带着jack所开发的鸭子游戏上大会上演讲,许多鸭子就在屏幕上飞来飞去,台下一片笑声。在讲台上,领导很尴尬。大会结束,立马打电话给jack。
jack在电话上承认他的设计有点问题,一旦给超类加上fly()方法,所有的鸭子都能飞了,连那些不具备飞行能力的鸭子也能飞了。但是jack又想,我可以让那些不具备飞行能力的鸭子在他自己的类中覆盖fly()方法,让它什么事情也不做,这样鸭子就不能飞了。
利用接口如何?
jack意识到继承不是好的解决方法,因为他拿到领导的备忘录,里面说是以后会常常更新产品,至于怎样的需求还没有想好。jack知道了规格会常常变更,每当有新鸭子子类出现时,他就要检查所有子类的鸭子的fly()方法是否要覆盖,要是还有其他方法的话也是同样需要检查,这简直是噩梦。
所以他需要一个更清晰的方法,让某些鸭子类型可飞或可叫等功能。
于是他想了一个方法把可变的功能性方法提取出来,比如把fly()方法从超类中取出,单独设置成Flyable接口,quack()方法设置成QuackAble接口,让会飞的鸭子就去实现flyable接口,会叫的鸭子实现Quackable接口。
领导看见了jack的这种设计模式,还是一脸不高兴,虽然你用接口的方法去解决所有鸭子继承的缺陷,但是使用接口的话,却造成代码无法复用,这只能是从一个噩梦跳进另一个噩梦,甚至,在会飞的鸭子中,飞行的动作也是千变万化。
如果能有一种建立软件的方法,好让我们可以用一种对既有的代码影响最小的方式来修改软件该有多好,我们就可以花较少的时间重做代码,而多让程序做更酷的事情。
现在我们知道使用继承不能很好的解决问题,因为鸭子的行为在子类里不断改变,并且让所有鸭子都有这些行为是不恰当的。使用接口也是有问题的,因为在接口中,并没有代码的实现,会造成代码不能复用,在每个实现的子类都要实现该方法,不可取。
设计原则一
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
好了,该是把鸭子的行为从duck类中取出的时候了!
现在,为了要分开“变化和不会变化的部分”,我们准备建立两组类,一个是fly相关的,一个是quack相关的,每一组类都将实现各自动作,比方说,我们可能有一个类实现“呱呱叫”,另一个类实现“吱吱叫”,还有一个类实现“安静”。
设计原则二
针对接口编程,而不是针对实现编程
为了不让鸭子子类直接实现行为接口,我们设计了一个行为接口类FlyBehavior和QuackBehavior 所表示的行为,所以实际的“实现”不会被绑死在鸭子的子类中。看类图
接下来,有了以上的行为类,我们只要把这些行为类作为鸭子类的属性,注入就行。
我们实现代码
/** * Created by huangx on 2018/11/12. */ public interface Flybehavior { public void fly(); }
/** * Created by huangx on 2018/11/12. */ public class FlyWithWings implements Flybehavior{ public void fly() { System.out.println("i am flying!"); } }
/** * Created by huangx on 2018/11/12. */ public class FlyNoWay implements Flybehavior{ public void fly() { System.out.println("i cant fly"); } }
/** * Created by huangx on 2018/11/12. */ public interface QuackBehavior { public void quack(); }
/** * Created by huangx on 2018/11/12. */ public class Quack implements QuackBehavior { public void quack() { System.out.println("quack"); } }
/** * Created by huangx on 2018/11/12. */ public class MuteQuack implements QuackBehavior{ public void quack() { System.out.println("silence"); } }
/** * Created by huangx on 2018/11/12. */ public abstract class Duck { Flybehavior flybehavior; QuackBehavior quackBehavior; public Duck(){ } public abstract void display(); public void performFly() { flybehavior.fly(); } public void performQuack(){ quackBehavior.quack(); } public void swim(){ System.out.println("All duck can float"); } }
/** * Created by huangx on 2018/11/12. */ public class MallardDuck extends Duck { // 属性不仅能通过构造函数注入,也可以通过set方法注入,看你心情。 public MallardDuck(){ super.flybehavior=new FlyWithWings(); //这是可飞的鸭子 给他设置会飞的属性 super.quackBehavior=new Quack(); //这是能叫唤的鸭子 给他设置会叫的属性 } public void display(){ System.out.println("i am a real Mallard duck"); } }
/** * Created by huangx on 2018/11/12. */ public class TestDuck { public static void main(String[] args) { Duck mallard=new MallardDuck(); mallard.performFly(); mallard.performQuack(); } }
领导看见jack的这个设计,高兴坏了,立马打电话让财务给jack加工资,并为她介绍女朋友!
这就是策略模式,策略模式的官方定义是他封装了算法族,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
结束了,这是我的第一篇技术blog,还希望以后我能继续坚持写下去!!!