设计模式之 - 单例模式

489 阅读3分钟
  • 携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情

何为单例?

单例模式是一种创建型设计模式,是一种相对比较简单的设计模式之一,在我们平时的编程中也非常的常见。它能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。

应用场景

  • 如计算机系统操作同一个文件,必须是一个实例。
  • 任务管理器,无论打开多次,它都只有一个窗口
  • 打印机任务,都只能执行一个任务等等

如何使用单例

单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。这样就减少了全局类重复的实例化使用。

  • 如下代码,创建一个ShowDialog类。注意:所有类都有构造方法,如果不指定会生成一个默认的构造方法。 要实现单例就要阻止new,那么如何阻止呢?简单粗暴直接修改构造函数为私有private
public class ShowDialog { }
// 等同于
public class ShowDialog 
{
    public ShowDialog() { }
}

var dialog = new ShowDialog();// 编译不会出现任何问题,可以正常new

// 带有私有构造函数
public class ShowDialog 
{
    private ShowDialog() { }
}
new ShowDialog(); // 编译会出错,无法访问,具有保护级别。
  • 单例模式实现之饿汉式:构造函数改为私有后,那我们如何实例化?我们可以创建一个publish方法,为外界提供获取实例。如下实现方式也称之为饿汉式,即类在初始化的时候,无论是否调用GetInstance 实例都会被初始化,浪费内存。
public static void Button_Click() 
{
    var dialog1 = ShowDialog.GetInstance();
    var dialog2 = ShowDialog.GetInstance();
    if (dialog1 == dialog2)
    {
        Console.WriteLine("是同一个实例!");
    }
}

public class ShowDialog 
{
    private static readonly ShowDialog showDialog = new ShowDialog();
    private ShowDialog() { } // 私有构造函数
    // 获取该类实例的访问点
    public static ShowDialog GetInstance() 
    {
        return showDialog;
    }
}
  • 懒汉式,线程不安全。在多线程模式下,多次调用GetInstance可能会出现多个ShowDialog实例。
public class ShowDialog 
{
    private static ShowDialog showDialog;
    private ShowDialog() { }
    public static ShowDialog GetInstance() 
    {
        if(showDialog == null) //不存在则创建一个实例
            showDialog = new ShowDialog();

        return showDialog;
    }
}
  • 多线程下的单例模式:加锁、双重锁定!具体代码实现如下
public class ShowDialog 
{
    private static ShowDialog showDialog;
    private static object _lock = new object();
    private ShowDialog() { }
    public static ShowDialog GetInstance() 
    {
        if (showDialog == null)
            lock (_lock) 
            {
                if (showDialog == null) 
                {
                    showDialog = new ShowDialog();
                }
            }
        return showDialog;
    }
}

Lock: 在多线程中,使用lock能够让线程按顺序执行,如果块有线程占用,其他线程只能等待!仔细看代码会发现上面有两处showDialog == null判断,第一层判断,如果没有产生实例则走下一步,在多线程中第一个线程进去创建之后出来,第二个线程进去,如果没有第二层判断,那么第二个线程还是会创建一个实例,就没有达到单例效果!

总结

单例模式非常简单,但是用好它也尤为重要。使用单例能减少频繁的对象创建、销毁,减少内存开销,单例模式会阻止实例化,能保证所有对象访问的都是唯一实例,但是在多线程模式下,也需要进行特殊处理,避免重复创建对象。