記一場精彩的籃球比賽——淺談策略模式

策略模式

聲明:本文爲原創,若有轉載請註明轉載與原做者並提供原文連接,僅爲學習交流,本人才識短淺,若有錯誤,敬請指正

雖然我本人比較討厭一些很官方的術語定義,由於我常常弄不明白有些定義講了個啥,可是爲了讓這篇博文顯得不那麼輕浮,因此我也就不能免俗的先將設計模式之策略模式的定義首先丟到各位看官面前。算法

策略模式定義了算法族,分別封裝起來,讓他們之間能夠互相替換,此模式讓算法的變化獨立於使用算法的客戶。

第一眼這個定義看上去,不免會讓人心生膽怯,又是算法族,又是封裝,又是替換,彷佛很複雜的樣子,可是實際上,很好理解,不信,咱們來一塊兒看一場很是激烈的籃球比賽吧,相信看完這場比賽以後你就能大體掌握這個設計模式了。設計模式

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,咱們下次再見。

相關文章
相關標籤/搜索