单例模式之简约美
最后更新于:2022-04-01 06:33:28
**生活中的单例**
中国(China),位于东亚,是一个以华夏文明为主体、中华文化为基础,以汉族为主要民族的统一多民族国家,通用汉语。中国疆域内的各个民族统称为中华民族,龙是中华民族的象征。古老的中国凭借自身的发展依旧美丽的屹立于东方民族之林,闪耀着她动人的光彩,世界上只有一个中国,任何部分都是祖国不可分割的一部分,今天我们的设计模式就从伟大的祖国开始说起---单例模式。
**详解单例模式**
单例模式是什么?跟我们的祖国有着怎样的关系呢?首先我们来看一下单例,从“单例”字面意思上理解为—一个类只有一个实例,所以单例模式也就是保证一个类只有一个实例的一种实现方法。官方定义:确保一个类只有一个实例,并提供一个全局访问点。在学习的过程中,我们需要把握三个主要的关键点,一、某个类只能有一个实例;二、它必须自行创建这个实例;三、它必须自行向整个系统提供这个实例。来看一下单例模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743f9d859.jpg)
**实现方法**
一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
**代码实现**
**第一版(基本代码)**
~~~
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace 单例模式基本模型
{
class Program
{
//客户端
static void Main(string[] args)
{
Singleton Instance1 = Singleton.GetInstance();
Singleton Instance2 = Singleton.GetInstance();
if (Instance1 == Instance2)
{
Console.WriteLine("两个实例是一模一样的实例。");
}
}
}
class Singleton
{
private static Singleton instance;
//构造方法采用private,外界便无法用new创建该类的实例
private Singleton()
{ }
//该方法提供一个获得该类实例的全局访问点,是唯一的
public static Singleton GetInstance()
{
//如果实例不存在,则返回一个新实例,否则返回原实例。
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
~~~
但是上述代码存在一些缺点,线程不安全,多线程情况下,多个线程同时访问Singleton,调用GetInstance()方法,同时判断instance==null,得到真值,导致创建多个实例,这不符合单例模式的基本原则。那我们要怎么办捏,为了解决以上缺点,我们来看改进的代码(一下版本的)
**第二版(多线程时的单例)**
~~~
class Singleton
{
private static Singleton instance=null;
//创建一个静态只读的进程辅助对象
private static readonly object ProgressLock = new object();
//构造方法采用private,外界便无法用new创建该类的实例
private Singleton()
{ }
//该方法提供一个获得该类实例的全局访问点,是唯一的
public static Singleton GetInstance()
{
lock (ProgressLock)
{
if (instance == null)
{
instance = new Singleton();
}
}
return instance;
}
}
~~~
每次调用GetInstance()方法都需要lock,这种做法是会影响性能的,所以我们需要对这个类进行改良。
**第三版(双重锁定)**
~~~
class Singleton
{
private static Singleton instance=null;
//创建一个静态只读的进程辅助对象
private static readonly object ProgressLock = new object();
//构造方法采用private,外界便无法用new创建该类的实例
private Singleton()
{ }
//该方法提供一个获得该类实例的全局访问点,是唯一的
public static Singleton GetInstance()
{
if (instance == null)
{
lock (ProgressLock)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
~~~
上述构造方式只有在实例未被创建的时候才加锁,避免了每次调用GetInstance()方法都加锁损失性能的问题。但是相对于后面的做法,她仍然有着美中不足的地方。
**第四版(静态初始化)**
~~~
//sealed关键字防止派生
public sealed class Singleton
{
//在第一次引用类的成员时创建实例,公共语言运行库负责处理变量的初始化
private static readonly Singleton instance=new Singleton();
//构造方法采用private,外界便无法用new创建该类的实例
private Singleton()
{ }
public static Singleton GetInstance()
{
return instance;
}
}
~~~
这种方式是在自己被加载时就将自己实例化,称为饿汉式。由于有.NetFramework 进行初始化,所以我们对实例化机制的控制权较少,没办法和其他实现一样实现延迟初始化。在上面三种形式中,您能够在实例化之前使用非默认的构造函数或执行其他任务。
**第五版(完全延迟加载实例化)**
~~~
public sealed class Singleton
{
private Singleton()
{
}
public static Singleton GetInstance()
{
return Nested.instance;
}
class Nested
{
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
~~~
**写在后面的话**
前三版编程方法,因为会面临多线程访问安全的问题,需要做双重锁定这样的处理才可以保证安全,但能够在实例化之前使用非默认的构造函数或执行其他任务,第四版(静态初始化方式)是类一加载就实例化的对象,占用系统资源,所以到底实用哪一种方式,视具体情况而定.