单例模式(singleton)

Posted 茂树24

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式(singleton)相关的知识,希望对你有一定的参考价值。

单例模式(singleton)



单例模式定义

保证类只有一个实例,并且全局可以访问到它

在前端项目中,还是有很多地方要求一个类只能由一个实例的。比如在一个页面中有登录部分,每次用户点击登录可以弹出对话框,再一次点击还是这个对话框,当然除了会有很多方法处理这个需求,比如加遮罩层、或者用户再一次点击的时候隐藏这个对话框;比如有些业务处理的类是全局唯一的。比如curry、factory、proxy等都需要在页面加载的时候进行初始化或者在需要的时候就进行初始化,但是在全局或者整个项目中都是唯一的。还有如果在写业务逻辑的时候在循环的场景下,会判断一个对象创建过或者一个方法调用过,这也是单例模式,为了保证这个对象创建单一、方法执行单一,尤其在事件绑定的时候会有很大的好处。还有一些框架比如jquery本质上也是单例模式。总之只要保证”单一”都可以使用单例模式。

在之前业务中处理单例需求会这样:如果要求创建唯一的对象,那么就是在全局new一个对象之后去使用它。这种方式在小项目整个项目视图比较清楚可以去使用,在大型项目中就必须让每一个开发者开发的每一个模块保证调用这个new的对象而不是自己创建它。
这确实挺难的,为什么不让单例的对象创建和正常对象一样呢?下面从一个坏的设计开始一步一步的分析如何去实现单例模式。

如何实现单例模式

方式一:

var single = (function()
    "use strict";
    function single(arg)
        this.arg = arg;
    
    //得到唯一的single对象
    single.getInstance = (function()
        var instance = null;
        return function(...arg)
            return instance || (instance = new single(...arg));
        
    )();
    return single;
)();

虽然这种方式在使用的时候保证了类实例的单一,还是存在几个问题:

  1. 在使用的时候不够透明,所谓的透明就是和正常类一样用过new 的方式使用它,而不是调用一个静态方法。
  2. 使用多层闭包会导致内存消耗比较严重。
  3. 这个single类本身仅仅是执行与该类相关的行为,增加getInstance无疑是增加了single的行为,每一职责单一。
  4. 正是由于上述没有职责单一,所以如果想取消single单一的需求要去改动single类,不满足开放封闭原则
  5. 因为一个single类将类和单一这俩个行为藕合在一起,复用single类或者复用单一处理行为是很难的。

方式二:

根据js语言new关键字的特性,如果返回一个对象的话,则不会使用内部生成的那个obj。

var single = (function () 
    "use strict";
    var instance = null;

    function single(arg) 
        if (instance) 
            return instance;
        
        this.arg = arg;
        return instance = this;
    ;
    return single;
)();

这种方式保证了创建类的透明性 但是single构造函数的职责不再是之前的创建初始化对象而是增加了控制单一的职责。
也就是说这方式二比起方式一仅仅是处理了第一条缺点。

方式三:

那么我们将上述的single构造函数中执行单一的行为抽象出来使用代理的方式将single构造函数职责单一化。

function single(arg) 
    "use strict";
    this.arg = arg;


function proxySingle(obj) 
    "use strict";
    var instance = null;
    return function (...arg) 
        return instance || (instance = new obj(...arg));
    


var _single = proxySingle(single);

console.log(new _single() === new _single())//true

js中的单例模式

上述将创建single的类与具有单一功能的函数结合在一起完成了保证single创建的单一。这种方式对于java这种语言来说是很实用的,因为这门语言在使用对象之前必须要声明类。在js中,他是完全面向对象的语言,所以完全可以单独的创建一个对象,作为全局的属性存在使用,为了防止在全局中造成命名冲突,一般会增加一个命名空间,比如jquery框架中使用jquery作为命名空间……命名空间就是单例模式的一个应用。

惰性单例

惰性单例在前端应用还是很多的,是一种按需创建对象。不至于在页面初始化的时候就创建无用的对象。
比如一个页面中举登录模块,但是他并不是每一次都是必须的而是当用户点击登录的时候才会出现,所以可以将登陆模块应用惰性,同时登陆模块还是单例的。
其实在方式三中proxySingle类的时候用就是惰性的,一般惰性通过闭包的方式让某一个过程或行为延迟执行。

function login() 
    "use strict";
    var div = document.createElement("div");
    div.innerhtml = "登录";
    document.body.appendChild(div);
    return div;


function proxySingle(fun) 
    "use strict";
    var instance = null;
    return function (...arg) 
        return instance || (instance = fun.apply(this, arg));
    


var _single = proxySingle(single);

以上是关于单例模式(singleton)的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript设计模式(单例模式)

javascript 设计模式1----单例模式

[JavaScript设计模式]惰性单例模式

单例模式(Singleton) 2

Singleton Pattern (单例模式)

java设计模式学习 ----- 单例模式(Singleton)