策略模式

来源:互联网 发布:linux花屏 卡死 编辑:程序博客网 时间:2024/05/21 03:19

策略模式

策略模式也是23种设计模式之一。

策略模式是指将程序中可变部分抽象分离成一系列的算法,并将每一个算法封装起来,而且使他们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

策略模式一般由下面三部分组成:

1. 抽象策略角色: 策略类,通常由一个接口或者抽象类实现。 

2. 具体策略角色:包装了相关的算法和行为。 
3. 环境角色:持有某一个策略类的引用,客户端调用。 

策略模式设计原则: 
1. 把程序中需要变化的部分抽离出来,独立于不变的部分。 
2. 面向接口编程,而不是面向实现编程,多用组合,少用继承。

(组合 :在类中增加一个私有域,引用另外一个已经有的类的实例,通过调用实例的方法从而获得新的功能)


我们以鸭子(Duck)做一个例子。

鸭子有哪些行为呢?

行为:游泳,飞。

所有的鸭子都会游泳我们是知道的,但是所有的鸭子都会飞嘛?

真鸭子会飞,那假鸭子呢?

答案是否定的,依照我们策略设计模式的原则,我们需要把程序中需要变化的部分抽离出来,也就是要把鸭子会飞的行为抽离出来。

在抽离之前,我们先来把不需要分离的部分写出来。

鸭子都会游泳,是一个共有的行为,那么我们把游泳的行为写进抽象鸭子类(Duck)中。

Duck.java代码:

public abstract class Duck {public void swing(){System.out.println("我会游泳!!!");}public void play(){swing();}}
共有的行为写完了,那么我们就来写飞的行为,因为有些鸭子会飞,有些鸭子不会飞,所以飞行行为的实现是不同的。

我们创建一个FlyBehavior的接口,让子类去实现不同的行为。

FlyBehavior.java代码:

public interface FlyBehavior {public void fly();}
然后我们再创建两个类来继承这个接口,实现不同的飞行行为。

FlyWithWing.java代码:

public class FlyWithWing implements FlyBehavior{@Overridepublic void fly() {System.out.println("我会飞!!!");}}
FlyNoWay.java代码:
public class FlyNoWay implements FlyBehavior{@Overridepublic void fly() {System.out.println("很遗憾,我飞不起来。。。");}}

这下子,我们把鸭子飞的行为也写完了,那么我们是不是需要把它们组合起来呢。

再次申明下组合的概念:

组合 :在类中增加一个私有域,引用另外一个已经有的类的实例,通过调用实例的方法从而获得新的功能。

我们需要在Duck类中增加一个私有域,引用FlyBehavior的实例,通过调用它的fly();方法来获得相应的飞行能力。

我们重新修改下Duck类。

修改后的Duck.java代码:

public abstract class Duck {private FlyBehavior flyBehavior;public void setFlyBehavior(FlyBehavior flyBehavior) {this.flyBehavior = flyBehavior;}public void fly(){flyBehavior.fly();}public void swing(){System.out.println("我会游泳!!!");}public void play(){swing();fly();}}
flyBehavior属性就是引用的那个私有域,我们为这个属性写上set方法的原因是为了之后能够更好的根据我们的需求而变化。

在fly();方法里面我们调用了flyBehavior的fly();方法,这样我们通过实例化FlyBehavior接口的不同子类来实现flyBehavior.fly()的动态改变。

接下来我们写一个真鸭子的子类来继承抽象Duck类。

RealDuck.java代码:

public class RealDuck extends Duck{public RealDuck(){super.setFlyBehavior(new FlyWithWing());}}
我们把假鸭子的代码也写出来。

RubberDuck.java代码:

public class RubberDuck extends Duck{public RubberDuck(){super.setFlyBehavior(new FlyNoWay());}}
这样下来,我们的真鸭子和假鸭子都通过自己的构造方法为flyBehavior属性实例化了不同的飞行行为。

我们用真鸭子为例,来测试下,新建一个DuckTest类。

DuckTest.java代码:

public class DuckTest {public static void main(String[] args){RealDuck realDuck=new RealDuck();realDuck.play();}}
控制台输出结果:

我会游泳!!!我会飞!!!

我们不需要知道飞行行为的具体实现,我们只需要根据自己的需求实例化不同的FlyBehavior接口子类就OK了。

通过这个实现,我们让可变的飞行行为独立于鸭子类本身,这样可便于后期的维护和拓展。


比如我们现在想写一只超级鸭子,它具有火箭推动的飞行能力,在我们之前的FlyBehavior接口子类中并没有这个飞行行为。

那么我们只需要重新写一个火箭推动的飞行类即可。继承FlyBehavior接口。

FlyWithRocketPower.java代码:

public class FlyWithRocketPower implements FlyBehavior{@Overridepublic void fly() {System.out.println("我用火箭来推动我飞行!!!");}}
火箭推动的飞行行为我们写好了,然后再写一只超级鸭子即可。

SuperDuck.java代码:

public class SuperDuck extends Duck{public SuperDuck() {super.setFlyBehavior(new FlyWithRocketPower());}}
我们再来测试一下超级鸭子,修改下测试类就好了。

修改后的DuckTest.java代码:

public class DuckTest {public static void main(String[] args){SuperDuck superDuck=new SuperDuck();superDuck.play();}}
控制台输出结果:

我会游泳!!!我用火箭来推动我飞行!!!


通过以上实例,我们将具有不同行为实现的飞行行为与鸭子基类分离,让它独立于鸭子类而变化。

这让我们在后期需求发生变化或者要求新的功能时,大大减少我们的代码量,我们只需要实现相应的接口即可。

让我们面向接口编程。
这就是策略模式!