策略模式

xiaoxiao2021-02-28  136

策略模式

策略模式也是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{ @Override public void fly() { System.out.println("我会飞!!!"); } }FlyNoWay.java代码: public class FlyNoWay implements FlyBehavior{ @Override public 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{ @Override public 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(); } } 控制台输出结果:

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

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

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

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

转载请注明原文地址: https://www.6miu.com/read-20569.html

最新回复(0)