单例模式算是最常见和最容易理解一种设计模式了。通常是指某一个类只有一实例存在,存在的空间我认为可以理解为该类所在的应用系统内,还有一种是在某一个容器内单一存在,比如像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<T>()
{
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;
}
}