首页 > 代码库 > [设计模式]-策略模式
[设计模式]-策略模式
问题的引出:
实例:从简单的模拟鸭子的应用说起,让我们来抽象一个鸭子超类。并让各种鸭子继承此超类。
具体如下图:
显然这还看不出问题,但如果现在需求改变了(程序员最讨厌就是这个了),好,现在老大说要让所有的鸭子都能飞。恩,机智的你在超类上面加上这个方法就搞定了,但现在问题来了,如果我们有一只橡胶鸭继承了这个超类,它也能飞!!这显然不科学啊。而且它也不是呱呱叫的,是吱吱叫的。这时候我们想到了子类继承的覆盖特性,我们让橡胶鸭的子类覆盖由超类继承来的quack(),并覆盖fly()方法,让它什么都不做,这样我们就又得到了一张图:
这么做有什么坏处呢:我们每次加入一个新的鸭子,我们都要去重写我们已经写过的代码,而且如果老大让我们再在超类加一个方法的话,我们是不是又要判断哪些鸭子没有这个方法了。我们需要大量的去维护代码,这显然很麻烦,我们程序员从不干这么麻烦的事情,让我们试着换一个思路:我们将可能需要判断的方法抽象出来,也就是我们抽象出Flyable(),Quackable()这两个超类,需要的鸭子再去继承,这显然还是无法解决代码复用的问题,我们依旧要去覆盖继承来的方法。
好了我们来总结一下我们遇到的问题:
1.代码在多个子类中重复 2.很难知道所有鸭子的行为
3.运行时不容易去改变,也就是加需求的时候 4.我们改变超类后,可能造成更多的问题
现在我们开始总结设计模式:
“找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要改变的代码混在一起”,这样代码变化引起的不经意后果变少,系统变的更有弹性
好,该是把鸭子的行为从Duck类抽离出来!
首先我们明白一个概念:“针对接口编程”的真正的意思是“针对超类型编程”,”超类型“:通常是抽象类或者一个接口。在这里我们不再将代码绑死在超类,这会让代码更有弹性,这里会涉及到“多态”,“动态链接”,“向上转型”这几个方面,详细的可以见我的下一篇博客,这里就不多讲了,毕竟一次只做好一件事情。
好了我们看下面这张图:
这样设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经和鸭子类无关了。而我们新增的一些行为也不再会影响到既有的行为了。
好了,让我们重新来看看鸭子的超类:
这样我们就可以尝试着写写代码了:(只写了Duck以及FlyBehavior接口以及两个FlyWithWings,FlyNoWay)
public abstract class Duck { FlyBehavior flyBehavior; QuackBehavior quackBehavior; public Duck() { } public abstract void dispaly(); public void performFly() { flyBehavior.fly(); } public void performQuack() { quackBehavior.quack(); } public void swim() { System.out.println("All ducks float, even decoys"); } } public interface FlyBehavior { public void fly(); } public class FlyWithWings implements FlyBehavior { public void fly() { System.out.println("I'm flying"); } } public class FlyNoWay implements FlyBehavior { public void fly() { System.out.println("I can't fly"); } }
最妙的是我们还可以在Duck这个超类上再加两个方法,动态的设置Duck的飞行和呱呱叫的行为:
public void setFlyBehavior(FlyBehavior fb) { flyBehavior = fb; } public void setQuackBehavior(QuackBehavior qb) { quackBehavior = qb; }
那么这里我们又多了一个设计原则:“多用组合,少用继承”,正如你看到的我们通过组合可以使代码更有弹性,更可以“在运行的时候动态的改变行为”。
现在我们再来画一个总体的图:
总结:
策略模式:定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
(转自维基百科)
优缺点:(转自百度百科)