單例模式算是最常見和最容易理解一種設計模式了。通常是指某一個類只有一實例存在,存在的空間我認爲可以理解爲該類所在的應用系統內,還有一種是在某一個容器內單一存在,比如像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;
}
}