裝飾模式:英雄裝配裝備

    在角色扮演類遊戲,常常涉及到某個英雄裝配各種裝備,例如大掌門,我叫MT這樣的遊戲中,角色都可以裝配一些裝備,如,武器,護甲等。

    當角色裝配各種裝備後,會使角色的某些屬性增加,例如,金蛇郎君裝備金蛇劍後,他的攻擊力可以增加500,再給他搞個軟蝟甲穿上,他的防禦力直接增加400。如果機緣巧合,他學會如來神掌,那他的攻擊力直接飆升到1000.當角色裝配某種“道具”後,他的屬性都會相應的發生改變。

   在遊戲中,一般情況下,角色可以隨意搭配裝備。還是那金蛇郎君舉例,給他 裝配了 金蛇劍, 軟蝟甲,如來神掌,他在數據庫存儲的記錄可能類似於這樣:

     name:         金蛇郎君

   weapon_id:   003 (對應金蛇劍)

    armor_id:     004 (對應軟蝟甲)

 kongfu_id:    005 (對應如來神掌)


 我們計算金蛇郎君的整體實力的時候 代碼可能是這樣:

function getUserPower($user)
    {
        // 配置 武器
        if ($user['weapon_id']) {
            if ($weapon = $this->_user->weapon->getOneWeapon($user['captain_id'])) {
                // 金蛇郎君的戰鬥力增加
                $user['attack'] += $captain['attack'];

                // 金蛇郎君的防禦力增加
                $user['defend'] += $weapon['defend'];
            } else {
                // 不存在則強行卸載
                $this->unloadItem($user, 'weapon');
                unset($weapon['weapon']);
            }
        }

        // 配置 護甲
        if ($user['armor_id']) {
            if ($armor = $this->_user->armor->getOneArmor($user['armor_id'])) {
              // 金蛇郎君的戰鬥力增加
                $user['attack'] += $captain['attack'];

                // 金蛇郎君的防禦力增加
                $user['defend'] += $weapon['defend'];
            } else {
                // 不存在則強行卸載
                $this->unloadItem($user, 'armor_id');
                unset($user['armor_id']);
            }
        }

樣的代碼,似乎可以完美的搞定這個需求,但是,某一天,坑爹的產品再次放出話來:夏雪宜裝配金蛇劍後,金蛇劍不僅可以“增加”夏雪宜的攻擊力,還能“增加”他的防禦力,除此之外,裝備對角色屬性影響的加成算法發生了變化。


遇到這樣的產品,我們仰天長嘆之後,還得充滿辛酸的修改角色類中getUserPower()方法。改就改吧,這又有什麼關係呢,問題就在這裏:由於金蛇劍這個裝備發生了變化,導致我不得不去改角色類裏面的方法!!如果哪一天,我增加了一個裝備類型,或者某個裝備的加成方式發生了變動,那我的角色類中的getUserPower()函數要改的面目全非。

這樣,角色模塊和裝備模塊耦合太高,裝備的改動易導致角色模塊出現錯誤的風險增加。有沒有一種好的設計模式能解決這個問題,當裝備發生變動的時候,我只需要改裝備類裏面的方法,而儘量少改動角色類裏面的getUserPower()方法。

這個時候,我們可以使用裝飾模式 。裝飾模式動態的把責任附加到對象上,若要擴展功能,裝飾者比繼承提供更有彈性的替代方案。在裝飾對象的時候,裝飾模式要求被裝飾者和裝飾者需要繼承同一個超類。

 這是裝飾者模式的類圖:

 


這裏,我們需要把焦點定格在裝飾這個詞彙上,所謂裝飾,就是那一樣東西就裝飾另一樣東西,在不改變另一樣東西的前提下,修改它的某些特徵,或者給他添加的新得職能。我們這裏,就可以參考以上的類圖形式,使用 金蛇劍類,軟蝟甲類,武功類分別裝飾金蛇郎君。

  類關係圖如下:

  

我們就是用裝備類來裝飾角色類,此遊戲的裝飾模式其實和定義的裝飾模式有點區別,裝飾模式要求被裝飾者和裝飾者是繼承同一個類,而我們的裝備類與角色類並不是繼承同一個父類。類圖如下:

  

         每個裝備類,都必須實現一個equip(Model_User $user)方法,這個方法的參數是一個角色對象:

       

/**
 * 裝備-武器模型
 *
 * $Id: weapon.php 1953 2013-04-10 06:33:38Z jiangjian $
 */

class Model_Item_Weapon extends Model_Item_Abstract
{
    /**
     * 裝配到角色上
     *
     * @param Model_User $user
     * @return void
     */
    public function equip(Model_User $user)
    {
        // 裝備屬性賦值
        $user['weapon'] = $this;

        // 增加 攻擊力
        $user['offense']   += $this->_prop['offense'];

        // 增加 射速
        $user['fire_rate'] += $this->_prop['fire_rate'];

        // 增加 命中率
        $user['hit_odds']  += $this->_prop['hit_odds'] ;
    }
}


在角色類中,我們需要一個方法:

class Model_User
{
    /**
     * 裝飾、配置我的角色(即計算裝備屬性增益)
     *
     * @param Model_User $user
     * @param array $userInfo 我的配置
     * @return void
     */
    private function _decorateUser(Model_User $user, &$userInfo)
    {
        // 配置 武器
        if ($userInfo['weapon_id']) {
            if ($weapon = $this->_user->Weapon->getOneWeapon($userInfo['weapon_id'])) {

                // 實現對角色的裝飾
                $weapon->equipUser($user);
            } else {
                // 不存在則強行卸載
                $this->unloadItem($userInfo, 'weapon');
                unset($userInfo['weapon_id']);
            } 
        }

        // 配置 護甲
        if ($userInfo['armor_id']) {

        }

        // 配置 功夫
        if ($userInfo['kongfu_id']) {

        }
  
    }

這樣做的好處是,到時候如果需要修改裝備時,我們只需要修改裝備類中的方法。


發佈了41 篇原創文章 · 獲贊 14 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章