1. 程式人生 > >Unity3d與設計模式(三)工廠模式

Unity3d與設計模式(三)工廠模式

這個系列的文章,並不會將所有用到的設計模式全部講一遍,事實上我個人認為,並不是所有的設計模式都適用於unity3d。這裡講的主要還是一些常用的設計模式。
那麼,本章講的就是常見的構建型模式當中的工廠模式。

簡單工廠模式

講工廠,首先得從簡單工廠說起。
簡單工廠模式的目的是用來建立不同型別的物件。需要指出的是它並不是GOF的23種模式之一。

結構

類圖

實現

廢話少說,直接上程式碼。

public interface IProduct {
    void DoSth();
}

public class ProductFirst : IProduct {
    public
virtual void DoSth(){ Debug.Log("ProductFirst DoSth"); } } public class ProductSecond : IProduct { public virtual void DoSth(){ Debug.Log("ProductFirst DoSth"); } } public class SimpleFactory { public static IProduct Create(int id){ switch(id){ case
1: return new ProductFirst(); break; case 2: return new ProductSecond(); break; default: return null; break; } } }

簡單工廠模式的Create()方法裡,可以新增各種邏輯,用於建立對應的例項。unity3d中很多時候建立的是遊戲中的物件,這時簡單工廠模式中建立者的引數可以對應prefab的名字。

優點

  • 簡單,可以取名叫『2分鐘內可以學會的設計模式』
  • 實現邏輯清晰,根據不同的建立引數建立對應例項。

名為簡單工廠方法,看起來果然是很簡單,對不對?那麼,本著”simple is best”的邏輯,是不是我們應該大力推廣簡單工廠模式呢?
答案是「No」。簡單工廠模式有其固有的缺陷,在使用時需要嚴格限定其範圍。

缺陷

讓我們首先考慮一個問題。此處使用的Create()方法,直接決定我們產生例項的邏輯。
那麼,現在問題來了。
假如我們不希望通過判斷引數是1還是2,來進行不同例項的生成呢?
顯然,一旦我們需要新的邏輯來產生例項的話,我們就不得不對程式碼進行修改。
當然,從理論上,我們也可以發現簡單工廠模式的一些問題。
Open-closed原則,即是對擴充套件開放,對修改封閉。使用簡單工廠模式時,很多時候違背了這一原則。同時,由於產生不同例項的方法在可預見的將來有可能會變得很複雜,是否滿足單一職責這一點也值得商榷。
那麼,我們有辦法解決這個問題嗎?嗯,接下來就是抽象程度更高的方法出場了。

工廠方法

工廠方法與簡單工廠最大的區別,在於工廠方法將工廠進行了抽象,將實現邏輯延遲到工廠的子類。

結構

工廠方法

實現

為了讓我們例子更貼近生產環境,在這裡採用一個更加實際的問題。
場景當中有兩個物體,我們希望其中一個向左走,一個向右走。
我們用工廠方法來生成這兩個向左向右的控制器,並新增到對應的物體上。當然,事實上這個例子依然很單薄,實際面對這個問題我們肯定不會這樣實現就是了。
上程式碼
* IWalker

public interface IWalker {
    void Walk(Transform target);
}
  • LeftWalker
public class LeftWalker : MonoBehaviour, IWalker {
    Transform _target;
    public virtual void Walk(Transform target){
        _target = target;
        StartCoroutine(WalkLeft());
    }

    IEnumerator WalkLeft(){
        while(true){
            _target.Translate(Vector3.left * Time.deltaTime);
            Debug.Log("WalkLeft " + _target.localPosition);
            yield return new WaitForFixedUpdate();
        }
    }
}
  • RightWalker
public class RightWalker : MonoBehaviour, IWalker  {
    Transform _target;
    public virtual void Walk(Transform target){
        _target = target;
        StartCoroutine(WalkRight());
    }

    IEnumerator WalkRight(){
        while(true){
            _target.Translate(Vector3.right * Time.deltaTime);
            Debug.Log("WalkRight " + _target.localPosition);
            yield return new WaitForFixedUpdate();
        }
    }
}
  • IWalkerFactory
public interface IWalkerFactory {
    IWalker Create();
}
  • LeftWalkerFactory
public class LeftWalkerFactory : IWalkerFactory {
    public virtual IWalker Create(){
        return new GameObject().AddComponent<LeftWalker>();
    }
}
  • RightWalkerFactory
public class RightWalkerFactory : IWalkerFactory{
    public virtual IWalker Create(){
        return new GameObject().AddComponent<RightWalker>();
    }
}

優點

工廠方法比簡單工廠多了一層抽象。
由於抽象工廠層的存在,當我們需要修改一個實現的時候,我們不需要修改工廠的角色,只需要修改實現的子類就可以完成這個工作。
同樣,當我們需要增加一個新產品的時候,我們也不需要修改工廠的角色,只需要增加一個新的實現工廠來完成實現就可以了。
顯然,這樣更易於擴充套件,並且,整體程式碼的層級結構更加分明,建立實際產品的職責更加單一。
此外,很顯然客戶在定義工廠角色的時候不需要知道實現子類。只有當實際需要建立的時候,才動態指定子類。這同樣帶來了程式碼的穩定性和最小可知性。

缺陷

顯然,使用工廠方法的程式碼量是多於簡單工廠的。
同時,每增加一個新的產品,就會增加一個新的工廠類,程式碼的複雜程度自然也隨之上升了。我們會為此建立很多的工廠。

抽象工廠

抽象工廠和工廠方法實際上是很像的,不過抽象工廠增加了另外一個概念,就是產品族。也就是說,一個工廠可以生產一系列的產品,這些產品的定義都在工廠當中。

結構

這裡寫圖片描述

實現

ok。這個模式老實說意義不是很大。直接上程式碼吧,就不加註釋了

public interface IActorFactory  {
    IFlyer CreateFlyer(GameObject go);
    IWalker CreateWalker(GameObject go);
}

public interface IFlyer {
    void Fly(Transform target);
}

public class LeftActorFactory : IActorFactory {
    public virtual IFlyer CreateFlyer(GameObject go){
        return go.AddComponent<LeftFlyer>();
    }

    public virtual IWalker CreateWalker(GameObject go){
        return go.AddComponent<LeftWalker>();
    }

}

public class RightActorFactory : IActorFactory{
    public virtual IFlyer CreateFlyer(GameObject go){
        return go.AddComponent<RightFlyer>();
    }

    public virtual IWalker CreateWalker(GameObject go){
        return go.AddComponent<RightWalker>();
    }

}

優點

當我們需要增加一個產品族的時候,我們只需要增加一個工廠,實現其中所有產品的實現就行了。
抽象工廠的設計,使得我們可以很容易的增加一個產品系列。

缺點

抽象工廠當中,產品族的定義使得子類必須去實現所有的產品生產。
因此,抽象工廠並不適合於橫向擴充套件,即需要增加產品的情況。
一旦需要增加產品,那麼我們甚至需要去修改抽象的基類。這是比較違反開閉原則,不太符合面向物件設計的做法。

總結

從簡單工廠到工廠方法再到抽象工廠。我們可以看到,抽象的程度越來越高,能夠解決的問題也越來越複雜。
不過,個人的經驗而言,一般在unity3d當中也頂多用到工廠方法而已。抽象工廠事實上並不是一個很靈活的解決方案。
而且,對於unity3d中元件的建立,事實上是有一些非常靈活的解決方案可以處理的。實體與元件系統,相當適合於元件的構建,比起工廠方法來說更加靈活和易於擴充套件。
以後有時間的時候再對此進行說明。

本章的例子
工廠模式