轉:單例模式
參考:
單例模式-維基百科
JavaScript 設計模式之單例模式
JS設計模式一:單例模式
設計模式——單例模式
1. 概念
單例模式,也叫單子模式,是一種常用的軟件設計模式。
在應用這個模式時,單例對象的類必須保證只有一個實例存在。
許多時候整個系統只需要擁有一個的全局對象,這樣有利於我們協調系統整體的行為。
比如在某個服務器程序中,該服務器的配置信息存放在一個文件中,這些配置數據由一個單例對象統一讀取,然後服務進程中的其他對象再通過這個單例對象獲取這些配置信息。這種方式簡化了在復雜環境下的配置管理。
單例模式的實現思路:getInstance
這個名稱)
當我們調用這個方法時:
-
如果類持有的引用不為空就返回這個引用
-
如果類保持的引用為空就創建該類的實例並將實例的引用賦予該類保持的引用
let obj if(!obj) { obj = xxx } return obj
單例模式在多線程的應用場合下必須小心使用。如果當唯一實例尚未創建時,有兩個線程同時調用創建方法,那麽它們同時沒有檢測到唯一實例的存在,從而同時各自創建了一個實例,這樣就有兩個實例被構造出來,從而違反了單例模式中實例唯一的原則。 解決這個問題的辦法是為指示類是否已經實例化的變量提供一個互斥鎖(雖然這樣會降低效率)。
通常單例模式在Java中,有兩種構建方式:
-
懶漢方式。指全局的單例實例在第一次被使用時構建。
-
餓漢方式。指全局的單例實例在類裝載時構建。
2.1 例子(維基百科)
在Java語言中,單例模式(餓漢模式)應用的例子如下述代碼所示:
public class Singleton { //第一次加載類的時候就實例化,static以保證所有的class都使用這一個實例 private static final Singleton INSTANCE = new Singleton(); // Private constructor suppresses// default public constructor private Singleton() {}; // 這個 INSTANCE 是不能在外部直接 new Singleton.getInstance() 來訪問 public static Singleton getInstance() { return INSTANCE; } }
在Java編程語言中,單例模式(懶漢模式)應用的例子如下述代碼所示 (此種方法只能用在JDK5及以後版本(註意 INSTANCE 被聲明為 volatile),之前的版本使用“雙重檢查鎖”會發生非預期行為[1]):
public class Singleton { private static volatile Singleton INSTANCE = null; // Private constructor suppresses // default public constructor private Singleton() {}; //Thread safe and performance promote public static Singleton getInstance() { if(INSTANCE == null){ synchronized(Singleton.class){ // When more than two threads run into the first null check same time, // to avoid instanced more than one time, it needs to be checked again. if(INSTANCE == null){ INSTANCE = new Singleton(); } } } return INSTANCE; } }
3.1 從命名空間說單例模式
單例模式的作用:
-
模塊之間的通信
-
系統中某個類的對象只能存在一個
-
保護自己的屬性
註意事項:
-
註意 this 的使用
-
閉包容易造成內存泄漏
-
註意 new 的成本(繼承)
3.1.1 單例模式概念解讀
單例就是保證一個類只有一個實例,實現的方法一般先是判斷實例存在與否,如果存在直接返回,如果不存在就創建了再返回,這就確保了一個類只有一個實例對象。
在 JavaScript 裏,單例作為一個命名空間提供者,從全局命名空間裏提供一個唯一的訪問點來訪問該對象。
學好單例模式,在開發中將能很好的控制命名空間,避免變量汙染等。
3.1.2 單例模式實現
1.如果房子沒有門,就找開發商造一個門;如果房子已經有門,那麽就直接用這個門;
2.小王跟小李的兩扇門分別歸屬各自的房子,只有唯一的一個門,擁有唯一的門牌號,之間又可以通信;
(各自唯一的一扇門,可以保護各自的家不被壞人侵入)
通過圖片展示一個例子:
// 1. 創建2個獨立的對象: xiaowang 和 xiaoli // 2. xiaowang 去 xiaoli 家,通過門鈴與 xiaoli 通信 // 3. 判斷 xiaowang 家有沒有門鈴:有門鈴,直接通過門鈴通信‘ding ding ding‘;沒有新建門鈴 // 4. 倆個單例開始通信 // 動態單例,需要的時候才 new 一個出來 let xiaowang = (() => { const xiaowangjia = function(message) { this.menling = message } let men const info = { sendMessage: function(message) { if(!men) men = new xiaowangjia(message) return men } } return info })() // 靜態單例,常駐內存 const xiaoli = { responseXiaowang: function(msg) { let _xw = xiaowang.sendMessage(msg) console.log(_xw.menling) _xw = null } } xiaoli.responseXiaowang(‘我在家呢!‘)
案例:假設有一個需求是點擊登錄需要彈出一個登錄框:這個登錄窗在頁面裏總是唯一的;不可能同時存在兩個登錄窗口的情況。
簡單實現:
let createLoginLayer = (() => { let div return () => { if(!div) { div = document.createElement(‘div‘) div.innerHTML = ‘展示登錄框‘ div.style.display = ‘none‘ document.body.appendChild(div) } return div } })()
<button id="loginBtn">按鈕</button> <script> var num = 1 var createLoginLayer = function() { num++ var div = document.createElement(‘div‘); div.innerHTML = ‘登錄窗‘; div.style.display = ‘none‘; document.body.appendChild(div); return div; } var getSingle = function(fn) { var result; return function() { // getSingle 執行完 reault 沒有被回收,而是指向 fn 的返回值,形成閉包 return result || (result = fn.apply(this, arguments)); } } var createSingleLoginLayer = getSingle(createLoginLayer); document.getElementById(‘loginBtn‘).onclick = function() { console.log(num); var loginLayer = createSingleLoginLayer(); loginLayer.style.display = ‘block‘; } </script>
轉:單例模式