策略模式玩轉步兵,騎士和弓箭手

繼承很不錯



小帥剛畢業就進了一家遊戲公司工作,公司正在開發一款新的即時戰略遊戲,領導讓他設計遊戲裏的兵種及行爲。

在第一個版本里,只有步兵,騎士和弓箭手三個兵種,每個兵種都有移動,停止,攻擊和自愈四種行爲。
在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述

小帥馬上做了個簡單的設計,每個兵種都能移動和停止,不同的兵種有不同的戰鬥方式和回血速度。

於是設計了Unit(兵種)抽象類,實現了move()和stop()方法,各個兵種繼承該超類,然後根據自己的戰鬥特點實現超類的中的fight()和selfHealing()抽象方法。

在這裏插入圖片描述

子類重用的了父類的代碼,又根據自己的特點實現了抽象方法,嗯,小帥覺得很滿意,就在小組中做了分享。

“我覺得還應該加個投石車兵種,攻城利器”,小組成員提議。

“沒問題,加個投石車的子類就行了”,小帥自信地說。

在這裏插入圖片描述
”可是投石車明明不能自愈,卻還要實現父類的selfHealing()方法,不覺得很奇怪嗎?”項目組的老王立馬指出了問題。


接口怎麼樣?



小帥立馬想到了接口,把自愈方法單獨抽離出來,放到一個自愈接口裏,有自愈能力的兵種都繼承該接口。沒自愈能力的投石車,就不用繼承了。
在這裏插入圖片描述

”這樣看上去好多了,但是代碼不能複用,每個子類都要重新實現fight()和selfHealing()方法,以後我們要設計幾十個兵種,每個兵種都得重新實現一遍,這樣太麻煩了。”老王犀利得指出了問題所在。

小帥一時語塞。


針對接口編程,而不是針對實現編程



老王看了看小帥一臉迷茫的樣子,託了託眼鏡,繼續說道:“我們要找出應用中經常變化的部分,把它們獨立出來,和那些不需要變動的代碼區分開來,這是個重要的設計原則。”

“在這個例子中,fight()和selfHealing()方法每個子類的實現都不一樣,屬於經常變化的部分,我們可以設計SelfHealingBeHavior和FightBeHavior接口,把戰鬥行爲和自愈行爲提取出來,這樣就應用了一個設計原則:針對接口編程,而不是針對實現編程。

“這裏我們可以使用策略(Strategy)模式進行設計”,老王已經停不下來了,繼續說道:
策略(Strategy)模式的定義如下:該模式定義了一系列算法,並將每個算法封裝起來,使它們可以相互替換,且算法的變化不會影響使用算法的客戶。

這裏的戰鬥行爲和自愈行爲就是“算法”,我們把算法封裝起來,各種戰鬥和自愈的實現方式都在子類中實現,客戶端只要調用接口就行了,不用管具體是怎麼實現的。

應用了策略模式後的類圖:
在這裏插入圖片描述

說了這麼多,也該上代碼了。

FightBeHavior接口以及實現類

/**
 * 戰鬥接口
 * @author zhanyd
 */
public interface FightBeHavior {
    /**
     * 戰鬥
     */
    void fight();
}
/**
 * 劍戰鬥子類
 * @author zhanyd
 */
public class FightWithSword implements FightBeHavior{
    public void fight() {
        System.out.println("攻擊:看我的劍法");
    }
}
/**
 * 弓箭戰鬥子類
 * @author zhanyd
 */
public class FightWithArrow implements FightBeHavior{
    public void fight() {
        System.out.println("攻擊:我射箭-->");
    }
}
/**
 * 長槍戰鬥子類
 * @author zhanyd
 */
public class FightWithSpear implements FightBeHavior{
    public void fight() {
        System.out.println("攻擊:看我的長槍");
    }
}
/**
 * 石頭戰鬥子類
 * @author zhanyd
 */
public class FightWithStone implements FightBeHavior{
    public void fight() {
        System.out.println("攻擊:我扔石頭");
    }
}

SelfHealingBeHavior接口以及實現類

/**
 * 自愈接口
 * @author zhanyd
 */
public interface SelfHealingBeHavior {
    /**
     * 自愈
     */
    void selfHealing();
}

/**
 * 快速回血子類
 * @author zhanyd
 */
public class SelfHealingFast implements SelfHealingBeHavior{
    public void selfHealing() {
        System.out.println("我每秒回血3%\n");
    }
}
/**
 * 中速回血子類
 * @author zhanyd
 */
public class SelfHealingNormal implements SelfHealingBeHavior{
    public void selfHealing() {
        System.out.println("我每秒回血2%\n");
    }
}
/**
 * 慢速回血子類
 * @author zhanyd
 */
public class SelfHealingSlow implements SelfHealingBeHavior{
    public void selfHealing() {
        System.out.println("我每秒回血1%\n");
    }
}

兵種類

/**
 * 兵種父類
 * @author zhanyd
 */
public class Unit {

    protected String name;

    protected FightBeHavior fightBeHavior;

    protected SelfHealingBeHavior selfHealingBeHavior;

    public void move() {
        System.out.println("我是:" + name);
        System.out.println("向前挺進...");
    }

    public void stop() {
        System.out.println("原地待命...");
    }

    /**
     * 動態設置戰鬥方式
     * @param fi
     */
    public void setFightBeHavior(FightBeHavior fi) {
        fightBeHavior = fi;
    }

    /**
     * 動態設置自愈速度
     * @param se
     */
    public void setSelfHealingBeHavior(SelfHealingBeHavior se) {
        selfHealingBeHavior = se;
    }

    /**
     * 戰鬥
     */
    public void fight() {
        fightBeHavior.fight();
    }

    /**
     * 自愈
     */
    public void selfHealing() {
        selfHealingBeHavior.selfHealing();
    }
}
/**
 * 步兵
 * @author Administrator
 */
public class Infantry extends Unit{
    public Infantry() {
        name = "步兵";
        // 用劍攻擊
        fightBeHavior = new FightWithSword();
        // 回血速度慢
        selfHealingBeHavior = new SelfHealingSlow();
    }
}

/**
 * 弓箭手
 * @author zhanyd
 */
public class Archer extends Unit{
    public Archer() {
        name = "弓箭手";
        // 用弓箭攻擊
        fightBeHavior = new FightWithArrow();
        // 回血速度一般
        selfHealingBeHavior = new SelfHealingNormal();
    }
}
/**
 * 騎士
 * @author zhanyd
 */
public class Knight extends Unit{
    public Knight() {
        name = "騎士";
        // 用長槍攻擊
        fightBeHavior = new FightWithSpear();
        // 回血速度快
        selfHealingBeHavior = new SelfHealingFast();
    }
}
/**
 * 投石車
 * @author zhanyd
 */
public class Catapult extends Unit{
    public Catapult() {
        name = "投石車";
        // 用石頭攻擊
        fightBeHavior = new FightWithStone();
        // 不能回血,不用設置回血功能
    }
}

測試類

public class TestUnit {
    public static void main(String[] args) {
        // 步兵出擊
        Unit unit = new Infantry();
        unit.move();
        unit.fight();
        unit.stop();
        unit.selfHealing();

        // 弓箭手出擊
        unit = new Archer();
        unit.move();
        unit.fight();
        unit.stop();
        unit.selfHealing();

        // 騎士出擊
        unit = new Knight();
        unit.move();
        unit.fight();
        // 騎士扔掉了長槍,拔出了寶劍
        System.out.println("動態設置:騎士扔掉了長槍,拔出了寶劍!");
        unit.setFightBeHavior(new FightWithSword());
        unit.fight();
        unit.stop();
        unit.selfHealing();

        // 投石車出擊
        unit = new Catapult();
        unit.move();
        unit.fight();
        unit.stop();
        // 修理工給投石車做了保養,投石車也能回血了
        System.out.println("動態設置:修理工給投石車做了保養,投石車也能回血了");
        unit.setSelfHealingBeHavior(new SelfHealingSlow());
        unit.selfHealing();
    }
}

測試結果

我是:步兵
向前挺進...
攻擊:看我的劍法
原地待命...
我每秒回血1%

我是:弓箭手
向前挺進...
攻擊:我射箭-->
原地待命...
我每秒回血2%

我是:騎士
向前挺進...
攻擊:看我的長槍
動態設置:騎士扔掉了長槍,拔出了寶劍!
攻擊:看我的劍法
原地待命...
我每秒回血3%

我是:投石車
向前挺進...
攻擊:我扔石頭
原地待命...
動態設置:修理工給投石車做了保養,投石車也能回血了
我每秒回血1%

“setFightBeHavior(FightBeHavior fi)方法和setSelfHealingBeHavior(SelfHealingBeHavior se)方法,能在程序運行中動態的切換實現的算法,這是策略模式的一大優點。“,老王補充道。

小帥看着代碼,陷入了沉思,真是越看越有味道。

“原來FightBeHavior接口就是兵器庫(算法庫),以後不管用到什麼其他的戰鬥方式,只要在實現類裏增加就行了,就像在兵器庫(算法庫)裏增加兵器(算法),比如:雙節棍,大刀,流星錘等等,如果有新的兵種直接拿來用就行了,實現了類的複用。

戰鬥方式可以隨時替換,並且不會影響到具體兵種的使用。“小帥貌似明白了策略模式的精髓所在,顯得有點興奮。

老王點點頭,開心的笑了,“就是這樣啊,你已經學會策略模式了”。

代碼鏈接









在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章