close

Singleton是甚麼

遊戲實作中的唯一物件,地球是唯一的,太陽是唯一的...

放在遊戲世界裡,同時間只能有一個關卡進行,只能同時操作一個遊戲角色...

類似GM的存在
 

兩種特性

  1. 同時間只存在一個
  2. 提供快速取得這個物件的方法

可以這樣寫:

class StaticClass
{
    public static StaticClass instance;

    public StaticClass()
    {
        instance = this;
    }
}

雖然這樣能達到上面的兩個特性,但這潛在了被修改的風險

例如:

只要在其他Code上

StaticClass.instance = null;

或是

StaticClass.instance = new StaticClass();

都會直接把instance修改掉

Singleton解決方法:

class StaticClass{ 
    public string name = "StaticClass"; 
    private static StaticClass instance; 
    public static StaticClass Instance{ 
        get{ 
            if(instance == null){ 
                instance = new StaticClass(); 
            }
            return instance; 
        }
    }
}

P.S.這邊順便提一下命名,通常靜態變數開頭為大寫,為了與一般變數做區隔,一般變數通常用小寫

如此這樣一來,就可以用一行程式碼就可以直接取得此物件,也不會被修改

print(StaticClass.Instance.name);

 

其實不是因為貪求方便性所有類別都用Singaton,有時候其實是可以避免的

在設計的時候可以想看看,要求的功能是上述兩個特性都要還是只要其中一個

 

如果只是要求同時間只存在一個 :

public class ClassWithCounter
{
    static int m_ObjCounter = 0; 
    bool m_bEnable = false;

    public ClassWithCounter()
    { 
        m_ObjCounter++; 
        m_bEnable = (m_ObjCounter == 1) ? true : false;
 
        if (m_bEnable == false)
        { 
            Debug.LogError("目前物件數 : " + m_ObjCounter + "超過一個"); 
        } 
    }

    public void Operator()
    { 
        if (m_bEnable == false) return; 
        Debug.Log("可以執行"); 
    } 
}

可以在別的類別下測試 :
 

public class Test : MonoBehaviour
{
    public void Start()
    {
        ClassWithCounter cwc = new ClassWithCounter();
        cwc.Operator();
        ClassWithCounter cwc2 = new ClassWithCounter();
        cwc2.Operator();
        cwc.Operator();
    }
}

 

執行結果:

截圖 2023-01-06 下午12.12.37

 

如果是要讓物件可以容易被取得,可以設定為類別的參考 :

public class ObjectTest
{
    public ObjectTest(Test ob)
    {
        ob.GetMessage("GetObject的方法");
    }
}
public class Test : MonoBehaviour
{
    public void Start()
    {
        ObjectTest objectTest = new ObjectTest(this);
    }

    public void GetMessage(string message)
    {
        Debug.Log(message);
    }
}

 

單例模式濫用問題:

  • 被引用的對象不能被GC回收。比如引用了資源對象,被Destroy後,雖然c++端的内存被釋放了,但是c#端的對象引用還存在。如果不小心引用了一個Component,Component又引用了一大堆其他Component,則引用鏈上的所有對象都將無法被GC回收。
  • 耦合度變高。如果大量單例對象間交叉引用,會讓代碼變得很亂,不方便後續維護;而且工程如果大了以後,也不方便拆分工程,引起編譯時間過長。可以適當的拆分重组一下功能,讓引用盡量變成單向的。也可以引入事件中心,来解耦合。
  • 功能安全性。成員變數容易被其他地方隨意修改,不好排查。某些方法可能是特定場合才使用的,但是其他人並不知道。
  • 線程安全。涉及到線程相關的操作,非常容易出事。
arrow
arrow

    Kouhei 發表在 痞客邦 留言(0) 人氣()