奇幻RPG 物品鍛造 與 Decorator模式

2021-07-03 14:00:10 字數 3728 閱讀 6210

物品鍛造是各類奇幻遊戲中的常見功能,就拿眾所周知的diablo來說吧。假設角色擁有一把單手劍,可能基礎攻擊力只有13,但是它有三個裝備孔。當給劍鑲嵌一顆藍寶石的時候,它就擁有了額外的冰凍效果並多加2點攻擊力;當給劍鑲嵌一顆紅寶石的時候,它又擁有了額外的火焰傷害並多加3點攻擊力;當給劍鑲嵌一顆綠寶石的時候,它又擁有了額外的中毒傷害並多加的4點攻擊力。當然,也可以三個孔都鑲嵌同一色的寶石。本文將說明如何使用decorator模式來完成這樣的設計。

我們首先想到應該有個基類 weapon,它供所有各式各樣的**繼承,比如說sword、axe、bow。description欄位代表**的說明,比如「one-hand light sword」,damage()方法則用於獲取**的傷害,getdescription用於獲取**的說明。在不考慮寶石的情況下,我們得到下面的設計:

現在我們考慮如何建立鑲嵌有寶石的**。我們首先考慮到可以用繼承來實現這樣的設計,結果卻發現如果我們需要定義所有嵌寶石的劍(sword),就需要3+6+7 = 16個類(note:三個物品孔,每個孔都有 藍、紅、綠 三種選擇,可以兩個或者三個孔同一色),如果我們給鑲嵌了兩顆藍一顆紅寶石的劍命名為 blue2redsword,給三色不同不劍命名為blueredgreensword,其餘的類推。那麼,我們會得到下面這樣龐大的類體系(只繪製了部分):

而這僅僅是開始,如果我們需要再添一種寶石,比如說白色,它可以附加詛咒的效果;或者我們需要給**再新增乙個物品孔,那麼我們的類的數目將迅速的由十幾個變成幾十個。

我們發現使用繼承的問題:使用繼承時將會建立出大量的類。除此以外,使用繼承,也意味我們需要例項化乙個特定的子類以獲取我們需要的功能(方法),這在編譯階段(compile time)就已經確定,類的客戶端不能控制何時(run time)根據需要改變,除非再例項化另乙個子類。

我們發現繼承會帶來兩個主要的問題,所以我們考慮換一種方式來思考,我們可以使用復合來完成它。說詳細一點,就是我們將藍寶石(bluediamond)、紅寶石(reddiamond)、綠寶石(greendiamond) 作為實體變數(instance variable)復合到基類中,然後在基類的damage()方法中計算出所有寶石額外增加的傷害(此時基類的damage()方法不再是抽象的)。

public abstract class weapon

}而在實體子類中,我們覆蓋這個方法,在方法內部先呼叫基類方法獲取寶石的附加傷害,然後再給它加上**本身的傷害。

public class sword: weapon

}此時的圖應該變成這樣:

相對於繼承,復合看上去要好得多,它的類的數目要少的多,並且又可以在執行時決定是否給**鑲嵌寶石,但是使用復合仍存在問題:

我們遺忘了一種組合,應該記得,我們的劍是可以鑲嵌三個同色寶石的,比如說:三個藍寶石或者 三個紅寶石,那麼上面的設計顯然無法完成。當然,我們可以從三種寶石中抽象出乙個diamond基類來,而在weapon中新增三個diamond型別的變數。但是,問題依然存在:如果我們需要多添乙個裝備孔,那麼我們又得再次修改weapon類。

現在假設我們不是一名軟體設計者,而是乙個遊戲玩家,我們要為劍新增一枚紅寶石,一枚藍寶石,那麼實際的操作順序是什麼呢?

我們當然首先要有一把劍。(需要先建立乙個sword物件,它只是把劍,不含任何寶石)。

我們為劍新增乙個紅寶石。(我們包裝sword物件,給它新增3點傷害,並給它火焰效果)。

我們為劍新增乙個藍寶石。(我們包裝 包含了乙個紅寶石的sword物件,給它新增2點傷害,並給它冰凍效果。)

用**來體現應該就是這樣的:

weapon sword = new sword();             // 建立一把劍

sword = new reddiamond(sword);          // 給劍新增 紅寶石

sword = new bluediamond(sword);         // 給劍新增 藍寶石

從給劍新增紅寶石那句**,我們發現第一件事:寶石應該儲存乙個對劍的引用。然後我們就可以在寶石類的內部來為sword新增行為或狀態。

從給劍新增藍寶石那句**,我們發現第二件事:新增了紅寶石的劍(僅從**看它屬於是寶石),仍然是劍,所以我們得出:寶石應該和**是同乙個型別(weapon基類)的,不然這裡將無法再次傳遞。

這個過程用圖來體現就是這樣的:

如果我們將整個過程用uml來表示,就構成了下面這幅圖:

從圖中我們可以看到,通過寶石的擴充套件,我們可以為劍提供新的能力:額外的傷害加成,以及額外的**特效(抱歉我不能顯示乙個華麗的魔法效果,只能在黑底白字的螢幕上輸出一句:addtional effect: fire !)。

在damage() 和 getdescription()中,我們先呼叫基類的相應方法,然後為damage()新增來自寶石的額外的傷害(狀態): icedamage,以及來自寶石的額外效果(行為):frozeneffect()。

上面這幅圖,就構成了gof的decorator設計模式,我們現在看一下它的官方定義:動態地為物件新增額外的職能。decorator模式為通過繼承來為類擴充套件功能這種方式提供了另一種靈活的選擇。

簡單起見,我們只實現一種**:sword,兩種寶石:藍寶石 和 紅寶石。

using system;

using system.collections.generic;

using system.text;

namespace decorator

public abstract int damage();           // **的傷害

}// 定義劍

public class sword : weapon

public override int damage()

}// 定義寶石基類

public abstract class diamond : weapon

// 定義藍寶石

public class bluediamond : diamond

public string iceeffect()

public override int damage()

public override string getdescription()

}// 定義紅寶石

public class reddiamond : diamond

public string fireeffect()

public override int damage()

public override string getdescription()

}class program }}

輸出為:

one-hand light sword

damage:15

one-hand light sword

addtional effect: frozen !

damage:17

one-hand light sword

addtional effect: frozen !

addtional effect: fire !

damage:20

本文中,我們通過乙個常見的給**(物件)新增寶石(額外的狀態和行為)的例子,討論了decorator設計模式的實現過程。

我們首先提出了要解決的問題:給**新增寶石,以使它有額外的攻擊(傷害)加成和特殊的攻擊效果。然後提出了使用繼承會遇到的問題:大量的類以及只能通過例項化子類的方式獲取行為。隨後我們使用復合(composition)的方式來解決,又遇到新的問題:程式不易維護,每次新增新的寶石或者新增新的物品孔,都需要修改**。最後,我們使用decorator模式巧妙地解決了這個問題。

希望這篇文章能給你帶來幫助!

X的奇幻之旅

順應之前 程式設計師如何學數學 的指導思想,買了不少比較輕鬆的數學科普書,the joy of x 就是其中之一。閒來無事隨手拿起,沒想到卻基本讀完了。看似不起眼的一本小書,內容的編排 文筆的輕鬆令人嘆為觀止。有的章節讓人覺得精妙無比,有的章節又扣人心懸。本文就簡單地加以整理,稍微打亂了原書的順序,...

rpg人物製作軟體 RPG遊戲製作教程

專案一 預設素材使用教程 任務認識 rpg與 rpg maker xp 1.rpg 簡述rpg 即角色扮演遊戲 英文全稱 是role playing game 是一種遊戲。在遊戲中,玩 家扮演虛擬世界中的乙個或者幾個特定角色在特定場景下進行遊戲。角色根據不同的遊戲情 節和統計資料 例如力量 靈敏度 ...

科幻,奇幻,武俠 武俠篇

我看武俠看得比較晚,以前只是看電視。到了大學才開始看金庸的原著。大師就是大師,寫的書都很耐讀。可惜的是那些情節都已經熟透了,沒有什麼新鮮感。看完金庸找了些其他人的書看,看了數人之作,模式單一,毫無新意。大部分連抄都不會抄,有武無俠。裡面的主角除了會n n種名字很nb的武功,加上鋪張造勢的語言,人物性...