有些时候我们需要只让同一个类只有一个对象,比如Unity的各个面板,我们在打开面板的时候,如果该面板已经显示出来了,那么我们没有必要在打开一个新的面板,直接使用已有的面板就行。这时候我们就要使用单例模式。
单例模式 Singleton
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们可以让一个全局变量使得一个访问对象被访问,但它不能防止你的实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
类图
代码
单线程下的单例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 单例设计模式
{
class Singleton
{
//保存实例
private static Singleton Instance { get; set; }
//构造方法让其private,这就保证了外界不能使用new创建此实例的可能
private Singleton()
{
}
//获得本类实例的唯一全局访问点
public static Singleton GetInstance()
{
//若实例不存在,则new一个新实例,否则返回已有的实例
if (Instance == null)
{
Instance = new Singleton();
}
return Instance;
}
}
class Program
{
static void Main(string[] args)
{
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
if (s1 == s2)
{
Console.WriteLine("两个对象相等");
}
else
{
Console.WriteLine("两个对象不相等");
}
}
}
}
多线程下的单例模式
多线程程序中,如果要同时访问Singleton类,调用GetInstace()方法,会有可能造成创建多个实例
这时候需要加一把进程锁,lock。lock是确保当一个线程位于代码的临界区时,另外一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。
修改上述代码:
class Singleton
{
//保存实例
private static Singleton Instance { get; set; }
private static readonly object syncRoot = new object();
//构造方法让其private,这就保证了外界不能使用new创建此实例的可能
private Singleton()
{
}
//获得本类实例的唯一全局访问点
//双重锁定
public static Singleton GetInstance()
{
//先判断实例是否存在 不存在再加锁判断
if (Instance == null)
{
lock (syncRoot)
{
//若实例不存在,则new一个新实例,否则返回已有的实例
if (Instance == null)
{
Instance = new Singleton();
}
}
}
return Instance;
}
}
实例说明
对于系统中的某些类来说,只有一个实例很重要,
例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。
如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
一般Singleton模式通常有三种形式:
第一种形式:饿汉式
public class Singleton{
//在自己内部定义自己的一个实例,只供内部调用
private static Singleton instance = new Singleton();
//这里提供了一个供外部访问本class的静态方法,可以直接访问
public static Singleton getInstance(){
return instance;
}
private Singleton(){
//do something
}
}
第二种形式:懒汉式(延迟加载),也是常用的形式。
public class SingletonClass{
private static SingletonClass instance=null;
public static SingletonClass getInstance(){
if(instance==null){
instance=new SingletonClass();
}
return instance;
}
private SingletonClass(){
}
}
第三种形式: 双重锁的形式。
public class Singleton{
private static Singleton instance=null;
private static readonly object syncObject = new object();
public static Singleton getInstance(){
if(instance==null){
lock(syncObject){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
private Singleton(){
//do something
}
}
单例模式的要点有三个;
一是某个类只能有一个实例;
二是它必须自行创建这个实例;
三是它必须自行向整个系统提供这个实例。
模式优点
1、提供了对唯一实例的受控访问。
2、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
3、允许可变数目的实例。
模式缺点
1、由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
2、单例类的职责过重,在一定程度上违背了“单一职责原则”。
3、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
模式适用环境
1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
2)控制资源的情况下,方便资源之间的互相通信。如线程池等。
系统只需要一个实例对象,或者因为资源消耗太大而只允许创建一个对象
客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例
资源读取管理器
项目的控制器