设计模式之单例模式

理论篇  

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

  Singleton类:

  class Singleton
    {
        private static Singleton instance;

        private Singleton()   //构造方法为private,防止外界利用new构造函数创建此类实例的可能
        {
            
        }
        public  static Singleton GetInstance()   //此方法是获取实例的唯一全局访问点
        {
            return instance ?? (instance = new Singleton());  //??空合并运算符,instance不为空就返回instance,为空就创建实例instance
        }
    }

 

  客户端代码:

            Singleton s1= Singleton.GetInstance();
            Singleton s2=Singleton.GetInstance();
            Console.WriteLine(s1==s2);    //显示true

 

  多线程下的单例模式

  Singleton类(加锁):

class Singleton
    {
        private static Singleton instance;
        private static readonly object syncRoot = new object();
        private Singleton()   //构造方法为private,防止外界利用new构造函数创建此类实例的可能
        {

        }
        public static Singleton GetInstance()   //此方法是获取实例的唯一全局访问点
        {
            lock (syncRoot)
                return instance ?? (instance = new Singleton());  //??空合并运算符,instance不为空就返回instance,
                                                                   //为空就创建实例instance
        }
 }

 

  Singleton类(双重锁)

  

class Singleton
    {
        private static Singleton instance;
        private static readonly object syncRoot = new object();
        private Singleton()   //构造方法为private,防止外界利用new构造函数创建此类实例的可能
        {

        }
        public static Singleton GetInstance()   //此方法是获取实例的唯一全局访问点
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    instance = new Singleton();  //??空合并运算符,instance不为空就返回instance,
                    //为空就创建实例instance
                }
            }
            return instance;
        }
    }

 

  静态初始化

  C#提供了一种静态初始化方法,这种方法不需要编写线程安全代码,即解决多线程下的安全访问问题。静态初始化在加载时就进行实例化

 sealed class SingletonSafe
    {
        private static readonly SingletonSafe instance = new SingletonSafe();

        private SingletonSafe(){}

        public static SingletonSafe GetInstance()
        {
            return instance;
        }
    }

 

  应用篇

 

  学以致用,在项目中使用单例模式。

  需求背景:最近再做一个新的项目,其中有一个需求是这样的,当用户点击按钮对硬件进行操作时,首次操作,需要验证,可以十分钟内免验证。超过十分钟,就有需要验证了,只是不用再输用户名,只需输入密码即可。

  解决步骤:

  1、首先创建一个记录时间情况的类。用于用户操作的时间情况。

   //遥控验证类
    public class ControlTest
    {
        private ControlTest()
        {
            //this.IsInit = isInit;
        }

        private const int Minute = 10;
        //选择了确定还是取消
        public bool IsInit { get; set; }

        //初始化时间
        public DateTime? InitDateTime { get; set; }

  //记录用户名
public string UserName { get; set; } //是否过时 public bool IsTimeOver { get { if (InitDateTime == null) { return true; } DateTime nowdDateTime = DateTime.Now; if (InitDateTime.Value.AddMinutes(Minute)>DateTime.Now) { return false; } return true; } } }

 

  2、创建窗体类代码:

  

 public partial class FrmControlLogin : DevExpress.XtraEditors.XtraForm
    {
        private static FrmControlLogin frmControlLogin;
        private static readonly object syncRoot = new object();
        private FrmControlLogin(ControlTest controlTest)
        {
            InitializeComponent();

            #region 取消函数

            Action btnCancelAction = delegate
            {
                try
                {
                    this.Close();
                    controlTest.IsInit = false;
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            };

            #endregion

            #region 取消操作

            this.btnCancle.Click += delegate
            {

                btnCancelAction();
            };

            #endregion

            #region 确定函数

            Action btnOkAction = delegate
            {
                try
                {
                    string personId = txtUserId.Text.Trim();
                    string password = txtPassWord.Text.Trim();

                    if (chkTest.Checked)
                    {
                        controlTest.InitDateTime = DateTime.Now;
                        controlTest.UserName = personId;
                    }

                    if (!FrmControlLogonDataAccess.CheckUserPassword(personId, password))
                    {
                        throw new Exception("用户名密码错误。");
                    }
                    controlTest.UserName = personId;
                    DialogResult = DialogResult.OK;
                    controlTest.IsInit = true;
                }
                catch (Exception ex)
                {
                    string errMsg = string.Format("登录失败,其原因为:\n{0}", ex.Message);
                    CommonMethodUnit.ShowMessage(this, errMsg, "登录", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            };

            #endregion

            #region 确定操作

            this.btnOk.Click += delegate
            {
                btnOkAction();
            };

            #endregion

            #region 接收键盘输入,ENTER键,ESC键

            this.KeyPreview = true;
            this.KeyDown += (srcObj, keyE) =>
            {
                try
                {
                    switch (keyE.KeyCode)
                    {
                        case Keys.Enter:
                            btnOkAction();
                            break;
                        case Keys.Escape:
                            btnCancelAction();
                            break;
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            };

            #endregion

        }
        private static FrmControlLogin GetFrmControlLogin(ControlTest controlTest)
        {
            if (frmControlLogin == null)
            {
                lock (syncRoot)
                {
                    if (frmControlLogin == null)
                    {
                        frmControlLogin = new FrmControlLogin(controlTest);
                    }
                }
            }
            frmControlLogin.txtUserId.Text = controlTest.UserName;
            frmControlLogin.txtPassWord.Text = "";
            return frmControlLogin;
        }

        public static void ShowDoialog(ControlTest controlTest)
        {
            if (controlTest.IsTimeOver)
            {
                GetFrmControlLogin(controlTest).ShowDialog();
            }

            if (!controlTest.IsInit)
            {
                return;
            }
        }
    }

 

   其中函数GetFrmControlLogin用于创建FrmControlLogin实例,此函数通过双重锁保证线程安全:

 private static FrmControlLogin GetFrmControlLogin(ControlTest controlTest)
        {
            if (frmControlLogin == null)
            {
                lock (syncRoot)
                {
                    if (frmControlLogin == null)
                    {
                        frmControlLogin = new FrmControlLogin(controlTest);
                    }
                }
            }
            frmControlLogin.txtUserId.Text = controlTest.UserName;
            frmControlLogin.txtPassWord.Text = "";
            return frmControlLogin;
        }

 

  有同事建议使用volatile关键字,减少代码量。

  1、把FrmControlLogin声明为volatile。然后就可以函数GetFrmControlLogin中的锁代码去掉,依然保证线程安全。

  private static volatile FrmControlLogin frmControlLogin;

   2、注释掉SyncRoot

  //private static readonly object syncRoot = new object();

    3、修改GetFrmControlLogin函数。

private static FrmControlLogin GetFrmControlLogin(ControlTest controlTest)
        {
            if (frmControlLogin == null)
            {
                frmControlLogin = new FrmControlLogin(controlTest);
            }
            frmControlLogin.txtUserId.Text = controlTest.UserName;
            frmControlLogin.txtPassWord.Text = "";
            return frmControlLogin;
        }

 

  图片就不多传了,就传一张十分钟后,需要输密码的图片吧。

 

                                                      

                                                      参考:《大化设计模式》  作者:程杰

 

posted @ 2015-04-29 14:57  荣码一生  阅读(149)  评论(0编辑  收藏  举报