资讯专栏INFORMATION COLUMN

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

DevTalking / 746人阅读

摘要:下面我们就来介绍怎么用策略模式来解决这个问题。结果很快的跳很快的跑红蓝相间的超人狗不会跳不会跑红蓝相间的超人狗总结策略模式就是把所有的可变的行为都抽取出来放到接口中,然后定义很多的行为类去实现接口。

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

首先创建一个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://www.ucloud.cn/yun/69045.html

相关文章

  • 后台开发常问面试题集锦(问题搬运工,附链接)

    摘要:基础问题的的性能及原理之区别详解备忘笔记深入理解流水线抽象关键字修饰符知识点总结必看篇中的关键字解析回调机制解读抽象类与三大特征时间和时间戳的相互转换为什么要使用内部类对象锁和类锁的区别,,优缺点及比较提高篇八详解内部类单例模式和 Java基础问题 String的+的性能及原理 java之yield(),sleep(),wait()区别详解-备忘笔记 深入理解Java Stream流水...

    spacewander 评论0 收藏0
  • 后台开发常问面试题集锦(问题搬运工,附链接)

    摘要:基础问题的的性能及原理之区别详解备忘笔记深入理解流水线抽象关键字修饰符知识点总结必看篇中的关键字解析回调机制解读抽象类与三大特征时间和时间戳的相互转换为什么要使用内部类对象锁和类锁的区别,,优缺点及比较提高篇八详解内部类单例模式和 Java基础问题 String的+的性能及原理 java之yield(),sleep(),wait()区别详解-备忘笔记 深入理解Java Stream流水...

    xfee 评论0 收藏0
  • 后台开发常问面试题集锦(问题搬运工,附链接)

    摘要:基础问题的的性能及原理之区别详解备忘笔记深入理解流水线抽象关键字修饰符知识点总结必看篇中的关键字解析回调机制解读抽象类与三大特征时间和时间戳的相互转换为什么要使用内部类对象锁和类锁的区别,,优缺点及比较提高篇八详解内部类单例模式和 Java基础问题 String的+的性能及原理 java之yield(),sleep(),wait()区别详解-备忘笔记 深入理解Java Stream流水...

    makeFoxPlay 评论0 收藏0
  • 后端ing

    摘要:当活动线程核心线程非核心线程达到这个数值后,后续任务将会根据来进行拒绝策略处理。线程池工作原则当线程池中线程数量小于则创建线程,并处理请求。当线程池中的数量等于最大线程数时默默丢弃不能执行的新加任务,不报任何异常。 spring-cache使用记录 spring-cache的使用记录,坑点记录以及采用的解决方案 深入分析 java 线程池的实现原理 在这篇文章中,作者有条不紊的将 ja...

    roadtogeek 评论0 收藏0

发表评论

0条评论

DevTalking

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<