Java设计模式之策略模式详解

策略模式就是定义了一系列的的算法,将它们都单独封装起来,让他们之间可以相互替换,可以让算法的变化独立于使用算法的客户。

首先创建一个Dog父类,有run方法控制跑,jump方法控制跳,color方法控制颜色。

public class Dog {
    public void run(){
        System.out.println("狗在跑");
    }

    public void jump(){
        System.out.println("狗在跳");
    }

    public void color(){
    }
}

创建两个子类,分别是WhiteDog和BlackDog,都重写了父类的color方法,运行一下。

public class WhiteDog extends Dog {

    @Override
    public void color() {
        System.out.println("白颜色的狗");
    }
}

public class BlackDog extends Dog {

    @Override
    public void color() {
        System.out.println("黑颜色的狗");
    }
}

public class Test {
    public static void main(String[] args){
        WhiteDog whiteDog = new WhiteDog();
        whiteDog.run();
        whiteDog.jump();
        whiteDog.color();
        BlackDog blackDog = new BlackDog();
        blackDog.run();
        blackDog.jump();
        blackDog.color();
    }
}

结果:
狗在跑
狗在跳
白颜色的狗
狗在跑
狗在跳
黑颜色的狗

这个时候看上去好像很完美没有任何问题,但是有一天我们又新建了一个公仔狗的对象Doll dog,这个时候我们发现好像有点不对,公仔狗是不会跳也不会跑的。可以如果继承了Dog父类他就自带跑和跳的功能了,那就不对了。
public class DollDog extends Dog {

@Override
public void color() {
    System.out.println("五颜六色");
}

}

DollDog dollDog = new DollDog();
dollDog.run();
dollDog.jump();
dollDog.color();

结果:
狗在跑
狗在跳
五颜六色的玩具狗

这个时候我们灵机一动想到可以让子类覆盖父类run方法和jump方法,让DollDog中的跑和跳功能失效,我们再运行一下。

@Override
public void run() {
    System.out.println("玩具狗不会跑");
}

@Override
public void jump() {
    System.out.println("玩具狗不会跳");
}

结果:
玩具狗不会跑
玩具狗不会跳
五颜六色的玩具狗

看起来是解决了问题,但是如果一天要增加100个各种各样的狗的话难道我们要让100个新建的子类都重写父类的方法吗?这个时候父类里面还只有3个方法,如果是30个呢?如果我们都靠子类重写的话那效率该多低呢?有没有别的方法来解决呢?答案是肯定的。下面我们就来介绍怎么用策略模式来解决这个问题。
首先我们要知道设计模式中第一个原则,要把代码中经常需要修改的部分独立抽取出来,不要和其他代码混在一起,这样更便于我们扩展要修改的部分 。目前来看最常变化的是run和jump方法。所以我们可以将这两个方法抽取出来,这里就要说到设计模式中第二个原则,针对接口编程,而不是现实编程。比如run和jump有不同的种类,我们可以声明一个接口里面定义run和jump方法,然后创建许多类去实现,调用的时候动态选择类型。这种类被称为行为类。行为类中的代码可以进行复用,却不会有继承带来的那些麻烦。

定义方法的接口:

public interface RunBehavior {
    void run();
}

public interface JumpBehavior {
    void jump();
}

实现方法的行为类:

public class RunNoWay implements RunBehavior {
    @Override
    public void run() {
        System.out.println("不会跑");
    }
}

public class RunFast implements RunBehavior {
    @Override
    public void run() {
        System.out.println("很快的跑");
    }
}

public class RunSlow implements RunBehavior {
    @Override
    public void run() {
        System.out.println("很慢的跑");
    }
}

public class JumpNoWay implements JumpBehavior {
    @Override
    public void jump() {
        System.out.println("不会跳");
    }
}

public class JumpFast implements JumpBehavior {
    @Override
    public void jump() {
        System.out.println("很快的跳");
    }
}

现在我们将变化的部分抽取出来了,所以Dog父类就会把run和jump的操作委托给行为类处理,那么具体要怎么使用这些行为类?这里就需要在Dog父类中定义两个实例变量,声明类型为RunBehavior和JumpBehavior,所以在代码运行的时候会用多态的方式引用正确的类型。然后在父类中的run和jump方法中委托行为类去执行功能。

public class Dog {
    public RunBehavior runBehavior;
    public JumpBehavior jumpBehavior;

    public void run(){
        runBehavior.run();
    }

    public void jump(){
        jumpBehavior.jump();
    }

    public void color(){
    }
}

最后当我们没出现一个新的类型的狗狗的时候,我们为它创建一个新类然后继承Dog父类,然后我们在子类的构造方法中获取父类中两个接口的引用,根据自己的需要通过多态指定不同的行为类。

public class SuperDog extends Dog {

    public SuperDog(){
        runBehavior = new RunFast();
        jumpBehavior = new JumpFast();
    }

    @Override
    public void color() {
        System.out.println("红蓝相间的超人狗");
    }
}

Dog dog = new SuperDog();
dog.jump();
dog.run();
dog.color();

结果:
很快的跳
很快的跑
红蓝相间的超人狗

最后我们还有一个小问题,每次指定选择类的时候都是在子类的构造方法中指定,可不可以动态的指定呢?当然可以,我们只需要为声明的接口引用添加两个set方法。然后在外部调用即可。

public void setRunBehavior(RunBehavior runBehavior) {
    this.runBehavior = runBehavior;
}

public void setJumpBehavior(JumpBehavior jumpBehavior) {
    this.jumpBehavior = jumpBehavior;
}

Dog dog = new SuperDog();
dog.jump();
dog.run();
dog.color();

dog.setJumpBehavior(new JumpNoWay());
dog.setRunBehavior(new RunNoWay());
dog.jump();
dog.run();
dog.color();

结果:
很快的跳
很快的跑
红蓝相间的超人狗
不会跳
不会跑
红蓝相间的超人狗

总结:
策略模式就是把所有的可变的行为都抽取出来放到接口中,然后定义很多的行为类去实现接口。在父类中声明了接口的引用利用多态去动态的选择自己需要的行为类,避免了以前因为单纯的继承造成的每次的新变动都需要写大量的重复代码,而现在只需要定义好行为类进行复用即可,不需要修改原本的代码。

    原文作者:设计模式
    原文地址: https://segmentfault.com/a/1190000014350915
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞