單例模式是一種常用的軟件設(shè)計(jì)模式,它所創(chuàng)建的對(duì)象只有一個(gè)實(shí)例,且該實(shí)例易于被外界訪問(wèn)。單例對(duì)象由于只有一個(gè)實(shí)例,所以它可以方便地被系統(tǒng)中的其他對(duì)象共享,從而減少系統(tǒng)中的資源開(kāi)銷(xiāo)。
單例模式的實(shí)現(xiàn)思路是:
單例模式的要點(diǎn):
我們來(lái)看一下使用TypeScript實(shí)現(xiàn)單例模式的代碼示例:
class Singleton { // 私有靜態(tài)屬性,存儲(chǔ)唯一實(shí)例 private static instance: Singleton; // 私有構(gòu)造函數(shù),防止外部實(shí)例化 private constructor() {} // 向外部提供能夠共享訪問(wèn)的唯一實(shí)例 public static getInstance(): Singleton { if (!Singleton.instance) { Singleton.instance = new Singleton(); } return Singleton.instance; } // 其他方法和屬性}const s1 = Singleton.getInstance(); const s2 = Singleton.getInstance();console.log(s1 === s2); // true
上面代碼中,Singleton類(lèi)的構(gòu)造函數(shù)被private修飾,使其無(wú)法在類(lèi)的外部通過(guò)new來(lái)創(chuàng)建實(shí)例。
getInstance方法首先會(huì)判斷實(shí)例是否存在,如果不存在才去新建實(shí)例,如果實(shí)例已存在則直接返回現(xiàn)有實(shí)例。這確保了整個(gè)程序中只會(huì)創(chuàng)建該類(lèi)的一個(gè)實(shí)例。
測(cè)試代碼中,s1和s2實(shí)際上是獲取的是同一個(gè)實(shí)例對(duì)象。
圖片
單例模式的優(yōu)點(diǎn):
單例模式的缺點(diǎn):
Singleton單例:在單例類(lèi)的內(nèi)部實(shí)現(xiàn)只生成一個(gè)實(shí)例,同時(shí)提供一個(gè)靜態(tài)方法getInstance()方法,讓用戶可以訪問(wèn)它的唯一實(shí)例;為了防止在外部對(duì)單例類(lèi)實(shí)例化,它的構(gòu)造函數(shù)可見(jiàn)性為private;在單例類(lèi)內(nèi)部定義了一個(gè)Singleton類(lèi)型的靜態(tài)屬性instance,作為提供給外部共享訪問(wèn)的唯一實(shí)例。
餓漢式單例類(lèi):當(dāng)類(lèi)被加載時(shí),靜態(tài)屬性instance會(huì)被初始化,此時(shí)類(lèi)的私有構(gòu)造函數(shù)會(huì)被調(diào)用,單例類(lèi)的唯一實(shí)例將會(huì)被創(chuàng)建。
普通單例模式和餓漢式單例模式的區(qū)別:
下面我們使用TypeScript代碼實(shí)現(xiàn)一個(gè)餓漢式單例:
class Singleton { private static instance = new Singleton(); private constructor() {} public static getInstance() { return Singleton.instance; }}const s1 = Singleton.getInstance();const s2 = Singleton.getInstance(); console.log(s1 === s2); // true
餓漢式單例由于在類(lèi)加載時(shí)就完成了初始化,所以理論上它是線程安全的,在多線程環(huán)境下也能保證單例。
但餓漢式也有可能造成不必要的實(shí)例化,如果這個(gè)單例的實(shí)例對(duì)象較大,而客戶端又沒(méi)調(diào)用getInstance方法,那就會(huì)浪費(fèi)內(nèi)存。
其實(shí)懶漢式單例模式,就是前面提到的普通單例模式。
懶漢式單例模式實(shí)現(xiàn)代碼如下:
class Singleton { private static instance: Singleton; private constructor() {} public static getInstance(): Singleton { if (!Singleton.instance) { Singleton.instance = new Singleton(); } return Singleton.instance; }}
但是,這種實(shí)現(xiàn)方式存在一個(gè)問(wèn)題,就是在多線程環(huán)境下會(huì)存在安全隱患。
如果有兩個(gè)線程A和B,它們同時(shí)調(diào)用 getInstance 方法,并且實(shí)例還沒(méi)有被初始化,那么它們會(huì)同時(shí)執(zhí)行 Singleton.instance = new Singleton();這行代碼。
這樣就會(huì)導(dǎo)致實(shí)際創(chuàng)建了兩個(gè)實(shí)例,違反了單例模式的初衷。
為了使懶漢式單例在多線程中也是安全的,我們可以對(duì)getInstance方法加鎖:
class Singleton { private static instance: Singleton; private constructor() {} public static getInstance(): Singleton { if (!Singleton.instance) { // 加鎖 lock() if (!Singleton.instance) { Singleton.instance = new Singleton(); } // 釋放鎖 unlock() } return Singleton.instance; }}
這樣當(dāng)一個(gè)線程進(jìn)入該方法時(shí),其它線程就只能等待,直到鎖被釋放后才能進(jìn)入方法。
這就確保了單例實(shí)例的唯一性。這里的鎖機(jī)制可以使用互斥量mutex等各種鎖的實(shí)現(xiàn)。
以上是關(guān)于懶漢式單例線程安全性問(wèn)題的一個(gè)補(bǔ)充說(shuō)明。讓我們的單例模式實(shí)現(xiàn)更加健壯。
懶漢式相比餓漢式更加靈活,但需要處理多線程安全問(wèn)題。餓漢式編寫(xiě)簡(jiǎn)單但不太高效。
在實(shí)際開(kāi)發(fā)中,我們可以根據(jù)需求選擇合適的實(shí)現(xiàn)方式,也可以采用雙重校驗(yàn)鎖等線程安全的懶漢式實(shí)現(xiàn)。
餓漢式單例類(lèi)不能實(shí)現(xiàn)延遲加載,不管將來(lái)用不用,它始終占據(jù)內(nèi)存;懶漢式單例類(lèi)線程安全控制繁瑣,而且性能收到影響。對(duì)此,無(wú)論是餓漢式單例還是懶漢式單例都在一些問(wèn)題,使用IoDH(Initialization on Demand Holder)可以結(jié)合兩者的優(yōu)點(diǎn),克服兩者的缺點(diǎn)實(shí)現(xiàn)性能和實(shí)現(xiàn)更優(yōu)的單例模式。
IoDH是一種技術(shù)方案,它利用了類(lèi)的靜態(tài)屬性來(lái)實(shí)現(xiàn)延遲加載和線程安全。要實(shí)現(xiàn)IoDH,只需在但李磊中增加靜態(tài)內(nèi)部類(lèi)即可,在該內(nèi)部類(lèi)中創(chuàng)建單例對(duì)象,再將該單例對(duì)象通過(guò)getInstance()方法返回給外部使用。
// 單例服務(wù)接口interface SingletonService { doSomething(): void; }// 單例服務(wù)類(lèi)class SingletonServiceImpl implements SingletonService { doSomething() { console.log('Doing something...'); }}// IoC容器類(lèi)class IoCContainer { private singleton: SingletonService; constructor() { this.singleton = new SingletonServiceImpl(); } getSingleton(): SingletonService { return this.singleton; }}// 測(cè)試代碼const container = new IoCContainer();const s1 = container.getSingleton();const s2 = container.getSingleton();console.log(s1 === s2); // true
詳細(xì)解析一下使用IoC容器實(shí)現(xiàn)單例模式的代碼:
這樣通過(guò)IoC容器管理單例的創(chuàng)建,可以實(shí)現(xiàn):
單例模式作為一種設(shè)計(jì)模式,由于具有明確的目的、簡(jiǎn)單的結(jié)構(gòu)和易于理解的特點(diǎn),在軟件開(kāi)發(fā)中使用頻率很高,在許多應(yīng)用程序和框架中都有廣泛應(yīng)用。
總之,單例模式是一種利用率較高的設(shè)計(jì)模式,其限制實(shí)例個(gè)數(shù)的特點(diǎn)可以帶來(lái)節(jié)省資源的優(yōu)勢(shì),但也可能導(dǎo)致擴(kuò)展性較弱以及與語(yǔ)言環(huán)境不夠匹配等問(wèn)題。在軟件設(shè)計(jì)中,開(kāi)發(fā)者需要權(quán)衡考慮系統(tǒng)的需求和優(yōu)缺點(diǎn),適當(dāng)使用單例模式。
本文鏈接:http://www.www897cc.com/showinfo-26-92-0.html三言兩語(yǔ)說(shuō)透設(shè)計(jì)模式的藝術(shù)-單例模式
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com