zoukankan      html  css  js  c++  java
  • 单例模式(20)

    今天我们来讲一下单例模式,下面我们来用winform来做一个简单的展示,就是点击一个菜单,弹出另一个窗体(做成父子窗体的形式)。

    建一个窗体(父窗体),拖一个MenuStrip,再建一个窗体(子窗体)。

    然后:

     1         private void Form1_Load(object sender, System.EventArgs e)
     2         {
     3             this.IsMdiContainer = true;
     4         }
     5 
     6         private void 工具栏ToolStripMenuItem_Click(object sender, System.EventArgs e)
     7         {
     8             Form2 form2 = new Form2();
     9             form2.MdiParent = this;
    10             form2.Show();
    11         }

    现在,我们看一下执行结果。

    我们可以看到,每次我们点击一下工具,都会弹出一个新的窗体,我们想要的结果是:之弹出一次这个窗体就行。

    有些伙伴说,可以用ShowDialog() 啊,在此,说明一下,在父子窗体中,子窗体是不能用ShowDialog()出来的,再退一步讲,即便是能用ShowDialog(),但是这是一个阻塞机制,很不灵活。

    那么,为了实现我们想要的结果,我们该如何做呢?

    简单啊,我们只需要修改一下点击事件里的代码就可以了。

    先声明一个子窗体的全局变量,然后修改一下代码:

     1         private Form2 form2;
     2         private void Form1_Load(object sender, System.EventArgs e)
     3         {
     4             this.IsMdiContainer = true;
     5         }
     6 
     7         private void 工具栏ToolStripMenuItem_Click(object sender, System.EventArgs e)
     8         {
     9             if (form2 == null)
    10             {
    11                 form2 = new Form2();
    12                 form2.MdiParent = this;
    13                 form2.Show();
    14             }
    15         }

    这样就可以实现我们想要的结果了,这样就完了嘛?

    这里存在两个问题:

    1、如果我有很多按钮,每个按钮都想弹出这个子窗体,想实现这个结果,需要复制粘贴这些代码,显然是很失败的做法。

    2、上述结果,如果我关闭了打开的窗体,我在点击工具菜单,则不会再弹出窗体来了。(因为关闭窗体后,该窗体仅仅是Disposed了,但对象还不是null,所以判断是否为null是有一定的问题的。)

    针对上述两个问题,我们来改进一下。就用到今天要讲的单例模式了,好,我们来看一下如何实现。

    Form2中的代码

     1     public partial class Form2 : Form
     2     {
     3         //声明一个静态的类变量
     4         private static Form2 form2 = null;
     5         //构造方法私有,外部代码不能直接new来实例化它
     6         private Form2()
     7         {
     8             InitializeComponent();
     9         }
    10         //得到类实例的方法,返回值就是本类对象,注意也是静态的。
    11         public static Form2 GetForm()
    12         {
    13             //当内部的form2是null或者被Dispose过,则new它
    14             //并且设计其MdiParent为Form1,此时将实例化的对象存在静态的变量form2中,以后就可以不用实例化而得到它了
    15             if (form2 == null || form2.IsDisposed)
    16             {
    17                 form2 = new Form2();
    18                 form2.MdiParent = Form1.ActiveForm;
    19             }
    20             return form2;
    21         }
    22     }

    Form1 中的调用

     1     public partial class Form1 : Form
     2     {
     3        
     4         public Form1()
     5         {
     6             InitializeComponent();
     7         }
     8     
     9         private void Form1_Load(object sender, System.EventArgs e)
    10         {
    11             this.IsMdiContainer = true;
    12         }
    13         private void 工具栏ToolStripMenuItem_Click(object sender, System.EventArgs e)
    14         {
    15             Form2.GetForm().Show();
    16         }
    17     }

    这样,就达到了我们想要的效果了。

    我们来总结一下:

    单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    通常我们剋让一个全局变量是的一个对象被访问,但它不能防止你实例化多个对象。最好的办法就是,让类自身负责保存它的唯一实例,这个类可以保证没有其他实例可以被创建,并且他可以提供一个访问该实例的方法。

    好,我们来写一下单例模式的代码

     1     class Singleton
     2     {
     3         private static Singleton instance;
     4         //构造方法让其private,这就堵死了外界利用用new创建次类实例的可能
     5         private Singleton()
     6         {
     7             
     8         }
     9         //此方法是获得本类实例的唯一全局访问点
    10         public static Singleton GetInstance()
    11         {
    12             //若实例不存在,则new一个新实例,否则返回已有的实例
    13             if (instance==null)
    14             {
    15                 instance = new Singleton();
    16             }
    17             return instance;
    18         }
    19     }

    客户端:

     1        public static void Main()
     2         {
     3             Singleton s1 = Singleton.GetInstance();
     4             Singleton s2 = Singleton.GetInstance();
     5             //比较两次实例化后对象的结果是否相同
     6             if (s1==s2)
     7             {
     8                 Console.WriteLine("两个对象是相同的实例。");
     9             }
    10             Console.ReadKey();
    11         }

    另外还有一个问题,如果多线程中,多个线程同时访问Singleton类,调用GetInstance()方法,会有可能创造多个实例的。所以,我们可以用lock进行处理一下。

    好,我们来看用lock处理后的Singleton类

     1     class Singleton
     2     {
     3         private static Singleton instance;
     4         //程序运行时创建一个静态只读的进程辅助对象
     5         private static readonly object syncRoot = new object();
     6         //构造方法让其private,这就堵死了外界利用用new创建次类实例的可能
     7         private Singleton()
     8         {
     9 
    10         }
    11         //此方法是获得本类实例的唯一全局访问点
    12         public static Singleton GetInstance()
    13         {
    14             //在同一个时刻加了锁的那部分程序只有一个线程可以进入
    15             lock (syncRoot)
    16             {
    17                 //若实例不存在,则new一个新实例,否则返回已有的实例
    18                 if (instance == null)
    19                 {
    20                     instance = new Singleton();
    21                 }
    22             }
    23             return instance;
    24         }
    25     }

    对于上述的代码,小伙伴们发现了一个问题没有,就是不管instance是不是为null,都会先加锁,这势必会影响性能的,好我们来看一下优化后的:

     1     class Singleton
     2     {
     3         private static Singleton instance;
     4         //程序运行时创建一个静态只读的进程辅助对象
     5         private static readonly object syncRoot = new object();
     6         //构造方法让其private,这就堵死了外界利用用new创建次类实例的可能
     7         private Singleton()
     8         {
     9 
    10         }
    11         //此方法是获得本类实例的唯一全局访问点
    12         public static Singleton GetInstance()
    13         {
    14             //先判断实例是否存在,如果不存在再加锁处理
    15             if (instance==null)
    16             {
    17                 //在同一个时刻加了锁的那部分程序只有一个线程可以进入
    18                 lock (syncRoot)
    19                 {
    20                     //若实例不存在,则new一个新实例,否则返回已有的实例
    21                     if (instance == null)
    22                     {
    23                         instance = new Singleton();
    24                     }
    25                 } 
    26             }
    27             return instance;
    28         }
    29     }

    其实再实际应用当中,C#与用功语言运行库也提供了一种“静态初始化”方法,这种方法不需要开发人员显示的编写线程安全代码,即可解决多线程环境下他是不安全的问题。

    好,下面我们来看一下“静态初始化”方法的单例模式

     1     //sealed阻止发生派生,而派生可能会增加实例
     2     public sealed class Singleton
     3     {
     4         //在第一次引用类的任何成员时创建实例,共功与原运行库负责处理变量初始化。
     5         private static readonly Singleton instance = new Singleton();
     6         private Singleton()
     7         {
     8         }
     9 
    10         public static Singleton GetInstance()
    11         {
    12             return instance;
    13         }
    14     }

    这种静态初始化的方式是自己被加载时就将自己实例化,所以被形象的成为恶汉式单例类,原先的单例模式处理是要再第一次被引用时,才会将自己实例化,所以被称为懒汉单例类。

    好,单例模式我们就介绍完了,下一篇博文我们讲 桥接模式


    本系列将持续更新,喜欢的小伙伴可以点一下关注和推荐,谢谢大家的支持

  • 相关阅读:
    套接字I/O函数write/read writev/readv send/recv sendto/recvfrom sendmsg/recvmsg
    套接字之recvmsg系统调用
    套接字之recv系统调用
    套接字之recvfrom系统调用
    套接字之sendmsg系统调用
    套接字之send系统调用
    数据类型
    简单的c程序分析
    c语言函数分析
    堆栈图
  • 原文地址:https://www.cnblogs.com/xiaomowang/p/6392954.html
Copyright © 2011-2022 走看看