zoukankan      html  css  js  c++  java
  • js单例模式

    单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

    为了帮助大家更好地理解单例模式,大家可以结合下面的类图来进行理解,以及后面也会剖析单例模式的实现思路:

    下面就看看具体的实现代码(看完之后你会惊讶道:真是这样的!):

      /// <summary>
        /// 单例模式的实现
        /// </summary>
        public class Singleton
        {
            // 定义一个静态变量来保存类的实例
            private static Singleton uniqueInstance;
    
            // 定义私有构造函数,使外界不能创建该类实例
            private Singleton()
            {
            }
    
            /// <summary>
            /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
            /// </summary>
            /// <returns></returns>
            public static Singleton GetInstance()
            {
                // 如果类的实例不存在则创建,否则直接返回
                if (uniqueInstance == null)
                {
                    uniqueInstance = new Singleton();
                }
                return uniqueInstance;
            }
        }

    上面的单例模式的实现在单线程下确实是完美的,然而在多线程的情况下会得到多个Singleton实例,因为在两个线程同时运行GetInstance方 法时,此时两个线程判断(uniqueInstance ==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了,既然上面的实现会运行多个线程执 行,那我们对于多线程的解决方案自然就是使GetInstance方法在同一时间只运行一个线程运行就好了,也就是我们线程同步的问题了(对于线程同步大家也可以参考我线程同步的文章),具体的解决多线程的代码如下:

    /// <summary>
        /// 单例模式的实现
        /// </summary>
        public class Singleton
        {
            // 定义一个静态变量来保存类的实例
            private static Singleton uniqueInstance;
    
            // 定义一个标识确保线程同步
            private static readonly object locker = new object();
    
            // 定义私有构造函数,使外界不能创建该类实例
            private Singleton()
            {
            }
    
            /// <summary>
            /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
            /// </summary>
            /// <returns></returns>
            public static Singleton GetInstance()
            {
                // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
                // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
                // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
                lock (locker)
                {
                    // 如果类的实例不存在则创建,否则直接返回
                    if (uniqueInstance == null)
                    {
                        uniqueInstance = new Singleton();
                    }
                }
    
                return uniqueInstance;
            }
        }

    上面这种解决方案确实可以解决多线程的问题,但是上面代码对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在,对于 这个操作完全没有必要的,因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(uniqueInstance==null)为假,此时 完全没必要对线程辅助对象加锁之后再去判断,所以上面的实现方式增加了额外的开销,损失了性能,为了改进上面实现方式的缺陷,我们只需要在lock语句前 面加一句(uniqueInstance==null)的判断就可以避免锁所增加的额外开销,这种实现方式我们就叫它 “双重锁定”,下面具体看看实现代码的:

    /// <summary>
        /// 单例模式的实现
        /// </summary>
        public class Singleton
        {
            // 定义一个静态变量来保存类的实例
            private static Singleton uniqueInstance;
    
            // 定义一个标识确保线程同步
            private static readonly object locker = new object();
    
            // 定义私有构造函数,使外界不能创建该类实例
            private Singleton()
            {
            }
    
            /// <summary>
            /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
            /// </summary>
            /// <returns></returns>
            public static Singleton GetInstance()
            {
                // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
                // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
                // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
                // 双重锁定只需要一句判断就可以了
                if (uniqueInstance == null)
                {
                    lock (locker)
                    {
                        // 如果类的实例不存在则创建,否则直接返回
                        if (uniqueInstance == null)
                        {
                            uniqueInstance = new Singleton();
                        }
                    }
                }
                return uniqueInstance;
            }
        }

    理解完了单例模式之后,菜鸟又接着问了:.NET FrameWork类库中有没有单例模式的实现呢?

    经过查看,.NET类库中确实存在单例模式的实现类,不过该类不是公开的,下面就具体看看该类的一个实现的(该类具体存在于System.dll程序集,命名空间为System,大家可以用反射工具Reflector去查看源码的):

    // 该类不是一个公开类
        // 但是该类的实现应用了单例模式
        internal sealed class SR
        {
            private static SR loader;
            internal SR()
            {
            }
            // 主要是因为该类不是公有,所以这个全部访问点也定义为私有的了
            // 但是思想还是用到了单例模式的思想的
            private static SR GetLoader()
            {
                if (loader == null)
                {
                    SR sr = new SR();
                    Interlocked.CompareExchange<SR>(ref loader, sr, null);
                }
                return loader;
            }
    
            // 这个公有方法中调用了GetLoader方法的
            public static object GetObject(string name)
            {
                SR loader = GetLoader();
                if (loader == null)
                {
                    return null;
                }
                return loader.resources.GetObject(name, Culture);
            }
        }

    js单例模式:

    var createLoginLayer = (function(){
        var div;
        return function(){
            if(!div){
                div = document.createElement('div');
                div.innerHTML = '我是登录浮窗'; 
                div.style.display = 'none';
                document.body.appendChild(div);
            }
            return div;
        }
    })(); 
    
    调用:document.getElementById('loginBtn').onclick = function(){
                    var loginLayer = createLoginLayer();
                    loginLayer.style.display = 'block';
                }; 

    用代理实现单例模式:

    var CreateDiv = function(html){
        this.html = html;
        this.init();
    };
            
    CreateDiv.prototype.init = function(){
        var div = document.createElement('div');
        div.innerHTML = this.html;
        document.body.appendChild(div);
    };
    
    var ProxySingletonCreateDiv = (function(){
        var instance;
        return function(html){
            if(!instance){
                instance = new CreateDiv(html);
            }
            return instance;
        }
    })();
    
    var a = new ProxySingletonCreateDiv('sven1');
    var b = new ProxySingletonCreateDiv('sven2');

    js通用的惰性单列 :

    var getSingle = function(fn){
        var result;
        return function(){
            return result || ( result = fn.apply(this,arguments));
        }
    };
    
    var createLoginLayer = function(){
        var div = document.createElement('div');
        div.innerHTML = '我是登录浮窗'; 
        div.style.display = 'none';
        document.body.appendChild(div);
        return div;
    };
    
    var createSingleLoginLayer = getSingle(createLoginLayer);
    
    document.getElementById('loginBtn').onclcik = function(){
        var loginLayer = createSingleLoginLayer();
        loginLayer.style.display = 'block';
    }; 
    
    
    var createSingleIframe = getSingle(function(){
        var iframe = document.createElement('iframe');
        document.body.appendChild(iframe);
        return iframe; 
    });
    
    document.getElementById('loginBtn').onclick = function(){
        var loginLayer = createSingleIframe();
        loginLayer.src = 'http://baidu.com';
    }; 
  • 相关阅读:
    Knight Moves
    Knight Moves
    Catch him
    Catch him
    Linux查看硬件信息以及驱动设备的命令
    23种设计模式彩图
    Android开发指南-框架主题-安全和许可
    Android启动组件的三种主流及若干非主流方式
    ACE在Linux下编译安装
    void及void指针含义的深刻解析
  • 原文地址:https://www.cnblogs.com/gongshunkai/p/6627358.html
Copyright © 2011-2022 走看看