设计模式——策略模式

        最近在学习设计模式,看那本HeadFirst的设计模式,讲的很是生动,详细讲解了几个常用的设计模式,对与入门新手,收益良多,这系列文章,总结一下我的学习成果,基于代码实现还有我个人的一个理解。

        这篇博客,谈谈策略模式。

        先简述一下,这篇文章思路,是以一个duck类为主体,不同的duck子类有不同的行为,也有相同的行为,并且可以给所有duck子类统一添加行为,也可以给某一个子类特定添加或修改某一行为。

        对于开发经验不足的人来说,首先我们会想到定义一个父类,然后子类继承或重写父类公共方法,并添加自己特定的方法。代码如下:

public abstract class Duck {
    //所有鸭子都会叫,都会游泳,所以超类直接实现这部分代码
    public void quack(){
        System.out.println("I can quack guaguagua");
    }
    public void swim(){
        System.out.println("I can swim");
    }
    //不同的鸭子形态不同,所以为抽象方法,子类自己实现
    public abstract void display();
}
public class GreenheadDuck extends Duck {
    @Override
    public void display() {
        System.out.println("I have green head");
    }
}
public class RedheadDuck extends Duck {
    @Override
    public void display() {
        System.out.println("I have red head");
    }
}

    很容易就实现了,毫无压力。

    但是现在需求更改了,要求鸭子要会飞。很多人会想到给超类添加fly()方法。但是如果不是让所有鸭子都会飞呢。比如一个橡皮鸭(RubberDuck),其他鸭子都会飞,但是它不能会飞。oh,对,可以在RubberDuck类中,把父类的fly()重写掉,里面没有任何的实现代码。是个方法.

但是如果需求改为添加一个木头鸭子,它不能飞还不能叫,怎么办?重写fly()和quack()?

    这显然不是好的解决方案。

    tip1:利用接口怎么样?有一个flyable接口,一个quackable接口,会叫的会飞的分别实现对应接口。这样可以实现我们的预期功能。当你真正去实现这个想法的时候,你发现,当你有40个子类的时候,其中三十个的飞行方式都是一样的,但是你就得重写三十次飞行方法。这种做法是可笑的。

    程序设计时,有一个设计原则,可以让你找到思路:

找出应用中可能要变化的地方,把它们独立出来,不要和不会变化的代码混合。

现阶段的需求中,可能变化的部分是鸭子叫的方式,飞的方式。为了把这两个行为从类中分离出来,我们新建一组新类,来代表每一个新的行为,如何设计呢?另一个设计原则会给我们思路:

针对接口编程,不要针对实现编程。

我们会设计多个接口,用来实现各种行为,然后特定的行为,不同的实现对应接口里的方法。与之前不同的是,这次是特定的行为类,在鸭子子类中,我们会通过一个变量实例化特定行为类,调用行为类中的方法,用来实现不同鸭子子类的不同行为。像这样(只演示飞行):

public interface FlyBehavior {
    void fly();
}
public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("I can fly");
    }
}
public class FlyWithNothing implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("I can't fly");
    }
}

接下来的问题在于怎么在duck类中调用不同的行为方法。因为我们的思路是所有的子类都调用fly,只是不能飞行的鸭子实现代码中没有内容。所以我们可以在超类中添加如下代码:

FlyBehavior flyBehavior;

public void performQuack(){
    flyBehavior.fly();
}
我们用performQuack()方法替代fly()方法;这样调用方法的过程委托给了行为类,
而且可以再运行时自主调用performQuack()方法。
public interface FlyBehavior {
    void fly();
}

我们需要在子类实现中添加下面代码,用来确定特定行为类

    //红鸭子会飞,所以实例化FlyWithWings类 

public RedheadDuck(){
    flyBehavior=new FlyWithWings();
}
//橡皮鸭子不会飞,所以实例化FlyWithNothing类
public RubberDuck(){
    flyBehavior=new FlyWithNothing();
}
 
 

测试代码:

public static void main(String[] args) {
   Duck redduck=new RedheadDuck();
redduck.performQuack();
redduck.swim();
redduck.display();

Duck rubberDuck=new RubberDuck();
rubberDuck.performQuack();
rubberDuck.swim();
rubberDuck.display();
}
结果:
I can fly
I can swim
I have red head
I can't fly
I can swim
I have Rubber head

有些经验的人会发现,我们平时的类不希望它所有的方法都是写死的,尤其在我们提取出的随时可能会改变的部分,我们更希望在运行时,由我们自己决定类中方法。所以我们在子类中直接实例化行为类是不可取的。

我们在超类中添加行为接口的get set方法

public FlyBehavior getFlyBehavior() {
    return flyBehavior;
}

public void setFlyBehavior(FlyBehavior flyBehavior) {
    this.flyBehavior = flyBehavior;
}
这样我们在运行时如果想要改变某个子类的行为,可以直接设定
Duck redduck=new RedheadDuck();
redduck.performQuack();
//动态改变红头鸭子行为,使其不能飞行
redduck.setFlyBehavior(new FlyWithNothing());
redduck.performQuack();
redduck.swim();
redduck.display();
结果:

I can fly

I can't fly

I can swim

I have red head


经过上面一系列历程,我们解决的面对的一些问题,可以自由的增添子类,动态的改变特定方法。让我们回顾一下我们这次程序设计的一些亮点。

1.我们把可能会改变的每种鸭子的行为用一个行为类进行了封装,不会改变的方法在超类中进行了实现。当一种鸭子的行为要发生改变的时候,我们可以在运行时动态的用另一个行为类进行替换;——这也是策略模式的中心思想。

2.我们针对接口编程

    所谓针对接口,并不是真正意义上的interface,可以使一个抽象类。重点使用多态就对了。

本例中,我们Duck redduck=new RedheadDuck();   redduck.performQuack();

这样你不用管performQuack里面是什么东西,你只需要知道,是一个鸭子,就要调用这个方法就够了。

之前我们会 RedheadDuck redheadDuck=new RedheadDuck(); redheadDuck.fly();

甚至我们不需要在类中硬编码实例化新的子类,可以 duck=getDuck(); duck.performQuack();这样在运行时具体制定要实现的对象。

3.我们使用组合,而非继承。

    所谓组合就是将两个或者更多的类在一个类中使用,在当前类中调用其他类的方法,而不是继承来所有的方法。这样可以使程序更具有弹性,随时调用你想用的任意方法。而“tip1”中的思想相当于每个类都实现一个接口,重写其中方法,这使得程序变得难以修改,而我们在超类中

FlyBehavior flyBehavior;

public void performQuack(){

    flyBehavior.fly();
}
 
 

然后在子类中确定FlyBehavior的具体实现类,相当于调用了另一个类的fly()方法。这样我们不仅可以将fly()中的代码封装起来,更可以在运行时动态的改变行为。

再看看策略模式的定义:定义一个算法族(指一系列的算法),分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

本例中的代码都很简单,但设计模式是思想,学习的路还很远~

猜你喜欢

转载自blog.csdn.net/wy9717/article/details/80109126