首页 > 代码库 > [设计模式]-策略模式

[设计模式]-策略模式

问题的引出:

      实例:从简单的模拟鸭子的应用说起,让我们来抽象一个鸭子超类。并让各种鸭子继承此超类。

具体如下图:


显然这还看不出问题,但如果现在需求改变了(程序员最讨厌就是这个了),好,现在老大说要让所有的鸭子都能飞。恩,机智的你在超类上面加上这个方法就搞定了,但现在问题来了,如果我们有一只橡胶鸭继承了这个超类,它也能飞!!这显然不科学啊。而且它也不是呱呱叫的,是吱吱叫的。这时候我们想到了子类继承的覆盖特性,我们让橡胶鸭的子类覆盖由超类继承来的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;
}

那么这里我们又多了一个设计原则:“多用组合,少用继承”,正如你看到的我们通过组合可以使代码更有弹性,更可以“在运行的时候动态的改变行为”。


现在我们再来画一个总体的图:


总结

       策略模式:定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户

                                                                           (转自维基百科)


优缺点:(转自百度百科)

优点:
1、 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。
2、 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。
3、 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。
缺点:
1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
2、 策略模式造成很多的策略类,每个具体策略类都会产生一个新类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。