单例模式算是最常见和最容易理解一种设计模式了。通常是指某一个类只有一实例存在,存在的空间我认为可以理解为该类所在的应用系统内,还有一种是在某一个容器内单一存在,比如像spring的IOC容器(作用域为singleton的bean在容器内是单例存在的),也可以是个简单的HashMap。
单例模式的实现通常分两种,按习惯叫法是饿汉式和懒汉式,这两种的区别主要在于是否延迟初始化。以下是java的饿汉式单例实现:
public class SingletonDemo { //私有默认构造函数 private SingletonDemo() {} //已经自行实例化 private static final SingletonDemo single = new SingletonDemo(); public static SingletonDemo getInstance() { return single; } }C#的实现与这个基本无异,单例的两个实现步骤是一私有化默认构造函数,使得类不可以在外部通过new操作实例化 (注:可以利用反射实例化),
二是内部自身实例化了一个对象供外部使用。那么取得一个SingletonDemo对象只能通过它的静态方法getInstance()了。我们再来看懒汉式的实现:
public class SingletonDemo { private SingletonDemo() {} //注意这里没有final private static SingletonDemo single=null; public synchronized static SingletonDemo getInstance() { if (single == null) { single = new SingletonDemo(); } return single; } }
C#的实现:
public class SingletonDemo{ private static SingletonDemo instance; private static object _lock=new object(); private SingletonDemo() { } public static SingletonDemo GetInstance() { if(instance==null) { lock(_lock) { if(instance==null) { instance=new SingletonDemo(); } } } return instance; }}
懒汉式主要在于使用时再实例化,可以说二者区别不大。另外懒汉式的一个缺点是要处理多线程调用而产生多个实例的问题,java使用了synchronized同步方法,而C#使用的是lock互斥锁。从这点上来说本人更喜欢饿汉式的简洁。
由上面我们已经知道了两种实现方式区别在于类成员的初始化顺序,我们看看java的成员初始化顺序:静态变量、静态初始化块)>(变量、初始化块)>构造器
很显然我们还可以在静态初始块中为single赋值
static final SingletonDemo single;
static{
single=new SingletonDemo();
//还可以干点其他事,比如启动一个hibernate的SessionFactory,哈哈
}
再看C#的,C#中是没有静态块这一说的,代替它的是静态函数
static readonly SingletonDemo single;
static SingletonDemo()
{
single=new SingletonDemo();
}
这里要提下的是有些人喜欢在静态块中做一些赋值或操作,NHibernate(.net版的hibernate)的示例有这么一段:
public class NHibernateHelper {public static readonly Configuration _Configuration; private const string _CurrentSessionKey = "nhibernate.current_session"; private static readonly ISessionFactory _SessionFactory; static NHibernateHelper() { log4net.Config.XmlConfigurator.Configure(); _Configuration = new Configuration(); _SessionFactory = _Configuration.Configure().BuildSessionFactory(); }}
当hibernate配置文件中的数据库配置存在错误时,这里将出现异常,而由于静态函数只在类初始化时运行一次,所以这个异常是不能弥补的,我们只能重启应用再试一次了。
我们也实现一个在容器内的单例,这回来个C#版的吧:
public class DALFactory { private static Hashtable cacheDAL = new Hashtable(); public static T createDAL() { string CacheKey = typeof(T).FullName;//使用类全名作为key T dal = (T)cacheDAL[CacheKey]; if (dal == null) { lock (cacheDAL) { if (dal == null) { Type t = typeof(T); dal = (T)Activator.CreateInstance(t);//反射实例化类 try { cacheDAL.Add(CacheKey, dal); } catch (ArgumentException) { } } } } return dal; } }
思路很简单,一个键值对容器存放类的实例,使用类全名作为唯一键,要创建类实例时先从容器查找如果有则返回该对象,如果没有则新实例化一个并放入容器。