一.引入
小菜遇到的问题:“工具箱”窗体无法让其只出现一次。
大鸟的回答:“工具箱”类也需要计划生育。
二.解决过程
① 最初的写法:
<span style="font-family:KaiTi_GB2312;"><span style="font-size:24px;">private void Form1_Load(object sender,EventArgs e) { this.IsMdiContainer=true; } private void TolStripMenuItemToolbox_Click(object sender,EventArgs e) { FormToolbox ftb =new FormToolbox(); ftb.MdiParent=this; ftb.Show(); }</span></span>
这样,点击一次工具箱按钮,就会出现一个窗体。就像下图一样:
② 判断对象是否为null:
<span style="font-family:KaiTi_GB2312;"><span style="font-size:24px;">private FormToolbox ftb; private void toolStripMenuItem1_Click(object sender, EventArgs e) { if (ftb == null) //判断是否实例化过 { ftb = new FormToolbox(); ftb.MdiParent = this; ftb.Show(); } }</span></span>这样,确实可以做到只允许一个工具箱窗体出现,可是在程序未退出前关闭了窗体一次,下一次点击时工具箱窗体也将不再出现。原因很简单,就是因为已经实例化一次。
③ 增加IsDisposed属性判断:
<span style="font-family:KaiTi_GB2312;"><span style="font-size:24px;">if (ftb == null || ftb.IsDisposed) { ftb = new FormToolbox(); ftb.MdiParent = this; ftb.Show(); }</span></span>
这样,就可以解决在②中遇到的问题,即使关闭过,也可再次实例化。
④ 将上述方法进行抽象整理:
<span style="font-family:KaiTi_GB2312;"><span style="font-size:24px;">private FormToolbox ftb; private void ToolStripMenuItem_Click(object sender, EventArgs e) { openFormToolbox(); } private void toolStripButton1_Click(object sender, EventArgs e) { openFormToolbox(); } private void openFormToolbox() { if (ftb == null || ftb.IsDisposed) { ftb = new FormToolbox(); ftb.MdiParent = this; ftb.Show(); } }</span></span>这样,看似已经没有问题,可实际上有个重要的问题就是究竟应该由谁负责判断是否实例化。按道理说,应该由工具箱自己判断,而在这里是在主窗体Form1代码里判断。所以,还需要修改。
⑤ 在工具箱类中判断实例化:
<span style="font-family:KaiTi_GB2312;"><span style="font-size:24px;">Public partial class FormToolbox:Form { <span style="white-space:pre"> </span>private static FormToolbox ftb=null; <span style="white-space:pre"> </span>private FormToolbox() <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>InitializeComponent(); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>public static FormToolbox GetInstance() <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>if (ftb==null || ftb.IsDisposed) <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>ftb=new FormToolbox(); <span style="white-space:pre"> </span>ftb.MdiParent=Form1.ActiveForm; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>return ftb; <span style="white-space:pre"> </span>} }</span></span>
这样,才真正做到了自己负责。
三.应用
单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点。
创建单例类:
<span style="font-family:KaiTi_GB2312;">class Singleton { private static Singleton instance; private Singleton() { } public static Singleton GetInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }</span>
四.扩展
① 多线程时的单例
场景举例:两个人进屋,一个人打开门进去了,当第二个人来时,同样也是自己打开门就进了,这样就相当于创建出了多个实例。
为了防止此类现象的发生,就需要加一把锁。一个人进屋后把门锁上,直至离开才开锁,另一个人才可进入。这样就相当于控制只创建了一个实例。
<span style="font-family:KaiTi_GB2312;">public static Singleton GetInstance() { lock (synRoot) { <span style="white-space:pre"> </span>if (instance == null) <span style="white-space:pre"> </span>{<span style="white-space:pre"> </span> <span style="white-space:pre"> </span>instance = new Singleton(); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>return instance; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span> }</span>
② 双重锁定
场景再现:两个人进屋,按照上述方法,不管屋里有没有人,都要先上锁。这样如果没有人,反而造成了不必要的麻烦。所以,先进行判断,是否实例化过。如果实例化了,就上锁,没有则直接进入。
<span style="font-family:KaiTi_GB2312;">public static Singleton GetInstance() { if (instance == null) { lock (syncRoot) { if (instance == null) { instance = new Singleton(); } } } return instance; }</span>
③ 静态初始化
场景再现:进屋的第一个人,阻止别人进来(想象成他是害怕屋里的好吃的也被其他人吃),除非自己已离开。所以也称之为饿汉式单例类。
<span style="font-family:KaiTi_GB2312;">public sealed class Singleton//阻止发生派生,派生可能会增加实例 { private static readonly Singleton instance=new Singleton();//readonly意味只能在初始化期间或构造函数中分配变量instance private static Singleton GetInstance() { return instance; } }</span>五.学习心得
单例模式的这一遍总结,不仅仅只是看了一遍才完成的,每一遍,自己都会在以前的基础上学到更多。联系到机房收费系统,第一遍的时候,我们会遍历每个窗体从而使得界面只允许一个加载一个窗体。现在我们学习了单例模式,这时,就可以加以应用。设计模式的学习重要的不是实现每个代码,应该是去理解,等到以后能够学以致用。