介绍
单例模式(SingIeton):保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该类实例的方法。
结构图
基本代码
Sigleton类
Sigleton类,定义一个GetInstance操作,允许客户访问它的唯一实例。GetInstance是一个静态方法,主要负责创建自己的唯一实例。
class Sigleton
{
private static Sigleton instance;
private Sigleton() //构造方法让其private,这就堵死了外界利用new创建此类实例的可能
{
}
public static Sigleton GetInstance() //此方法时获得本实例的唯一全局访问点
{
if (instance==null) //若实例不存在,则new一个新实例,否则返回已有的实例
{
instance = new Sigleton();
}
return instance;
}
}
客户端代码
static void Main(string[] args)
{
Sigleton s1 = Sigleton.GetInstance();
Sigleton s2 = Sigleton.GetInstance();
if (s1==s2) //比较两次实例化后对象的结果是实例相同
{
Console.WriteLine("两个对象是相同的实例");
}
Console.Read();
}
例子、代码
在windows应用程序中除默认窗体Form1.cs之外再建立一个FormToolbox.cs窗体。
Form1.cs代码
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.IsMdiContainer = true;
}
private FormToolbox ftb;
private void ToolStripMenuItemToolbox_Click(object sender, EventArgs e)
{
ftb = new FormToolbox();
ftb.MdiParent = this;
ftb.Show();
}
}
实现效果
链接:http://pan.baidu.com/s/1i3YUqoP 密码:iqng(单例模式第一版代码)
每次单击工具箱,都会新实例化出一个工具箱窗体。如果需要单击工具箱按钮只能实例化一次,那么需要怎么做呢?
我们可以把声明的工作放到类的全局变量中,这样就可以判断这个变量是否被实例化过。
private void ToolStripMenuItemToolbox_Click(object sender, EventArgs e)
{
if (ftb == null)
{
ftb = new FormToolbox();
ftb.MdiParent = this;
ftb.Show();
}
}
其他代码不变。
如果客户的需要再次变动,要求在程序的“工具栏”中添加”工具箱“一按钮,我们是否是只需要来粘贴复制呢?
如果我们只是添加一个toolStripButton按钮,然后在去粘贴复制
private void toolStripButton1_Click(object sender, EventArgs e)
{
if (ftb==null)
{
ftb = new FormToolbox();
ftb.MdiParent = this;
ftb.Show();
}
}
其他代码不变
运行后,虽然能够达到我们的目的,但是这个程序中存在Bug,当我们把程序运行后,启动“工具箱”,然后把“工具箱”窗体关闭,再次点击启动按钮的时候,我们的“工具箱”并没有出现。这是因为在我们关闭“工具箱”时,它的实例并没有变为null而是Disposed。我们的判断只是是否等于null,所以“工具箱”窗体不会出现。
那么如何修补这个Bug呢?
我们需要再增加IsDisposed属性的判断
private void toolStripButton1_Click(object sender, EventArgs e)
{
if (ftb==null||ftb.IsDisposed)
{
ftb = new FormToolbox();
ftb.MdiParent = this;
ftb.Show();
}
}
为了保证程序以后增加新的功能调用“工具箱”窗体的时候多出需要实例化的时候,当然粘贴复制肯定不可以l
这时候我们需要将它提炼到一个方法里。
private void openToolbox()
{
if (ftb==null||ftb.IsDisposed)
{
ftb = new FormToolbox();
ftb.MdiParent = this;
ftb.Show();
}
}
这个Bug到这里已经修复好了,下面我们看一下整体的代码
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.IsMdiContainer = true; //也可以直接设置Form1窗体的IsMdiContainter属性为true
}
private FormToolbox ftb;
private void ToolStripMenuItemToolbox_Click(object sender, EventArgs e)
{
openToolbox();
}
private void toolStripButton1_Click(object sender, EventArgs e)
{
openToolbox();
}
private void openToolbox()
{
if (ftb==null||ftb.IsDisposed)
{
ftb = new FormToolbox();
ftb.MdiParent = this;
ftb.Show();
}
}
}
链接:http://pan.baidu.com/s/1qXkRpkK 密码:hnc1(单例模式第二版代码)
单例模式的优点
1、提供了对唯一实例的受控访问。
2、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
3、允许可变数目的实例。
单例模式的缺点
1、由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
2、单例类的职责过重,在一定程度上违背了“单一职责原则”。
3、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
适用环境
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时