Singleton单件(创建型模式)

动机:在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。

意图:可以保证一个类有且只有一个实例,并提供一个访问它的全局访问点。

单线程Singleton模式的几个要点

l         Singleton模式中的实例构造器可以设置为protected以允许子类派生。

l         Singleton模式一般不要支持ICloneable接口(ICloneable接口支持克隆,即用与现有实例相同的值创建类的新实例),因为这可能会导致多个对象实例。

l         Singleton模式一般不要支持序列化,因为这也有可能导致多个对象实例。

http://www.microsoft.com/china/msdn/archives/library/dndotnet/html/objserializ.asp

l         Singleton模式只考虑了对象创建的管理,没有考虑对象销毁的管理。就支持垃圾回收的平台和对象的开销来讲,我们一般没有必要对其销毁进行特殊的管理。

l         不能应对多线程环境:在多线程环境下,使用Singleton模式仍然有可能得到Singleton类的多个实例对象。

 

使用静态方法创建单件:

Spooler.cs代码示例:


using  System;

namespace  GlobalSpooler
{
    
/// <summary>
    
/// Summary description for Spooler.
    
/// </summary>

    public class Spooler     {
        
private static bool instance_flag= false;
        
private Spooler()  {
        }

        
public static Spooler getSpooler() {
            
if (! instance_flag) 
                
return new Spooler ();
            
else
                
return null;
        }


    }

}

ClobSpooler.cs代码示例:

using  System;

namespace  GlobalSpooler
{
    
/// <summary>
    
/// Summary description for Class1.
    
/// </summary>

    class GlobSpooler
    
{
        
static void Main(string[] args)         {
            Spooler sp1 
= Spooler.getSpooler();
            
if (sp1 != null)
                Console.WriteLine (
"Got 1 spooler");
            Spooler sp2 
= Spooler.getSpooler ();
            
if (sp2 == null)
                Console.WriteLine (
"Can't get spooler");
            
//fails at compile time
            
//Spooler sp3 = new Spooler ();
        }

    }

}


让一个类只有一个实例,最容易的方法是在类中嵌入一个静态变量,并在第一个类实例中设置该变量,而且每次进入构造函数都要做检查。不管类有多少个实例,静态变量只能有一个实例。为了防止类被多次实例化,把构造函数声明为私有的,这样只能在类的静态方法里创建一个实例。

优点:如果单件已经存在,不需要考虑异常处理。如需要修改程序,允许该类有两个或三个实例,则修改Spooler类可以很容易实现这一点。

缺点:需要程序员检查getSpooler方法的返回值,以确保它不是空的。让程序员始终记得去检查错误的设想是招致失败的开始,应该尽力避免。

解决方案:创建一个异常类,如果试图多次实例化该类,它回抛出一个异常,这时才需要程序员采取行动,因而这是一个安全的方法。

SingletonException.cs代码示例:

using  System;

namespace  SingleSpooler
{
    
/// <summary>
    
/// Summary description for SingletonException.
    
/// </summary>

    public class SingletonException:Exception
    
{
        
//new exception type for singleton classes
        public SingletonException(string s):base(s) {
            
        }


    }

}


如果已创建则抛出异常

Spooler.cs代码示例:

using  System;

namespace  SingleSpooler
{
    
/// <summary>
    
/// Prototype of Spooler Singleton
    
/// such that only one instane can ever exist.
    
/// </summary>

    public class Spooler     {
        
static bool instance_flag = false//true if one instance
        public Spooler()  {
        
if (instance_flag)
            
throw new SingletonException("Only one printer allowed");
        
else
            instance_flag
=true;     //set flag for one instance
        Console.WriteLine ("printer opened");
        }

    }

}


创建一个类实例

SingleSpooler.cs代码示例:

using  System;

namespace  SingleSpooler
{
    
/// <summary>
    
/// Summary description for Class1.
    
/// </summary>

    public class SingleSpooler     {
        
static void Main(string[] args) 
            Spooler pr1, pr2;
            
//open one printer--this should always work
            Console.WriteLine ("Opening one spooler");
            
try {
              pr1 
= new Spooler();
            }

            
catch (SingletonException e)    {
                Console.WriteLine (e.Message);
            }

            
//try to open another printer --should fail
            Console.WriteLine ("Opening two spoolers");
            
try{
                pr2 
= new Spooler();
            }

            
catch (SingletonException e) {
                Console.WriteLine (e.Message);
            }

        }

    }

}


提供一个单件的全局访问点

解决方案()

在程序的开头创建单件,并将其作为参数传递到需要使用它的类中,如:

pr1 = iSpooler.Instance();

Customers cust = new Customers(pr1);

此方法的缺点是,在某次程序运行中,可能不需要所有的单件,这样会影响程序的性能。

 

解决方案()

在程序创建一个所有单件类的注册表,并使注册表始终是可用的。每次实例化一个单件,都将其记录在注册表中。程序的任何部分都能使用标识符串访问任何一个单件实例,并能取回相应的实例变量。

此方法的缺点是:减少了类型检查,因为注册表中的单件表可能把所有的单件都保存成对象类型。另外,注册表本身也可能是一个单件,必须使用构造函数或其他set函数把它传递给程序的所有部分。

 

提供一个全局访问点的最常用方式是使用类的静态方法。类名始终是可用的,静态方法只能由类调用,不能由类的实例调用,所以,不管程序中有多少个地方调用该方法,永远只能有一个这样的类实例。

单件模式的其他效果

1.         子类花一个单件很难,因为只有在基类单件没有被实例化时,才能实现这一点。

2.         可以很容易修改一个单件,使它有少数几个实例,这样做是允许的而且是有意义的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值