欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Unity3D 單例模式和靜態(tài)類的使用詳解

 更新時(shí)間:2021年04月13日 14:50:15   作者:EZhex1991  
這篇文章主要介紹了Unity3D 單例模式和靜態(tài)類的使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧

Unity3D的API提供了很多的功能,但是很多流程還是會(huì)自己去封裝一下去。當(dāng)然現(xiàn)在網(wǎng)上也有很多的框架可以去下載使用,但是肯定不會(huì)比自己寫(xiě)的用起來(lái)順手。

對(duì)于是否需要使用框架的問(wèn)題上,本人是持肯定態(tài)度的,把一些常用方法進(jìn)行封裝,做成一個(gè)功能性的框架,可以很大程度上提高代碼的效率,維護(hù)也方便。

對(duì)于網(wǎng)絡(luò)上很多教程上使用的“游戲通用MVC框架”,現(xiàn)在看來(lái)并不符合MVC這種結(jié)構(gòu)性框架的設(shè)計(jì)思想:要知道,MVC最初是被設(shè)計(jì)為Web應(yīng)用的框架,而游戲中的很多事件并不是通過(guò)用戶點(diǎn)擊UI發(fā)生的,View和Controller在游戲邏輯中的占比一般都少的可憐,而且很多教程上把Model剝離出很多“Manager”模塊,甚至有人把View和Controller合在一起寫(xiě)了UIManager——連MVC的結(jié)構(gòu)都沒(méi)了,為啥還要稱之為MVC框架呢?

MVC: “人紅是非多。。。?!?/p>

目前大部分的游戲框架——特別是小型項(xiàng)目的游戲框架——都是把一些數(shù)據(jù)的特定行為進(jìn)行了一下封裝:生成一個(gè)物件,播放一個(gè)特效,進(jìn)行一次隨機(jī)事件等。當(dāng)然也會(huì)有一些結(jié)構(gòu)性的設(shè)計(jì)或者資源管理設(shè)計(jì)如:UI的回退?;蛘呋赝随湥瑘?chǎng)景的載入記錄和切換,下載隊(duì)列的管理等。

在Unity的框架設(shè)計(jì)中,有一個(gè)詞會(huì)經(jīng)常見(jiàn)到:?jiǎn)卫J剑╯ingleton)。單例模式就是在整個(gè)游戲中只使用某個(gè)類的一個(gè)實(shí)例,核心的一句話就是public static T Instance;即在類中定義了一個(gè)靜態(tài)的自身實(shí)例供外部使用,調(diào)用方法時(shí)就是:T.Instance.Function()。在本人最初接觸這種設(shè)計(jì)方式時(shí)經(jīng)常會(huì)與靜態(tài)類弄混淆,T.Function()。中間差了一個(gè)靜態(tài)Instance,很多時(shí)候好像區(qū)別不大。。。

在接近兩周左右的時(shí)間里,我一直在糾結(jié)于自己正在寫(xiě)的框架到底應(yīng)該寫(xiě)成單例模式的還是靜態(tài)模式的,今天剛好對(duì)這個(gè)問(wèn)題有了一個(gè)新的想法:靜態(tài)可不可以理解為一種封閉性很強(qiáng)的單例?

首先回想一下靜態(tài)的兩個(gè)常識(shí):

1、靜態(tài)類不能繼承和被繼承!(嚴(yán)格點(diǎn)說(shuō)是只能繼承System.Object)也就是說(shuō)你的靜態(tài)類不可能去繼承MonoBehaviour,不能實(shí)現(xiàn)接口。

2、靜態(tài)方法不能使用非靜態(tài)成員!如果你大量使用靜態(tài)方法,而方法里又需要用到這個(gè)類的成員,那么你的成員得是靜態(tài)成員。

第2點(diǎn)需要注意:如果你想在Unity的編輯器下調(diào)整某個(gè)參數(shù),那么這個(gè)參數(shù)就不能是靜態(tài)的(哪怕你自定義EditorWindow去修改這個(gè)值也沒(méi)用),解決的辦法是通過(guò)UnityEngine.ScriptableObject去存放配置(生成*.asset文件),然后在運(yùn)行中通過(guò)LoadAsset去加載,然后再改變靜態(tài)成員。至于原因,相信不難理解——你看到的所有Unity組件都是一個(gè)個(gè)實(shí)例,你要通過(guò)Unity的編輯器去配置,那么你就得有一個(gè)這樣的可配置實(shí)例。

從面向?qū)ο笊舷胍幌拢红o態(tài)方法或者靜態(tài)類,不需要依賴對(duì)象,類是唯一的;單例的靜態(tài)實(shí)例,一般就是唯一的一個(gè)對(duì)象(當(dāng)然也可以有多個(gè))。差別嘛。。。好像也不大。。。

如果這樣考慮沒(méi)有錯(cuò),那再回頭比較一下兩種方式:

1、靜態(tài)(靜態(tài)方法或者靜態(tài)類),代碼編寫(xiě)上絆手絆腳,方法調(diào)用很方便,運(yùn)行效率高一丟丟。邏輯面向過(guò)程,不能很好地控制加載和銷毀。

2、單例(類的靜態(tài)實(shí)例),代碼編寫(xiě)和其他類完全一樣,繼承抽象模版接口都可以,Unity里也很方便進(jìn)行參數(shù)配置,不過(guò)使用麻煩有犯錯(cuò)的可能性(必須通過(guò)實(shí)例調(diào)用方法),效率不如靜態(tài)(但是也不會(huì)有很大影響吧)。

如果這些說(shuō)法太抽象,那我再給出一個(gè)常見(jiàn)的問(wèn)題:如果你的框架有一個(gè)SoundManager能夠管理所有的聲音播放,那么你會(huì)怎么去實(shí)現(xiàn)?

(在剛接觸AudioSource這個(gè)組件的時(shí)候,我想的是每一個(gè)聲音都由一個(gè)AudioSource去播放。但是后來(lái)發(fā)現(xiàn)完全沒(méi)必要,AudioSource有靜態(tài)的PlayClipAtPoint方法去播放臨時(shí)3D音效,同時(shí)有實(shí)例方法PlayOneShot去播放臨時(shí)音效(2D和3D取決于當(dāng)實(shí)例的SpatialBlend)。如果沒(méi)有特殊的需求,那么一個(gè)AudioSource循環(huán)播放背景音樂(lè),上述兩種方法播放游戲中的特效音頻,這對(duì)于大部分游戲已經(jīng)足夠了。)

那么問(wèn)題來(lái)了:你的SoundManager播放聲音的方法如果是靜態(tài)的,那么AudioSource組件必須在代碼中通過(guò)各種方式去獲?。ㄐ陆ńM件或者獲取特定GameObject下的組件)——因?yàn)楸4孢@個(gè)組件的變量必須是靜態(tài)的,也就不能通過(guò)Unity的編輯器去賦值。如果不去閱讀代碼那么用戶完全不知道這是一個(gè)什么樣的組件獲取流程,如果我破壞這個(gè)流程(同名物體,包含互斥組件等),那么這個(gè)Manager很有可能會(huì)出現(xiàn)不可預(yù)料的異常。

而繼承MonoBehaviour并RequireComponent(typeof(AudioSource)),怎么看也比“為了靜態(tài)而靜態(tài)”的代碼要方便健壯的多。

實(shí)際上到這里已經(jīng)可以基本總結(jié)出何時(shí)需要使用單例了:

1、只要你的類需要保存其他組件作為變量,那么就有必要使用單例;

2、只要你有在Unity編輯器上進(jìn)行參數(shù)配置的需求,那么就有必要使用單例;

3、只要你的管理器需要進(jìn)行加載的順序控制,那么就有必要使用單例(比如熱更新之后加載ResourcesManager);

當(dāng)然,這里都只是“有必要”,并不是“必須”。兩者區(qū)別最大的地方,一個(gè)是方便寫(xiě),一個(gè)是方便用。方便寫(xiě)的代價(jià)是每次調(diào)用加個(gè)instance,方便用的代價(jià)則是放棄了面向?qū)ο蠛蚒nity的“所見(jiàn)即所得”,孰輕孰重,自己抉擇。

另一方面,和“為了靜態(tài)而靜態(tài)”一樣,“為了單例而單例”同樣是一個(gè)不合理的設(shè)計(jì)。這樣的解釋仍然是那么的模糊,那么,就給自己定義一個(gè)最簡(jiǎn)單的規(guī)則吧——如果你的單例類里沒(méi)有任何需要保存狀態(tài)的變量,那么這個(gè)類里的方法就可以全都是靜態(tài)方法,這個(gè)類也可以是個(gè)靜態(tài)類。

補(bǔ)充:從實(shí)例出發(fā),了解單例模式和靜態(tài)塊

就算你沒(méi)有用到過(guò)其他的設(shè)計(jì)模式,但是單例模式你肯定接觸過(guò),比如,Spring 中 bean 默認(rèn)就是單例模式的,所有用到這個(gè) bean 的實(shí)例其實(shí)都是同一個(gè)。

單例模式的使用場(chǎng)景

什么是單例模式呢,單例模式(Singleton)又叫單態(tài)模式,它出現(xiàn)目的是為了保證一個(gè)類在系統(tǒng)中只有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。從這點(diǎn)可以看出,單例模式的出現(xiàn)是為了可以保證系統(tǒng)中一個(gè)類只有一個(gè)實(shí)例而且該實(shí)例又易于外界訪問(wèn),從而方便對(duì)實(shí)例個(gè)數(shù)的控制并節(jié)約系統(tǒng)資源而出現(xiàn)的解決方案。

使用單例模式當(dāng)然是有原因,有好處的了。在下面幾個(gè)場(chǎng)景中適合使用單例模式:

1、有頻繁實(shí)例化然后銷毀的情況,也就是頻繁的 new 對(duì)象,可以考慮單例模式;

2、創(chuàng)建對(duì)象時(shí)耗時(shí)過(guò)多或者耗資源過(guò)多,但又經(jīng)常用到的對(duì)象;

3、頻繁訪問(wèn) IO 資源的對(duì)象,例如數(shù)據(jù)庫(kù)連接池或訪問(wèn)本地文件;

下面舉幾個(gè)例子來(lái)說(shuō)明一下:

1、網(wǎng)站在線人數(shù)統(tǒng)計(jì);

其實(shí)就是全局計(jì)數(shù)器,也就是說(shuō)所有用戶在相同的時(shí)刻獲取到的在線人數(shù)數(shù)量都是一致的。要實(shí)現(xiàn)這個(gè)需求,計(jì)數(shù)器就要全局唯一,也就正好可以用單例模式來(lái)實(shí)現(xiàn)。當(dāng)然這里不包括分布式場(chǎng)景,因?yàn)橛?jì)數(shù)是存在內(nèi)存中的,并且還要保證線程安全。下面代碼是一個(gè)簡(jiǎn)單的計(jì)數(shù)器實(shí)現(xiàn)。

public class Counter {   
    private static class CounterHolder{
        private static final Counter counter = new Counter();
    }
    private Counter(){
        System.out.println("init...");
    }
    public static final Counter getInstance(){
        return CounterHolder.counter;
    }
    private AtomicLong online = new AtomicLong();
    public long getOnline(){
        return online.get();
    }
    public long add(){
        return online.incrementAndGet();
    }
}    

2、配置文件訪問(wèn)類;

項(xiàng)目中經(jīng)常需要一些環(huán)境相關(guān)的配置文件,比如短信通知相關(guān)的、郵件相關(guān)的。比如 properties 文件,這里就以讀取一個(gè)properties 文件配置為例,如果你使用的 Spring ,可以用 @PropertySource 注解實(shí)現(xiàn),默認(rèn)就是單例模式。如果不用單例的話,每次都要 new 對(duì)象,每次都要重新讀一遍配置文件,很影響性能,如果用單例模式,則只需要讀取一遍就好了。以下是文件訪問(wèn)單例類簡(jiǎn)單實(shí)現(xiàn):

public class SingleProperty {
    private static Properties prop;
    private static class SinglePropertyHolder{
        private static final SingleProperty singleProperty = new SingleProperty();
    }
    /**
    * config.properties 內(nèi)容是 test.name=kite 
    */
    private SingleProperty(){
        System.out.println("構(gòu)造函數(shù)執(zhí)行");
        prop = new Properties();
        InputStream stream = SingleProperty.class.getClassLoader()
                .getResourceAsStream("config.properties");
        try {
            prop.load(new InputStreamReader(stream, "utf-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SingleProperty getInstance(){
        return SinglePropertyHolder.singleProperty;
    }    
 
    public String getName(){
        return prop.get("test.name").toString();
    }
    public static void main(String[] args){
        SingleProperty singleProperty = SingleProperty.getInstance();
        System.out.println(singleProperty.getName());
    }
}

3、數(shù)據(jù)庫(kù)連接池的實(shí)現(xiàn),也包括線程池。

為什么要做池化,是因?yàn)樾陆ㄟB接很耗時(shí),如果每次新任務(wù)來(lái)了,都新建連接,那對(duì)性能的影響實(shí)在太大。所以一般的做法是在一個(gè)應(yīng)用內(nèi)維護(hù)一個(gè)連接池,這樣當(dāng)任務(wù)進(jìn)來(lái)時(shí),如果有空閑連接,可以直接拿來(lái)用,省去了初始化的開(kāi)銷。

所以用單例模式,正好可以實(shí)現(xiàn)一個(gè)應(yīng)用內(nèi)只有一個(gè)線程池的存在,所有需要連接的任務(wù),都要從這個(gè)連接池來(lái)獲取連接。

如果不使用單例,那么應(yīng)用內(nèi)就會(huì)出現(xiàn)多個(gè)連接池,那也就沒(méi)什么意義了。如果你使用 Spring 的話,并集成了例如 druid 或者 c3p0 ,這些成熟開(kāi)源的數(shù)據(jù)庫(kù)連接池,一般也都是默認(rèn)以單例模式實(shí)現(xiàn)的。

單例模式的實(shí)現(xiàn)方法

如果你在書(shū)上或者網(wǎng)站上搜索單例模式的實(shí)現(xiàn),一般都會(huì)介紹5、6中方式,其中有一些隨著 Java 版本的升高,以及多線程技術(shù)的使用變得不那么實(shí)用了,這里就介紹兩種即高效,而且又是線程安全的方式。

1. 靜態(tài)內(nèi)部類方式

public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE; 
    }  
}

這種寫(xiě)法仍然使用 JVM 本身機(jī)制保證了線程安全問(wèn)題,由于 SingletonHolder 是私有的,除了 getInstance() 方法外沒(méi)有辦法訪問(wèn)它,因此它是懶漢式的;同時(shí)讀取實(shí)例的時(shí)候不會(huì)進(jìn)行同步,沒(méi)有性能缺陷;也不依賴 JDK 版本。上面的兩個(gè)例子就是用這種方式實(shí)現(xiàn)的。

2. 枚舉方式

public enum SingleEnum {
    INSTANCE;
    SingleEnum(){
        System.out.println("構(gòu)造函數(shù)執(zhí)行");
    }
    public String getName(){
        return "singleEnum";
    }
    public static void main(String[] args){
        SingleEnum singleEnum = SingleEnum.INSTANCE;
        System.out.println(singleEnum.getName());
    }
}

我們可以通過(guò) SingleEnum.INSTANCE 來(lái)訪問(wèn)實(shí)例。而且創(chuàng)建枚舉默認(rèn)就是線程安全的,并且還能防止反序列化導(dǎo)致重新創(chuàng)建新的對(duì)象。

靜態(tài)塊

什么是靜態(tài)塊呢

1、它是隨著類的加載而執(zhí)行,只執(zhí)行一次,并優(yōu)先于主函數(shù)。具體說(shuō),靜態(tài)代碼塊是由類調(diào)用的。類調(diào)用時(shí),先執(zhí)行靜態(tài)代碼塊,然后才執(zhí)行主函數(shù)的;

2、靜態(tài)代碼塊其實(shí)就是給類初始化的,而構(gòu)造代碼塊是給對(duì)象初始化的;

3、靜態(tài)代碼塊中的變量是局部變量,與普通函數(shù)中的局部變量性質(zhì)沒(méi)有區(qū)別;

4、一個(gè)類中可以有多個(gè)靜態(tài)代碼塊;

他的寫(xiě)法是這樣的:

static {
        System.out.println("static executed");
    }

來(lái)看一下下面這個(gè)完整的實(shí)例:

public class SingleStatic {
    static {
        System.out.println("static 塊執(zhí)行中...");
    }
    {
        System.out.println("構(gòu)造代碼塊 執(zhí)行中...");
    }
    public SingleStatic(){
        System.out.println("構(gòu)造函數(shù) 執(zhí)行中");
    }
    public static void main(String[] args){
        System.out.println("main 函數(shù)執(zhí)行中");
        SingleStatic singleStatic = new SingleStatic();
    }
}

他的執(zhí)行結(jié)果是這樣的:

static 塊執(zhí)行中...

main 函數(shù)執(zhí)行中

構(gòu)造代碼塊 執(zhí)行中...

構(gòu)造函數(shù) 執(zhí)行中

從中可以看出他們的執(zhí)行順序分別為:

1、靜態(tài)代碼塊

2、main 函數(shù)

3、構(gòu)造代碼塊

4、構(gòu)造函數(shù)

利用靜態(tài)代碼塊只在類加載的時(shí)候執(zhí)行,并且只執(zhí)行一次這個(gè)特性,也可以用來(lái)實(shí)現(xiàn)單例模式,但是不是懶加載,也就是說(shuō)每次類加載就會(huì)主動(dòng)觸發(fā)實(shí)例化。

除此之外,不考慮單例的情況,利用靜態(tài)代碼塊的這個(gè)特性,可以實(shí)現(xiàn)其他的一些功能,例如上面提到的配置文件加載的功能,可以在類加載的時(shí)候就讀取配置文件的內(nèi)容,相當(dāng)于一個(gè)預(yù)加載的功能,在使用的時(shí)候可以直接拿來(lái)就用。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

相關(guān)文章

  • c# 播放聲音的四種方法

    c# 播放聲音的四種方法

    這篇文章主要介紹了c# 播放聲音的四種方法,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下
    2020-11-11
  • C# 面向?qū)ο笕筇匦裕悍庋b、繼承、多態(tài)

    C# 面向?qū)ο笕筇匦裕悍庋b、繼承、多態(tài)

    本文主要介紹了面向?qū)ο蟮娜筇匦裕悍庋b、繼承、多態(tài),提供了簡(jiǎn)單的示例供大家參考和理解。
    2016-03-03
  • C#基于WebSocket實(shí)現(xiàn)聊天室功能

    C#基于WebSocket實(shí)現(xiàn)聊天室功能

    這篇文章主要為大家詳細(xì)介紹了C#基于WebSocket實(shí)現(xiàn)聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • WPF中圖像處理的方法介紹

    WPF中圖像處理的方法介紹

    這篇文章介紹了WPF中圖像處理的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • C#自定義事件模擬風(fēng)吹草搖擺效果

    C#自定義事件模擬風(fēng)吹草搖擺效果

    這篇文章主要介紹了C#自定義事件模擬風(fēng)吹草搖擺效果,草地上每一顆草都監(jiān)聽(tīng)HoverTreeWindEvent事件,根據(jù)風(fēng)向(WindDdirection)調(diào)整姿態(tài)。需要的朋友可以參考下
    2017-08-08
  • 一篇文章說(shuō)通C#中的異步迭代器

    一篇文章說(shuō)通C#中的異步迭代器

    這篇文章主要給大家介紹了如何通過(guò)一篇文章說(shuō)通C#中的異步迭代器的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • C#異常執(zhí)行重試的實(shí)現(xiàn)方法

    C#異常執(zhí)行重試的實(shí)現(xiàn)方法

    這篇文章主要介紹了C#異常執(zhí)行重試的一種實(shí)現(xiàn)方法,重試模式可以用poll替代,通過(guò)示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08
  • WPF利用LiveCharts實(shí)現(xiàn)動(dòng)態(tài)曲線圖繪制

    WPF利用LiveCharts實(shí)現(xiàn)動(dòng)態(tài)曲線圖繪制

    LiveCharts是一個(gè)比較漂亮的WPF圖表控件,在數(shù)據(jù)發(fā)生變化后,還可以設(shè)置相對(duì)于的動(dòng)畫(huà)效果,本文就來(lái)利用LiveCharts繪制簡(jiǎn)單的動(dòng)態(tài)曲線圖吧
    2023-10-10
  • Unity shader實(shí)現(xiàn)頂點(diǎn)動(dòng)畫(huà)波動(dòng)效果

    Unity shader實(shí)現(xiàn)頂點(diǎn)動(dòng)畫(huà)波動(dòng)效果

    這篇文章主要為大家詳細(xì)介紹了Unity shader實(shí)現(xiàn)頂點(diǎn)動(dòng)畫(huà)波動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • Unity游戲開(kāi)發(fā)實(shí)現(xiàn)場(chǎng)景切換示例

    Unity游戲開(kāi)發(fā)實(shí)現(xiàn)場(chǎng)景切換示例

    這篇文章主要為大家介紹了Unity游戲開(kāi)發(fā)實(shí)現(xiàn)場(chǎng)景切換示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08

最新評(píng)論