一般化(Generalization)是很重要的一個步驟
卻也是常常被人忽略的一個細節
簡單來說,就是把特殊用途的程式碼
轉變成為一般用途的程式碼


舉個例子來說,我現在有20種怪物的AI要寫
直覺上我們會建立20個Class來分別實現
但是事實上,真的需要那麼多嗎?
仔細分析一下怪物的種類與特性
地上爬的,天上飛的,水裡游的,就分成三大類
攻擊模式,體型大小等等都可以是分類的依據

Template Method是個一般化的好方法
假設今天想要寫怪物建立的流程
每種怪物建法當然都不同
舉個例子:
void BaseMonster::Create()

{
    if (AgentType == Generator ||
AgentType == UnvisibleGenerator)
        BackupChild();
    else if (AgentType == ChildHexting || AgentType == FlyHexting)
        CreateWayPath();

    if (AgentType != UnvisibleGenerator && AgentType != ChildHexting)
        CreateWeapon();

    if
(AgentType != UnvisibleGenerator)
    {
        CreateModel();
        CreatePhysXBounding();
    }
   
    InitializeFSM();
}

乍看之下這樣處理似乎不錯
有考慮到各種不同類型怪物的差異性
事實上,怪物的種類千奇百怪
這個function只會無限制的擴充!

這時候應該把相同的部分留下來,特殊的部分交給sub class去處理

依照上面的內容,應該可以衍生出五種sub class
姑且叫做
GeneratorUnvisibleGeneratorChildHextingFlyHexting
以及一般最普遍的Hexting
然後在BaseMonster裡面增加一個virtual function,讓其他sub class去覆寫
於是上面的function修改為:

void BaseMonster::Create()
{
    BackupChild();
    CreateWayPath();
    CreateWeapon();
    CreateModel();
    CreatePhysXBounding();
    InitializeFSM();
}
void BaseMonster::BackupChild(){};
void BaseMonster::CreateWayPath(){};
void BaseMonster::CreateWeapon(){};
void BaseMonster::CreateModel(){};
void BaseMonster::CreatePhysXBounding(){};
除了BaseMonster共通的InitializeFSM()之外
其他的function內容
是空的

主要內容會在底下sub class中做個別處理
這樣的function還有個名稱,叫做Hook

然而仔細看一下這個function
其實它並不是很好的寫法
其中
BackupChild只有產生器類
G
enerator
UnvisibleGenerator會用到
另外可能18種怪物都不需要生怪物
CreateWayPath也是一樣
只有
ChildHextingFlyHexting會使用
寫太多似乎也會變得冗長

那麼就把它抽出來吧!
改成以下的寫法:
void BaseMonster::Create()
{
    InitializeSelfProperty();

    CreateWeapon();
    CreateModel();
    CreatePhysXBounding();
    InitializeFSM();
}

void UnvisibleGenerator::InitializeSelfProperty()
{
    BackupChild();
}
void ChildHexting::InitializeSelfProperty()
{
    CreateWayPath();
}
其中Generator繼承UnvisibleGenerator
FlyHexting會繼承ChildHexting

嗯,似乎又精簡了一點
但是
InitializeSelfProperty的共用性很差
假設有一隻怪物會飛,又會生小怪物呢?
FlyGenerator同時繼承Gemerator和FlyHexting
於是乎Kit的系統終於上場

(當然多重繼承也不是壞方法啦,只是不在目前討論範圍內)

它將這些可共用,又可獨立的屬性抽出來
更改一下以上的Code會變成:
void BaseMonster::Create()
{
    InitializeKit();
    InitializeFSM();
}
Kit的系統是一個巨大的Composite架構
你會在裡面發現各種屬性的Kit
例如GeneratorKit,WayPathKit,PhysXKit等等
也由於是
Composite的迴圈建立架構
一隻怪物想要什麼樣的屬性
便取決於它擁有哪些Kit
因此我們才能夠使用少量的Kit
就能夠排列組合建立出多樣化的AI!

想想看,如果每一種怪物都自己寫一個Class
並且寫自己的建立function
跟以上的寫法比起來
code的數量會差多少?
所以說
Generalization實在是太重要了
沒有它我們該怎麼辦啊!!!

arrow
arrow
    全站熱搜

    seeulin 發表在 痞客邦 留言(1) 人氣()