单例模式的几种实现方式,使用场景以及优缺点
设计模式之单例模式
单例模式:单例对象的类只允许一个实例的存在。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中应用该模式的类一个类只有一个实例,即一个类只有一个对象实例。
单例模式有两种构建方式:
懒汉模式:指全局的单例实例在第一次被使用时构建。
饿汉方式:指全局的单例实例在类装载时构建。
单例模式要求类能够有返回对象的一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance名称)。
单例模式的实现主要是通过以下两个步骤:
- 将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
- 在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
懒汉式推荐用的几种方式:
- 双重检查:
优点:线程安全;延迟加载;效率较高;
Eg:
1 /// <summary> 2 3 /// 单例类(懒汉式) 4 5 /// </summary> 6 7 class Singleton 8 9 { 10 11 #region Fields 12 13 /// <summary> 14 15 /// 用静态变量存储唯一的对象 16 17 /// </summary> 18 19 private static Singleton m_uniqueinstance; 20 21 /// <summary> 22 23 /// 声明锁 24 25 /// </summary> 26 27 private static readonly object Locker = new object(); 28 29 #endregion 30 31 32 33 #region Constructors 34 35 /// <summary> 36 37 /// 私有构造函数 38 39 /// </summary> 40 41 private Singleton() 42 43 { 44 45 } 46 47 #endregion 48 49 50 51 #region Public Methods 52 53 /// <summary> 54 55 /// 静态方法构建唯一实例(双重检查) 56 57 /// </summary> 58 59 /// <returns></returns> 60 61 public static Singleton createInstance() 62 63 { 64 65 if (m_uniqueinstance == null) 66 67 { 68 69 lock (Locker) 70 71 { 72 73 if (m_uniqueinstance == null) 74 75 { 76 77 m_uniqueinstance = new Singleton(); 78 79 Console.WriteLine("我是一个新的对象"); 80 81 } 82 83 } 84 85 } 86 87 else 88 89 { 90 91 Console.WriteLine("路过,未创建新实例"); 92 93 } 94 95 return m_uniqueinstance; 96 97 } 98 99 }
- 内部静态类
优点:资源利用率不高,不执行Get Instance()不被实例化,可以执行该类其他静态方法
缺点:第一次加载时反应不够快
/// <summary> /// 单例类(静态内部类) /// </summary> public class SingletonStaticClass { #region Constructors private SingletonStaticClass() { } #endregion #region Public Methods public static SingletonStaticClass GetInstance() { return SingletonInstance.Instance; } #endregion /// <summary> /// 内部静态类 /// </summary> private static class SingletonInstance { public static SingletonStaticClass Instance = new SingletonStaticClass(); } }
- 使用Lazy延迟加载
/// <summary> /// 单例类(Lazy) /// </summary> class Singleton2 { #region Fields /// <summary> /// lazy实现延迟加载 /// </summary> private static readonly Lazy<Singleton2> lazy = new Lazy<Singleton2>(() => new Singleton2()); #endregion #region Constructors /// <summary> /// 私有构造函数 /// </summary> private Singleton2() { } #endregion #region Properties /// <summary> /// 实例属性 /// </summary> public static Singleton2 Instance { get { return lazy.Value; } } #endregion }
- 静态初始化
/// <summary> /// 密封的单例类 /// </summary> public sealed class SingletonStatic { #region Fields /// <summary> /// 定义实例字段 /// </summary> private static readonly SingletonStatic m_Instance = null; #endregion #region Constructors /// <summary> /// 静态构造函数 /// </summary> static SingletonStatic() { m_Instance = new SingletonStatic(); } /// <summary> /// 私有构造函数 /// </summary> private SingletonStatic() { } #endregion #region Properties /// <summary> /// 实例属性 /// </summary> public static SingletonStatic Instance { get { return m_Instance; } } #endregion }
饿汉式:
优点:1.线程安全
2.在类加载的同时已经创建好了一个静态对象,调用是反应速度快
缺点:资源效率不高,可能GetInstance()永远不会执行到,但执行该类的其他静态方法或者加载了该类,那么这个实例仍然初始化。
/// <summary> /// 单例类(饿汉式) /// </summary> class Singleton1 { #region Fields /// <summary> /// 静态常量来存储唯一的对象 /// </summary> private static Singleton1 Instance = new Singleton1(); #endregion #region Constructors /// <summary> /// 私有构造方法 /// </summary> private Singleton1() { } #endregion #region Public Methods /// <summary> /// 静态方法返回实例 /// </summary> /// <returns></returns> public static Singleton1 GetSingleton1() { return Instance; } #endregion }
单例模式的优点:
- 在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例
2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
3.提供了对唯一实例的受控访问。
4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
5.允许可变数目的实例。
6.避免对共享资源的多重占用。
单例模式的缺点:
- 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
3.单例类的职责过重,在一定程度上违背了“单一职责原则”。
4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
使用注意事项:
1.使用时不能用反射模式创建单例,否则会实例化一个新的对象
2.使用懒单例模式时注意线程安全问题
3.饿单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式)
适用场景:
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的 场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
1.需要频繁实例化然后销毁的对象。
2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
3.有状态的工具类对象。
4.频繁访问数据库或文件的对象。