记一场精彩的篮球比赛——浅谈策略模式

策略模式

声明:本文为原创,若有转载请注明转载与原做者并提供原文连接,仅为学习交流,本人才识短浅,若有错误,敬请指正

虽然我本人比较讨厌一些很官方的术语定义,由于我常常弄不明白有些定义讲了个啥,可是为了让这篇博文显得不那么轻浮,因此我也就不能免俗的先将设计模式之策略模式的定义首先丢到各位看官面前。算法

策略模式定义了算法族,分别封装起来,让他们之间能够互相替换,此模式让算法的变化独立于使用算法的客户。

第一眼这个定义看上去,不免会让人心生胆怯,又是算法族,又是封装,又是替换,彷佛很复杂的样子,可是实际上,很好理解,不信,咱们来一块儿看一场很是激烈的篮球比赛吧,相信看完这场比赛以后你就能大体掌握这个设计模式了。设计模式

PS:有人确定要问了,为啥没有个UML图呢,其余人说设计模式都会咣当扔个UML图上来,你咋没有呢,是否是偷工减料,其实啊,真不是,在于我这我的呢,比较喜欢直接撸代码看代码,也没有说看UML图理解一个设计模式的,并不说UML图不重要,只是策略模式的UML图在网上一搜一大把,我仍是不要当搬运工,复制粘贴了吧。ide

欢迎你们来到NBA赛场,今天的比赛呢,双方分别是湖人和骑士,科比与詹姆斯的宿命对决,做为篮球运动员,在场上,最重要的两件事是啥呢,没错,就是“投篮”和“传球”,投篮是为了得分,传球是为了更轻松的投篮得分,投篮有不少种方式,后仰投篮,三分远投,急停跳投,传球包括击地传球,不看人传球等,而在Java的设计模式中,咱们能够把这些通通定义成“方法”。学习

咱们首先抽象出来一个投篮相关的接口,它包含了一个方法:shoot(),即投篮this

public interface ShootStrategy {

    public void shoot();
}

一样的,咱们抽象出来一个传球相关的接口,它包含了一个方法:pass(),即传球spa

public interface PassStrategy {

    public void pass();
}

那么这两个接口有什么用呢,回到策略模式的定义,注意“算法族”这三个字,什么是算法族,若是后仰投篮是一个算法,三分远投是一个算法,急停跳投也是一个算法,咱们就会发现他们的共同点是,他们都是投篮的算法,也就是说他们都是投篮的算法族,同理,击地传球与不看人传球也是传球的算法族,然后策略模式的定义说要分别封装他们,提到封装你们想到了什么呢,类!设计

接下来咱们尝试封装一下后仰投篮算法和三分远投算法3d

public class BackwardShoot implements ShootStrategy {

    @Override
    public void shoot() {
        System.out.println("标志性的后仰跳投");
    }
}

封装后仰跳投算法,代码很简单,由于咱们不但愿算法的逻辑干扰到咱们着眼于真正的重点——策略模式。code

public class ThreeShoot implements ShootStrategy {

    @Override
    public void shoot() {

        System.out.println("神准的三分");
    }
}

封装三分远投算法。对象

一样的,咱们也封装一下传球的算法,毕竟篮球离不开传球(科比:传球,传球是什么意思,误~)

public class DropPass implements PassStrategy {

    @Override
    public void pass() {
        System.out.println("诡异的击地传球");
    }
}

封装击地传球算法

public class NoLookPass implements PassStrategy {

    @Override
    public void pass() {
        System.out.println("一记精彩的不看人传球");
    }
}

封装不看人传球算法

好了,如今咱们全部的策略都准备好了(原谅我这里直接使用了策略,不用惊吓,咱们刚刚已经完成了策略模式最重要的部分),就差咱们的运动员上场了。

咱们首先定义一个篮球运动员的类。

public class BasketballPlayer {

    private String name;

    private ShootStrategy shootStrategy;
    private PassStrategy passStrategy;

    public void shoot(){
        System.out.println("------" + name + "------");
        shootStrategy.shoot();
    }

    public void pass(){
        System.out.println("------" + name + "------");
        passStrategy.pass();
    }

    public void setShootStrategy(ShootStrategy shootStrategy) {
        this.shootStrategy = shootStrategy;
    }

    public void setPassStrategy(PassStrategy passStrategy) {
        this.passStrategy = passStrategy;
    }

    public void selfIntroduction(){
        System.out.println("我是个篮球运动员");
    }

    public void setName(String name) {
        this.name = name;
    }
}

看一下这段代码,我定义了三个变量,name,表示运动员的名字,而后是我本身定义的投篮接口以及传球接口,这俩干干巴巴,麻麻赖赖的接口放这里有啥用呢,盘他,咱们往下看,咱们会发现,篮球运动员的投篮,使用的是咱们投篮接口里的shoot()方法实现,而传球呢,使用的是咱们传球接口里的pass()方法实现,有人就会想了,我这俩接口都没方法体,调用个毛啊,稍安勿躁,继续往下看,下面是两个平平无奇的setter方法,然而,奥秘就是在这里,这个奥秘,咱们通常叫他“解耦”,经过使用Java的接口回调,咱们能够将任意对象传入这个类中供这个类使用,只要这个类实现了对应的接口便可,在这里就是投篮接口与传球接口,而后咱们就会发现,刚才咱们实现的后仰投篮,三分远投等,这里通通受用!

说了之久,咱们的大明星怎么还没露面呢,继续

科比是一名(is-a)篮球运动员,因此

public class Kobe extends BasketballPlayer {

    @Override
    public void selfIntroduction(){
        System.out.println("个人名字是科比");
    }
}

詹姆斯是一名(is-a)篮球运动员,因此

public class James extends BasketballPlayer {

    @Override
    public void selfIntroduction(){
        System.out.println("我叫詹姆斯");
    }
}

注意,虽然两个类代码比较少,可是不要忘了,因为他俩继承了篮球运动员类,因此篮球运动员里的那些方法,他们也都拥有。

好啦,如今万事俱备,让咱们开始这一场激动人心的比赛吧

public class LakerVsCavalier {

    public static void main(String[] args) {

        Kobe kobe = new Kobe();
        kobe.selfIntroduction();
        kobe.setName("科比");

        James james = new James();
        james.selfIntroduction();
        james.setName("詹姆斯");

        kobe.setPassStrategy(new NoLookPass());
        kobe.setShootStrategy(new BackwardShoot());
        kobe.pass();
        kobe.shoot();

        james.setPassStrategy(new DropPass());
        james.setShootStrategy(new ThreeShoot());
        james.pass();
        james.shoot();

        kobe.setPassStrategy(new DropPass());
        kobe.setShootStrategy(new ThreeShoot());
        kobe.pass();
        kobe.shoot();

        james.setPassStrategy(new NoLookPass());
        james.setShootStrategy(new BackwardShoot());
        james.pass();
        james.shoot();
    }
}

 

虽然代码看起来很简单,只是让科比与詹姆斯两个对象shoot()与pass()而已,然而事情其实并无那么简单,回顾策略模式的定义,“让它们之间能够互相替换”,这就是问题的关键,咱们为同一个对象设置了不一样的算法,科比能够先设置传球策略为不看人传球,设置投篮策略为后仰跳投,而后此封装的算法就会被科比对象中的pass()和shoot()方法调用,缘由上文已述,而后,咱们能够从新为科比对象设置不一样的算法,咱们把传球策略为击地传球,投篮策略设置为三分远投,而后科比再次传球(调用pass方法),就变成了击地传球,再次投篮(调用shoot方法),就变成了三分远投,詹姆斯对象同理。

咱们来看一下执行结果

一切皆如咱们所料。

至此,咱们也就能理解策略模式定义的最后一句:此模式让算法的变化独立于使用算法的客户。

更重要的一点是,经过策略模式,咱们真正的将易于变化的部分抽出来,使代码对修改关闭,对扩展开放,假设咱们要添加一个新的策略——急停跳投,咱们只须要经过实现投篮接口的方式新建一个急停跳投策略,而后把他set进科比,詹姆斯这种使用算法的对象便可,彻底不用修改已有的调用算法的代码!

有兴趣的能够本身去实现看看哦

今天的篮球比赛转播到此结束,我是设计模式评论员JR,咱们下次再见。

相关文章
相关标签/搜索