这里写的代码,相当于《Head First 设计模式》的读书笔记,原书是java的,自己在学习的过程中将其翻译为C#:
(一)剖析经典的单件模式实现
单件模式
-- 确保一个类只有一个实例,并提供一个全局访问点
-- 单件模式的类图可以说是所有模式的类图中最简单的
-- 有一些对象其实我们只需一个,如线程池、缓存、对话框、处理偏好设置和注册表的对象、日志对象和充当打印机、显卡等设备的驱动程序的对象等。如果制造出多个实例,可能导致许多问题,如程序的行为异常、资源使用过度,或者结果不一致等
1.新建一个控制台应用程序:SingletonPatternDemo。
2.新建一个类:Singleton.cs
namespace SingletonPatternDemo
{
public class Singleton
{
/// <summary>
/// 利用一个静态变量来记录Singleton类的唯一实例
/// </summary>
private static Singleton _uniqueInstance; //这里是其它变量... /// <summary>
/// 构造器私有化:只能在类内部才能调用构造器
/// </summary>
private Singleton() { } /// <summary>
/// 只能通过该方法获取到对象实例
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
//【注意】如果我们不需要该实例,它就永远不会产生。这就是“延迟实例化”(lazy instantiaze)
return _uniqueInstance ?? (_uniqueInstance = new Singleton()); #region 上行相当于以下代码
//if (_uniqueInstance == null)
//{
// _uniqueInstance = new Singleton();
//} //return _uniqueInstance;
#endregion
} //这里是其它方法...
}
}
下面我们去掉注释看看
namespace SingletonPatternDemo
{
public class Singleton
{
private static Singleton _uniqueInstance; private Singleton() { } public static Singleton GetInstance()
{
return _uniqueInstance ?? (_uniqueInstance = new Singleton());
}
}
}
哇塞,这么简单啊!如果你也这么认为的话,那就错啦......接下来,我们看下第(二)部分
(二)场景应用
巧克力工厂
现代化的巧克力工厂具备计算机控制的巧克力锅炉,锅炉做的事,就是把巧克力和牛奶融在一起,然后送到下一个阶段,以制造成巧克力棒。
这里有一个Choc-O-Holic公司的工业强度巧克力锅炉控制器,用于控制锅炉的日常运作,比如:锅炉内为空时才可以加入原料、锅炉内存在原料并且尚未煮沸时才能够进行煮沸,还有排出牛奶和巧克力的混合物时要求炉内存在已经煮沸的原料等。
下列是巧克力锅炉控制器的代码:
namespace SingletonPatternDemo
{
/// <summary>
/// 巧克力锅炉
/// </summary>
public class ChocolateBoiler
{
private bool Empty { get; set; }
private bool Boiled { get; set; } //代码开始时,锅炉为空,未燃烧
public ChocolateBoiler()
{
Empty = true;
Boiled = false;
} /// <summary>
/// 填充
/// </summary>
public void Fill()
{
//在锅炉内填入原料时,锅炉必须为空;
//填入原料后就把两个属性标识好
if (Empty)
{
//在锅炉内填满巧克力和牛奶的混合物... Empty = false;
Boiled = false;
}
} /// <summary>
/// 排出
/// </summary>
public void Drain()
{
//锅炉排出时,必须是满的,并且是煮过的;
//排出完毕后将Empty标志为true。
if (!Empty && Boiled)
{
//排出煮沸的巧克力和牛奶... Empty = true;
}
} /// <summary>
/// 煮沸
/// </summary>
public void Boil()
{
//煮混合物时,锅炉必须是满的,并且是没有煮过的;
//煮沸后,就把Boiled标识为true。
if (!Empty && !Boiled)
{
//将炉内物煮沸... Boiled = true;
}
}
}
}
试试根据(一)中所学的内容将它修改成单例模式
namespace SingletonPatternDemo
{
/// <summary>
/// 巧克力锅炉
/// </summary>
public class ChocolateBoiler
{
private static ChocolateBoiler _uniqueInstance; //【新增】一个静态变量 private bool Empty { get; set; }
private bool Boiled { get; set; } //代码开始时,锅炉为空,未燃烧
private ChocolateBoiler() //【修改】原来是public
{
Empty = true;
Boiled = false;
} /// <summary>
/// 获取ChocolateBoiler对象实例
/// </summary>
/// <returns></returns>
public static ChocolateBoiler GetInstance() //【新增】一个静态方法
{
return _uniqueInstance ?? (_uniqueInstance = new ChocolateBoiler());
} /// <summary>
/// 填充
/// </summary>
public void Fill()
{
//在锅炉内填入原料时,锅炉必须为空;
//填入原料后就把两个属性标识好
if (Empty)
{
//在锅炉内填满巧克力和牛奶的混合物... Empty = false;
Boiled = false;
}
} /// <summary>
/// 排出
/// </summary>
public void Drain()
{
//锅炉排出时,必须是满的,并且是煮过的;
//排出完毕后将Empty标志为true。
if (!Empty && Boiled)
{
//排出煮沸的巧克力和牛奶... Empty = true;
}
} /// <summary>
/// 煮沸
/// </summary>
public void Boil()
{
//煮混合物时,锅炉必须是满的,并且是没有煮过的;
//煮沸后,就把Boiled标识为true。
if (!Empty && !Boiled)
{
//将炉内物煮沸... Boiled = true;
}
}
}
}
点击查看答案
【问题】万一同时存在多个ChocolateBoiler(巧克力锅炉),可能将发生很多糟糕的事情!... 敬请收看第(三)部分
(三)处理多线程
现在,只要使用lock,就可以很简单地解决(二)中出现的问题了
namespace SingletonPatternDemo
{
public class Singleton
{
/// <summary>
/// 利用一个静态变量来记录Singleton类的唯一实例
/// </summary>
private static Singleton _uniqueInstance; private static readonly object Locker = new object(); //这里是其它变量... /// <summary>
/// 构造器私有化:只能在类内部才能调用构造器
/// </summary>
private Singleton() { } /// <summary>
/// 只能通过该方法获取到对象实例
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
//lock:迫使每个线程在进入该方法之前,需要等候别的线程离开该方法,
// 也就是说,不会有两个线程可以同时进入该方法
lock (Locker)
{
if (_uniqueInstance == null)
{
//【注意】如果我们不需要该实例,它就永远不会产生。这就是“延迟实例化”(lazy instantiaze)
_uniqueInstance = new Singleton();
}
} return _uniqueInstance; } //这里是其它方法...
}
}
但是,现在又出现了性能的问题!...
方案一:使用“急切”创建实例,而不用延迟实例化的做法
namespace SingletonPatternDemo
{
public class Singleton
{
//如果应用程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,可以选择这种方法 //在静态初始化器中创建单件,这段代码保证了线程安全
private static readonly Singleton UniqueInstance = new Singleton(); private Singleton() { } public static Singleton GetInstance()
{
return UniqueInstance;
}
}
}
方案二:用“双重检查加锁”
namespace SingletonPatternDemo
{
public class Singleton
{
private static Singleton _uniqueInstance;
private static readonly object Locker = new object(); private Singleton() { } public static Singleton GetInstance()
{
//检查实例,如果不存在,就进入同步区块
if (_uniqueInstance == null)
{
lock (Locker)
{
if (_uniqueInstance == null)
{
//只有第一次才彻底执行这里的代码
_uniqueInstance = new Singleton();
}
}
} return _uniqueInstance;
} }
}
完毕... ...