设计模式学习笔记(二)——Singleton单件模式
基本的23种设计模式从目的上可分为三种:
1、 创建型(Creational)模式:负责对象创建。
2、 结构型(Structural)模式:处理类与对象间的组合,可以解决一些继承依赖性的问题
3、 行为型(Behavioral)模式:类与对象交互中的职责分配,可以解决组件间如何和交互,隔离变化。
下面来说说单件模式:
首先说说单件模式产生的动机,也就是为什么会出现单件模式。有一些类在系统中只存在一个实例才能确保他们的逻辑正确性以及良好的效率。这时我想到我遇到的一个问题。我曾经遇到一个WinForm程序,运行后出现一个登陆框,输入用户名密码后点击登陆,然后显示一个登陆后的界面。但是点击登陆后,程序要做一些操作,由于这段操作用时相对较长,在不经意时,我有点击了一次登陆按钮,最后出现了两个对话框。如:我现在有两个Form窗体Form1和Form2。Form1上有一个按钮用来打开Form2并隐藏自己。我们可以这样写:
private void button1_Click(object sender, System.EventArgs e)
{
Form2 form = new Form2();
form.Show();
this.Hide();
}
如果我们在显示Form2前由一些比较耗时的操作。如:我们让线程的沉睡10秒在显示Form2,当我们在线程沉睡时继续点击Form1上的Button,有可能就会出现两个Form2的窗体。(我试过可以出现两个Form2,如果你有心试但没事出来别拿西红柿砍我,哈哈)
private void button1_Click(object sender, System.EventArgs e)
{
Thread.Sleep(10000);
Form2 form = new Form2();
form.Show();
this.Hide();
}
这种情况出现不能怪客户多点了一下,也不能说是编译器不够智能,应该是我们程序上的Bug,我想这种情况用单件模式应该可以解决。
单件模式的使用意图就是:保证一个类仅有一个实例,并提供一个该实例全局的访问点(这句话当然不是我先说的,是引用Gof在《设计模式》中的一句话)
那类的设计者如何绕过常规的构造器来实现单件模式呢?下面就来谈谈单件模式的实现。
单件模式在结构上使用了景泰方法来约束构造器(也就是构造函数)创建对象。
在单线程的情况下:私有化构造函数,使类的使用者调用不到这个构造函数来new一个实例。类型中可以自己new一个实例。类中创建一个静态私有变量和Static公有属性。在公有属性中实现此类的实例化。这样在第一次请求时创建此对象。代码如下:
class Singleton
{
private static Singleton _instance;
private Singleton(){}
public static Singleton f_Instance
{
get
{
if(_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
}
我在main函数中写入如下程序来查看一下这样写是否有效:
static void
{
Singleton t1 = Singleton.f_Instance;
Singleton t2 = Singleton.f_Instance;
Console.Write(object.ReferenceEquals(t1,t2));
Console.Read();
}
控制台显示为True,开来还是有效的。当然在Main中我也试过这样写:Singleton t1 = new Singleton(),编译时告诉我Singleton()不可访问(当然,人家是私有的,不是自家人当然不见)
这种单线程下的单件模式有几点要注意:
1、 构造器私有化(如果要此类被继承,可以用protected声明构造器)
2、 不要支持IClinieable接口,因为会导致多个对象实例的出现
3、 不能支持序列化
4、 单件模式只考虑了对象创建的管理,没有考虑对象的销毁管理(创建自己的对象,销毁的事交给垃圾回收器吧)
5、 不能应对多线程环境,因为会导致多个对象实例的出现
那在多线程下如何实现呢?代码如下:
class SingletonMuli//多线程Singleton模式
{
private static volatile SingletonMuli _instance; //volatile是为了让编译器对此代码编译后的位置不进行调整
private SingletonMuli(){}
private static object lockHelper = new object(); //辅助器,不参与对象构建
public static SingletonMuli f_Instance
{
get
{
if(_instance == null)
{
lock(lockHelper)
{
if(_instance == null) //双检查
{
_instance = new SingletonMuli();
}
}
}
return _instance;
}
}
}
当然还有一些更简单的实现方法,如:
class Singleton1//可以用在多线程环境
{
public static readonly Singleton1 _instance = new Singleton1();
private Singleton1(){}
}
其中要提到的是在_instance私有字段的实例化叫做“内联初始化”。内联初始化是指在声明时。
实际上面的代码上相当于如下代码:
Public static readonly Singleton1 _instance;
Static Singleton() //静态构造函数
{
_instance = new Singleton(); //私有构造器
}
Private Singleton(){}
内联初始化时会先执行静态构造器,如果没有静态构造函数,系统会默认一个。在访问此静态字段时执行静态构造器生成。静态构造器保证了在多线程时只有一个线程执行,自动加锁。
当然,第二种实现方式也有一些缺点,如:静态构造器必须是私有的、无参的。不过也可以用其他的方式解决这类问题。如可以用方法属性实现扩展或修改私有构造器。
现在我们可以回来看看我开始说的那两个Form的问题,我们现在可以这样实现:
private static Form2 form;
private void button1_Click(object sender, System.EventArgs e)
{
Thread.Sleep(10000);
object lockhelp = new object();
if(form == null)
{
lock(lockhelp)
{
if(form == null)
{
form = new Form2();
form.Show();
}
}
}
this.Hide();
}
这样问题就解决了(我是没有点出来第二个Form2,如果那位点出来了,给我发Email,我请她/他在天津的烤鸭)
单件模式实际上是利用控制对象创造过程来控制对象的创造个数的方法,我们可以对其进行扩展,不是让他只生成一个对象,可以让他只生成几个对象,这样可以实现对象池。
单件模式的核心是:如何控制用户使用new对一个类的实例构造器的任意调用。