程序員過關斬將--請不要隨便修改基類

菜菜哥,又來找你了架構

YY妹子,沒和你男票分吧?測試

暫時尚未....找你仍是由於前幾天和你說的那個遊戲的事優化

產品經理又要改?spa

you are right! 此次產品狗要給玩家(player)加升級機制,例如經驗值到100升級到二級架構設計

這個好辦呀,不就是修改一下任務的級別信息嗎?設計

麻煩的不是這裏,除了級別信息變更以外,有可能還獲取到技能,好比:升到10級獲取了跳躍的技能code

這樣的功能在儘可能不修改源代碼的基礎上仍是須要好好設計一下,來靠近一點,菜菜哥詳細和你說對象

若是你對問題的背景不太熟悉,不如複習一下上一篇,入口》.blog


◆◆初級版本◆◆


這是玩家的抽象基礎類,這個設計很好,把一些玩家共有的特性抽象出來繼承

//玩家的基礎抽象類
    abstract class Player
    {     
        //玩家的級別
        public int Level { getset; }
        //其餘屬性代碼省略一萬字
    }


這是新加需求:10級能夠跳躍,具體跳躍動做是客戶端作處理

 //玩家的基礎抽象類
    abstract class Player
    {     
        //玩家的級別
        public int Level { getset; }
        //其餘屬性代碼省略一萬字

        //新加玩家跳躍動做,因爲須要到達10級因此須要判斷level
        public virtual bool Jump()
        
{
            if (Level >= 10)
            {
                return true;
            }
            return false;
        }
    }


這種代碼初級人員很容易犯,有什麼問題呢?

1.  跳躍的動做被添加到了基類,那全部的子類就都有了這個行爲,若是子類機器人玩家不須要這個跳躍的行爲呢?

2.  爲了新需求,修改了基類,若是每次需求都須要修改基類,時間長了,項目大了,這個是比較要命的。


◆◆優化版本◆◆


因爲需求是增長玩家一個行爲,根據上一節的介紹,咱們應該瞭解到,行爲在代碼級別更傾向於用接口來表示。並且不是全部的玩家類型都須要附加跳躍這個行爲。據此優化以下:

//玩家跳躍的行爲
    interface IJump
    {
        bool Jump();
    }
    //玩家的基礎抽象類
    abstract class Player
    {     
        //玩家的級別
        public int Level { getset; }
        //其餘屬性代碼省略一萬字

    }
    //真實玩家
    class PersonPlayer : PlayerIJump
    {
        public bool Jump()
        
{
            if (Level >= 10)
            {
                return true;
            }
            return false;
        }
    }


不錯,到此咱們已經避免了初級人員所犯的錯誤了,每種玩家類型能夠根據須要自行去擴展行爲,改天產品狗在加一個10級玩家能夠飛的行爲,頂多在加一個IFly的行爲接口,而後實現便可。可是這樣的設計就沒有問題了嗎?有,固然有

1.  每次需求其實仍是改動了已經存在的而且穩定運行的老代碼,這是不可取的。並且修改老代碼,大大增長了bug出現的機率。

2.  假如如今咱們的遊戲有20種玩家類型,其中19種須要添加跳躍的行爲,那咱們須要修改19個玩家的子類,工做量是如此之大。

3.  利用相似繼承的方式擴展對象的行爲,是在編譯期就把對象的行爲肯定了。也就是說在設計層面,其實你已經把代碼寫死了。


有不少同窗的代碼就到目前爲止了


假設如下爲產品狗一個月以後的新需求:

1.  能跳躍的等級調整爲11級

2.  玩家添加能遁地的行爲

3.  新加了10種玩家類型


若是你讀到了這裏,說明你們都是對於設計追求卓越的技術人。這裏菜菜再強調一遍架構設計的一項重要原則


類應該對修改關閉,對擴展開放。


這裏須要強調一點,設計的每一個部分想要都遵循開放-關閉原則,一般很難作到。由於要想在不修改現有代碼的狀況下,你須要花費許多時間和精力。遵循開放關閉原則,一般須要引入更多的抽象,增長更多的層次,增大代碼的複雜度。所以菜菜建議把注意力集中在業務中最有可能變化的點上,這些地方應用開放關閉原則。至於怎麼肯定哪些是變化的點,這須要對業務領域很強的理解和經驗了。

如今咱們分析一下咱們要作的事情,咱們但願一個對象(player)在不改動的狀況下動態的給它賦予新的行爲,在業務上實現的功能和用繼承的結果相似。總之一句話:

現有的類型優雅的添加新行爲,而且能夠靈活疊加和替換

理想中的設計圖大體以下:


◆◆再次優化◆◆


如今咱們認真分析一下,若是每一個新的行爲要想擴展對象而又能保持該對象的自身特性,新行爲對象必須是擴展對象的子類,還必須包含對象的一個引用才能實現。


◆◆重要提示◆◆


1. 在系統設計過程當中,實現一個接口泛指實現某個對象的超類型,也就是說能夠是類或者接口。

2. 在你係統設計中,若是你的代碼依賴於某個具體的類型,並不是抽象的超類型,應用此篇介紹的設計方法可能會受到影響。

3. 附加在對象最外層的行爲,不該該窺視被包裝的類型內部的一些特性。

4. 附加在對象外層的行爲,能夠在內層對象的行爲先後加入本身的行爲,甚至能夠覆蓋掉內層對象的行爲。

5. 若是擴展的行爲過多,會出現不少小對象,過分使用會使程序變的很複雜,因此設計擴展行爲時候須要注意。


◆◆落實到代碼◆◆


假設如今真實玩家的定義以下:

//玩家的基礎抽象類
    public abstract class Player
    {
        //玩家的級別
        public int Level { getset; }
        //其餘屬性代碼省略一萬字
    }
    //真實玩家
    public class PersonPlayer : Player
    {

    }


如今的需求是給真實玩家添加一個10級能跳躍的行爲,在不修改原有玩家代碼的狀況下,擴展跳躍行爲代碼以下

//玩家行爲的擴展積累
    public class PlayerExtension : Player
    {
       protected Player player;

    }
    //跳躍玩家的行爲擴展類
    public class PlayerJumpExtensionPlayerExtension
    {
       public PlayerJumpExtension(Player _player)
        {
            player = _player;
        }
        public bool Jump()
        {
            if (player. Level >= 10)
            {
                return true;
            }
            return false;
        }
    }


測試代碼以下:

 PersonPlayer player = new PersonPlayer();
        //給用戶動態添加跳躍的行爲
            PlayerJumpExtension jumpPlayer = new PlayerJumpExtension(player);
           var ret= jumpPlayer.Jump();
            Console.WriteLine("玩家能不能跳躍:"+ret);
            //如今玩家升級到10級了
            player.Level = 10;
            ret = jumpPlayer.Jump();
            Console.WriteLine("玩家能不能跳躍:" + ret);


測試加過以下:

玩家能不能跳躍:False
玩家能不能跳躍:True


一個月後產品狗新加一個需求:真實玩家20級得到飛行的行爲,無序改動現有代碼,只需繼續添加一個能夠飛行的新擴展

//玩家能夠飛行的擴展
    public class PlayerFlyExtension : PlayerExtension
    {
        public PlayerFlyExtension(Player _player)
        
{
            player = _player;
        }
        public bool Fly()
        
{
            if (player.Level >= 20)
            {
                return true;
            }
            return false;
        }
    }


測試代碼以下:

 PlayerFlyExtension flyPlayer = new PlayerFlyExtension(player);
            Console.WriteLine( "玩家能不能飛行"+flyPlayer.Fly());
            player.Level = 20;
            Console.WriteLine("玩家能不能飛行" + flyPlayer.Fly());


測試結果:

玩家能不能飛行False
玩家能不能飛行True


重要提示

以上代碼級別上屬於演示代碼,可是設計的理念卻很重要。基於以上的設計思想,擴展的行爲徹底有能力修改,覆蓋玩家的某些行爲。好比玩家對象自己有一個喊話的行爲,那擴展類根據業務徹底可讓喊話行爲執行兩次等等修改。


相關文章
相關標籤/搜索