2016年8月3日 星期三

簡單工廠模式 - Simple Factory Pattern

在前一篇文章中,我們已經將各類敵人的物件類別定義好了,是時候該將他們派上戰場了!




設計想法





我們已經將上一篇討論的「敵人的物件架構」做了一些修改,讓他看起來更完善。現在,我們需要一個物件,來將這些敵人建立出來。當然,我們會有另外一個模組來告訴你需要產哪一架敵人飛機。

沒錯,這個設計想法就只是把敵人建立出來而已。講得更設計面一點,就只是把物件 new() 出來。幸好,我們上一篇文章中有提到,要針對介面而寫程式,所以我們在實體化物件時,可以使用多型的概念。所以設計師通常會有以下的設計概念

public void CreateEnemyPlane(String type){
    Enemy plane;
    
    if(type == "Helicopter")
        plane = new Helicopter();
    else if(type == "Fighter")
        plane = new Fighter();
    else if(type == "Airship")
        plane = new Airship();

    plane.Init();
    plane.SetInitPosition();
    // more ...
}

這樣的設計還蠻直覺的,但他一樣有一些淺在的問題
  • 遊戲編導突然覺得飛船(Airship)加在遊戲中不太適合,想要把他拿掉。並且換上另外一個轟炸機(Bomber)
老實說,這不是什麼重要的變動。其實只要把 if ... else 的條件式稍做修改就 OK 了。但在這裡,有一個設計觀念可以討論一下。
設計守則

  1. 開放關閉守則 : 類別應該要開放,以便擴充;應該關閉,禁止修改

這條守則聽起來有點矛盾,又要開放,又要關閉,到底是想怎樣?其實他的意思是,我們盡可能在不動到原先的程式碼的前提下,還可以擴充功能。這聽起來也有點不可思議,想要加新功能,就勢必要改寫程式碼啊!注意,這裡指的是「盡可能」。

在上述的程式碼中,若要新增敵人,我們勢必要動到這個 Function。會修改到的部分,就只有 if ... else 的條件式,後面的部份幾乎沒有動到。因此,根據上一張學到的技巧「找出程式中可能會變動的地方,把他們獨立出來,不要和固定不變的程式混在一起」,我們應該要把這段 Code 獨立出來一個物件來處理。

public class SimpleEnemyFactory{
    public Enemy CreateEnemy(String type){
        Enemy e;
    
        if(type == "Helicopter")
            e = new Helicopter();
        else if(type == "Fighter")
            e = new Fighter();
        else if(type == "Airship")
            e = new Airship();
        
        return e;
    }
}

如此一來,你就可以把原先 CreateEnemyPlane 裡的程式簡化

public class EnemyCreator{
    SimpleEnemyFactory factory;

    public EnemyCreator(SimpleEnemyFactory  _factory){
        factory = _factory;
    }

    public void CreateEnemyPlane(String type){
        Enemy plane;
  
        plane = factory.CreateEnemy(type);
        plane.Init();
        plane.SetInitPosition();
        // more...
    }
}



這就是所謂的「簡單工廠模式」。看到這,你一定會覺得「這個設計模式也太廢了吧!它不過就是把其中一段程式碼搬到另一邊去,有任何的技巧可言?」老實說,簡單工廠模式其實不是設計模式裡的一員,他只是一個小技巧 (真正的重頭戲在下一個章節 - 工廠模式)。你要注意的是,CreateEnemyPlane 其實還有很多自己的事情要做 (只是我寫個 more... 偷懶帶過),他是無法給別人重複使用的。當專案越來越龐大,你的工作夥伴想在另外一個地方也用到產生敵人的功能時,卻因為 CreateEnemyPlane 裡面做了他不想要的事情,而不能拿來用,這時候他會做一件很直覺但很恐怖的事情,就是把他要的程式碼 Copy 到他那裏去(也就是 if ... else 那段)。當你發現專案裡到處都是這段「建立敵人」的程式碼時,已經為時已晚,當遊戲編導想要修改替換敵人物件時,你就要到處修改程式碼,可能還會導致不必要的錯誤。

這就是開放關閉守則重要之處,我們把 SimpleEnemyFactory 獨立出來,想用的人就直接呼叫它來擴充功能,而當我想修改它時,我也只動到 Factory 裡的邏輯,而不用到處修改。








沒有留言:

張貼留言