繼承很不錯
小帥剛畢業就進了一家遊戲公司工作,公司正在開發一款新的即時戰略遊戲,領導讓他設計遊戲裏的兵種及行爲。
在第一個版本里,只有步兵,騎士和弓箭手三個兵種,每個兵種都有移動,停止,攻擊和自愈四種行爲。
小帥馬上做了個簡單的設計,每個兵種都能移動和停止,不同的兵種有不同的戰鬥方式和回血速度。
於是設計了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接口就是兵器庫(算法庫),以後不管用到什麼其他的戰鬥方式,只要在實現類裏增加就行了,就像在兵器庫(算法庫)裏增加兵器(算法),比如:雙節棍,大刀,流星錘等等,如果有新的兵種直接拿來用就行了,實現了類的複用。
戰鬥方式可以隨時替換,並且不會影響到具體兵種的使用。“小帥貌似明白了策略模式的精髓所在,顯得有點興奮。
老王點點頭,開心的笑了,“就是這樣啊,你已經學會策略模式了”。