设计模式学习(三):确保对象的唯一性—单例模式
一、简介
单例模式是用于创建那些在软件系统中独一无二的对象。
二、单例模式的意图
在实际开发过程中经常遇见这样的情况,为了节约系统资源,有时需要确保系统中某个类只有唯一一个实例,当这个唯一实例创建成功之后,无法再创建一个同类型的其他对象,所有的操作都只能基于这个唯一实例。例如,Windows任务管理器在系统中有且仅有一个。
三、单例模式概述
(1)概念
单例模式:确保某一类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类成为单例类,它提供全局访问的方法。
(2)类图
(3)核心代码
1 public class Singleton 2 { 3 //私有构造函数 4 private Singleton(){} 5 6 //静态变量 7 private static Singleton instance = null; 8 9 //公有的静态方法返回该实例 10 public static Singleton GetInstance() 11 { 12 if(instance == null) 13 { 14 instance = new Singleton(); 15 } 16 return instance; 17 } 18 }
说明:
①为了确保Singleton实例的唯一性,需要禁止类的外部直接使用new来创建对象,因此需要将Singleton的构造函数的可见性改为private。
②为了让外界可以访问这个唯一实例,需要在Singleton中定义一个静态的Singleton类型的私有变量。
③为了让外界可以访问这个唯一实例,需要增加一个公有的静态方法来返回这个唯一实例。
(4)单例模式三要点
①某个类只能有一个实例(通过私有构造函数实现)。
②它必须自行创建这个实例。
③它必须自行向整个系统提供这个实例(通过公有的静态方法来实现)。
四、饿汉式单例与懒汉式单例
<1>饿汉式单例
当类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。
①类图
②代码
1 public class EagerSingleton 2 { 3 private static EagerSingleton instance = new EagerSingleton(); 4 5 private EagerSingleton(){} 6 7 public static EagerSingleton GetInstance() 8 { 9 return instance; 10 } 11 }
<2>懒汉式单例
懒汉式单例在第一次调用GetInstance()方法时实例化,在类加载时并不自行实例化,这种技术又称为延迟加载技术,即需要的时候再加载实例。
①类图
②代码
1 public class LazySingleton 2 { 3 private static LazySingleton instance = null; 4 5 private LazySingleton(){} 6 7 public static LazySingleton GetInstance() 8 { 9 if(instance == null) 10 { 11 instance = new LazySingleton(); 12 } 13 14 return instance; 15 } 16 }
<3>两种模式的比较
饿汉式单例类在类加载时就将自己实例化,它的优点在于无须考虑多线程访问问题,可以确保实例的唯一性;从调用速度和反应时间角度来讲,由于单例对象一开始就被创建,因此要优于懒汉式单例。但是无论系统在运行时是否需要使用该单例对象,由于在类加载时该对象就需要创建,因此从资源利用效率角度来讲,饿汉单例不及懒汉单例,而且在系统加载时由于需要创建懒汉式单例对象,加载时间可能会比较长。
懒汉式单例类在第一次使用时创建,无须一直占用系统资源,实现了延迟加载,但是必须处理好多线程同时访问的问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费大量时间,这意味着出现多线程同时首次引用此类的几率比较大,需要通过双重检查锁定机制进行控制,这将导致系统性能受到一定影响。
五、单例模式总结
1.主要优点
(1)单例模式提供了对唯一实例的受控访问。
(2)由于系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
(3)允许可变数目的实例。基于单例模式,开发人员可以自行扩展,使用与控制单例对象相似的方法来制定个数的实例对象,既节省系统资源,又解决了单例模式对象共享过多有损性能的问题。
2.主要缺点
(1)由于单例模式中没有抽象层,因此单例类的扩展有很大困难。
(2)单例类的职责过重,在一定程度上违背了单一职业原则。因为既提供了业务方法,有提供了创建对象的方法,将对象的创建和对象本身的功能耦合在了一起。
(3)很多面向对象预言的运行环境都提供了自动垃圾回收技术,因此,如果实例化的共享对象长时间不利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。
3.使用场景
(1)系统只需要一个实例对象。例如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
(2)客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。