Javascript 設計模式之單例模式

個人博客:github.com/ruizhengyun…javascript

爲何使用單體模式

在回答這個問題以前,先說下什麼是單例模式。 單例模式又叫單體模式,保證一個類僅有一個實例,這意味着第二次使用同一個類建立新對象時,獲得的是與第一次所建立的對象徹底相同,並提供全局訪問點。java

與全局變量的是非

抓住關鍵詞 「惟一」 和 「全局訪問」 的對象,不經讓我想起全局對象git

// 全局對象
var globaObj = {};
複製代碼

but,使用全局變量會有如下問題:es6

  • 命名空間污染(變量名衝突)
  • 維護時不方便管控(容易不當心覆蓋)

全局變量問題折中的應對方案:github

  • 使用命名空間
  • 閉包封裝私有變量(利用函數做用域)
  • ES6的 const/symbol

做用

  • 能很好組織代碼,便於維護和調試,只實例化一次;
  • 可生成本身的命名空間,防止代碼被篡改;
  • 惰性實例化,須要一個對象的時候才建立它,有助於性能提高和減小沒必要要的內存消耗;

說明

  • 單例模式須要用到 private 特性,但 ts 和 java 有,但 es6(javascript) 中沒有;
  • 使用 java 代碼來演示 UML 圖的內容;

代碼演示

1.java 實現設計模式

// 0.0.3/Singleton.java
public class Singleton {
    // 私有化構造函數,即外部不能使用 new Singleton(),外部不能使用new!!
    private Singleton(){}
    // 內部 new
    private Singleton instance = null;
    // 對外接口
    public Singleton getInstance() {
        if(instance === null) {
            // 保證只會 new 一次
            instance = new Singleton();
        }
        return instance;
    }
    
    //對象方法
    public void show(name, pwd) {
        System.out.printIn('展現');
    }
}

public class SingletonDemo {
    public static void main(String[] args) {
        // 不合法
        Singleton object = new Singleton();

        // 正確使用,惟一可用可用對象
        Singleton object = Singleton.getInstance();
        object.show();
    }
}
複製代碼

2.javascript 簡單實現 使用一個變量存儲類實例對象(值初始爲 null/undefined )。進行類實例化時,判斷類實例對象是否存在,存在則返回該實例,不存在則建立類實例後返回。屢次調用類生成實例方法,返回同一個實例對象。閉包

// 0.0.3/Singleton.js
class Singleton {
    constructor(name) {
        this.name = name;
        this.instance = null;
    }
    show() {
        console.log(this.name);
    }
}

Singleton.getInstance = function (name) {
    if (this.instance) {
        return this.instance;
    }
    return this.instance = new Singleton(name);
}

// 實例
// 只能使用靜態函數 getInstance,不能使用 new Singleton(),可是隻能文檔約束
let s1 = Singleton.getInstance('展現1');
s1.show();
let s2 = Singleton.getInstance('展現2');
s2.show();
console.log(s1 === s2); // true


let s3 = new Singleton('展現3');
s3.show();
let s4 = new Singleton('展現4');
s4.show();
console.log(s3 === s4); // false
複製代碼

上面 s1 === s2true,而 s3 === s4false,緣由在於 s1s2 在堆內存中指向同一地址, 而 s3s4 在堆內存開闢了兩套空間。ide

存在問題函數

  • 不夠透明,沒法使用 new 類實例化,只能用文檔約束調用方式 Singleton.getInstance(...);
  • 管理操做與對象建立的操做,功能代碼耦合在一塊兒,不符合**「單一職責原則」**;

3.javascript 透明實現 統一用 new 操做符獲取單例,而不是使用 Singleton.getInstance(...)post

// 0.0.3/Singleton2.js
let Singleton = (function () {
    let instance
    return function (name) {
        if (!instance) {
            this.name = name;
            return instance = this;
        }
        return instance;
    }
})();

Singleton.prototype.show = function () {
    console.log(this.name);
}

// 實例
let s3 = new Singleton('展現3');
s3.show();
let s4 = new Singleton('展現4');
s4.show();
console.log(s3 === s4); // true
複製代碼

透明版解決了簡單版不夠「透明」的問題,又可使用 new 操做符來建立實例對象,瞬間以爲天是藍色,這個顏色真美,看誰也都順眼了。

4.javascript 代理版

// 0.0.3/Singleton3.js
let SingletonProxy = (function () {
    let instance
    function main(name) {
        if (!instance) {
            return instance = new Singleton(name);
        }
        return instance;
    }
    return main
})();

let Singleton = function (name) {
    this.name = name;
}
Singleton.prototype.show = function () {
    console.log(this.name);
}

// 實例
const p1 = new SingletonProxy('代理1');
p1.show(); // 代理1
const p2 = new SingletonProxy('代理2');
p2.show(); // 代理1
console.log(p1 === p2); // true
複製代碼

將管理單例操做,與對象建立操做進行拆分,實現更小的粒度劃分,符合「單一職責原則」。

實現過程

  • 類的構造方法必須私有,不能被外界訪問;
  • 使用類的靜態變量以標記實例對象是否已建立,固然該變量能夠直接指向建立的實例對象;
  • 使用類的靜態方法來返回和建立實例對象;

適用場景

1.模態框(登陸框,信息提高框)

// 0.0.3/SingletonModal.js
class Modal {
    constructor() {
        this.display = 'hide';
    }
    show() {
        if (this.display === 'show') {
            console.log('不可重複展現');
            return
        }
        this.display = 'show';
        console.log('成功展現');
    }
    hide() {
        if (this.display === 'hide') {
            console.log('不可重複隱藏');
            return
        }
        this.display = 'hide';
        console.log('成功隱藏');
    }
}

Modal.getInstance = (function () {
    let instance = null
    return function () {
        if (instance === null) {
            instance = new Modal();
        }
        return instance;
    }
})();

// 實例
let m1 = Modal.getInstance();
let m2 = Modal.getInstance();
m1.show();
m2.show();

m1.hide();
m2.hide();
console.log(m1 === m2);
複製代碼

2.其餘

  • 引用第三方庫(屢次引用只會使用一個庫引用,如 jQuery)
  • 購物車(一個用戶只有一個購物車)
  • 全局態管理 store (Vuex / Redux)

項目中引入第三方庫時,重複屢次加載庫文件時,全局只會實例化一個庫對象,如 jQuery,lodash,moment ..., 其實它們的實現理念也是單例模式應用的一種:

// 引入代碼庫 libs(庫別名)
if (window.libs != null) {
  return window.libs;    // 直接返回
} else {
  window.libs = '...';   // 初始化
}
複製代碼

設計原則驗證

  • 符合單一職責原則,只實例化惟一的對象

你能夠

目錄:Javascript 設計模式小書

上一篇:Javascript 設計模式之工廠模式

下一篇:Javascript 設計模式之適配器模式

相關文章
相關標籤/搜索