老生常谈:单例模式

 
单例模式有以下的特点: 

    1 :单例类只可有一个实例。 

    2 :单例类必须自己创建自己这惟一的实例。 

    3 :单例类必须给所有其他对象提供这一实例。

 

     一般创建单例我们常用的有两种方式:


     1:静态变量法,也叫惰性实例化
     2:双重锁定


     静态变量法非常好理解,.net的运行机制决定了静态变量可以形成单件,静态变量是一个存储在内存中的变量.它的机制可以保证同一时间只会存在一个变量.为此我们非常容易的可以写出这样的程序:
    

    1:    

Code
非线程安全的单件#region 非线程安全的单件
       /** 
       /// 定义自身的静态变量实例,默认值为null
       /// 
       private static Singleton instance = null;
       /** 
       /// 私有构造函数
       /// 之所有不是public类型的,因为是为了避免发生new Singleton()产生更多的实例
       /// 
       private Singleton()
       { }
       /** 
       /// 生成实例方法
       /// 
       /// 
 
 
       public static Singleton getInstance()
       {
           //如果实例没有被初始化则实例化变量
           if (instance == null)
           {
               instance = new Singleton();
           
           }
           return instance;
       }
       #endregion
 

    这种对于单线程运行的程序来说是绝对没有问题的,例如运行一个asp.net程序,一般网站都基本用不到多线程,所有上面的例子就足够满足.但是如果是多线程呢?如果同时有两个线开始判断instance==null,如果此时都为真,则创建了两上实例,这样就违背了单件的原则.为此可进行下修改:

 

    2:    

Code
     线程安全的单件(静态变量方法)#region 线程安全的单件(静态变量方法)
       /** 
       /// 定义自身的静态变量实例,在程序运行时初始化变量
       /// instance被申请成readonly,也是为了避免给实例重新赋值.
       /// 
       private static readonly Singleton instance = new Singleton();
       /** 
       /// 私有构造函数
       /// 之所有不是public类型的,因为是为了避免发生new Singleton()产生更多的实例
       /// 
       private Singleton()
       { }
       /** 
       /// 生成实例方法
       /// 
       /// 
 
 
       public static Singleton getInstance()
       {
           return instance;
       }
       #endregion
 

    这种方法在程序初始化时对类实例进行了初始化,利用静态变量以及私有构造方法完成了单件的生成.之所以称这种方式是惰性实例化,是因为,无论你是否调用这个实例,它都会被创建.


    双重锁定:


    我们可以对方法1进行下改造,它的缺点就是非线程安全,既然有缺点当然就要改正了.

    3:     

Code
      线程安全的单件(双重锁定方法1:线程安全但性能较差)#region 线程安全的单件(双重锁定方法1:线程安全但性能较差)
       /** 
       /// 定义自身的静态变量实例,默认值为null
       /// 
       private static Singleton instance = null;
       /** 
       /// 创建一个object对象,同样它也是静态只读,用来实现锁定功能
       /// 
       private static readonly object olock = new object();
       /** 
       /// 私有构造函数
       /// 之所有不是public类型的,因为是为了避免发生new Singleton()产生更多的实例
       /// 
       private Singleton()
       { }
       /** 
       /// 生成实例方法
       /// 
       /// 
 
 
       public static Singleton getInstance()
       {
           //取得实例的时候先锁定对象,然后判定是否存在
           lock (olock)
           {
               //如果实例没有被初始化则实例化变量
               if (instance == null)
               {
                   instance = new Singleton();

               }
               return instance;
           }

       }
       #endregion
 

     这个方法是线程式安全的单件模式,但是它每次生成单件的时候都要给对象加锁,这样也是一种性能消耗.上面说了有缺点就要改,这个方法当然也不例外:

    

     4:      

Code
线程安全的单件(双重锁定方法2:线程安全而且性能优越)#region 线程安全的单件(双重锁定方法2:线程安全而且性能优越)
       /** 
       /// 定义自身的静态变量实例,默认值为null
       /// instance被申请成readonly,也是为了避免给实例重新赋值.
       /// 
       private static Singleton instance = null;
       /** 
       /// 创建一个object对象,同样它也是静态只读,用来实现锁定功能
       /// 
       private static readonly object olock = new object();
       /** 
       /// 私有构造函数
       /// 之所有不是public类型的,因为是为了避免发生new Singleton()产生更多的实例
       /// 
       private Singleton()
       { }
       /** 
       /// 生成实例方法
       /// 
       /// 
 
 
       public static Singleton getInstance()
       {
           if (instance == null)
           {
               //取得实例的时候先锁定对象,然后判定是否存在
               lock (olock)
               {
                   //如果实例没有被初始化则实例化变量
                   if (instance == null)
                   {
                       instance = new Singleton();

                   }

               }

           }
           return instance;

       }
       #endregion
 

     

      这个方法在生成的实例的时候,先判断是否为空,如果不为空则不对对象进行加锁操作直接返回对象实例.同时这种双重锁定对比静态方法来看,有一个优势就是它的实例化延迟到类中,只有调用此类的实例时它才会生成实例.

 

      上面都是常规的单件模式,其实并不是所有的情况都如上面一样.在抽象工厂的应用中经常结合单件模式来应用,使得工厂实例是唯一的.在上一篇 用抽象工厂模式武装新闻组件  中,我用上了抽象工厂,当时在抽象工厂类实例的生成上并没有结合单件模式,所在在这想改造一下.我的程序思路是这样的,有一个抽象工厂基类AbstractFactoryClass,它包含一个public static AbstractFactoryClass GetInstance()方法,作用是生成它的派生类的实例,而这个生成过程是通过反射来完成的.这两个派生类分别是:AbstractFactory_China,AbstractFactory_US.这种情况下要想直接返回AbstractFactoryClass的实例是不可能的,因为它是抽象类,不能直接new AbstractFactoryClass().

      

       先看下这三个类的类图:

       

 

      代码如下:

     

 

Code
//把抽象类设置成静态变量,避免多次加载程序集
        private static AbstractFactoryClass instance=null ;
        /** 
        /// 创建一个object对象,同样它也是静态只读,用来实现锁定功能
        /// 
        private static readonly object olock = new object();
        public static AbstractFactoryClass GetInstance()
        {
            //应用双重锁定来产生工厂单件对象
            if (instance == null)
            {
                lock (olock)
                {
                    
                    if (instance == null)
                    {
                        //取得当前工厂名称
                        string factoryName = ConfigurationSettings.AppSettings["factoryName"].ToString();
                        if (factoryName != "")
                            instance = (AbstractFactoryClass)Assembly.Load("AbstractFactoryCompent").CreateInstance(factoryName);
                        else
                            instance = null;
                    }
                }
            }

            return instance;
        }
 

 

     上面的代码因为基类是抽象类所有没有上面常规程序中的私有构造函数,这种生成抽象工厂类派生类实例的案例并不适用于静态方法构造单件的情况,因为你不能直接通过new的方式来初始化类实例.



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值