艾伟_转载:.NET设计模式:单件模式(Singleton Pattern)

  概述
  Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点。这就提出了一个问题:如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?客户程序在调用某一个类时,它是不会考虑这个类是否只能有一个实例等问题的,所以,这应该是类设计者的责任,而不是类使用者的责任。
  从另一个角度来说,Singleton模式其实也是一种职责型模式。因为我们创建了一个对象,这个对象扮演了独一无二的角色,在这个单独的对象实例中,它集中了它所属类的所有权力,同时它也肩负了行使这种权力的职责!

  意图
  保证一个类仅有一个实例,并提供一个访问它的全局访问点。

  模型图
  逻辑模型图:

  物理模型图:

  生活中的例子
  美国总统的职位是Singleton,美国宪法规定了总统的选举,任期以及继任的顺序。这样,在任何时刻只能由一个现任的总统。无论现任总统的身份为何,其头衔"美利坚合众国总统"是访问这个职位的人的一个全局的访问点。

 

  以下是五种实现
  1.简单实现

public sealed class Singleton{static Singleton instance=null;

Singleton()    {    }

public static Singleton Instance    {get        {if (instance==null)            {                instance = new Singleton();            }return instance;        }    }}

  这种方式的实现对于线程来说并不是安全的,因为在多线程的环境下有可能得到Singleton类的多个实例。如果同时有两个线程去判断(instance == null),并且得到的结果为真,这时两个线程都会创建类Singleton的实例,这样就违背了Singleton模式的原则。实际上在上述代码中,有可能在计算出表达式的值之前,对象实例已经被创建,但是内存模型并不能保证对象实例在第二个线程创建之前被发现。

  该实现方式主要有两个优点:

  一、由于实例是在 Instance 属性方法内部创建的,因此类可以使用附加功能(例如,对子类进行实例化),即使它可能引入不想要的依赖性。

  二、直到对象要求产生一个实例才执行实例化;这种方法称为“惰性实例化”。惰性实例化避免了在应用程序启动时实例化不必要的 singleton。

  2.安全的线程

public sealed class Singleton{static Singleton instance=null;static readonly object padlock = new object();

Singleton()    {    }

public static Singleton Instance    {get        {lock (padlock)            {if (instance==null)                {                    instance = new Singleton();                }return instance;            }        }    }}

  这种方式的实现对于线程来说是安全的。我们首先创建了一个进程辅助对象,线程在进入时先对辅助对象加锁然后再检测对象是否被创建,这样可以确保只有一个实例被创建,因为在同一个时刻加了锁的那部分程序只有一个线程可以进入。这种情况下,对象实例由最先进入的那个线程创建,后来的线程在进入时(instence == null)为假,不会再去创建对象实例了。但是这种实现方式增加了额外的开销,损失了性能。

  3.双重锁定

public sealed class Singleton {static Singleton instance=null;static readonly object padlock = new object();

Singleton()     {     }

public static Singleton Instance    {get        {if (instance==null)            {lock (padlock)                {if (instance==null)                    {                        instance = new Singleton();                    }                }            }return instance;        }    }}

  这种实现方式对多线程来说是安全的,同时线程不是每次都加锁,只有判断对象实例没有被创建时它才加锁,有了我们上面第一部分的里面的分析,我们知道,加锁后还得再进行对象是否已被创建的判断。它解决了线程并发问题,同时避免在每个 Instance 属性方法的调用中都出现独占锁定。它还允许您将实例化延迟到第一次访问对象时发生。实际上,应用程序很少需要这种类型的实现。大多数情况下我们会用静态初始化。这种方式仍然有很多缺点:无法实现延迟初始化。

  4.静态初始化

public sealed class Singleton {static readonly Singleton instance=new Singleton();

static Singleton()    {    }

Singleton()    {    }public static Singleton Instance    {get        {return instance;        }    }}

  看到上面这段富有戏剧性的代码,我们可能会产生怀疑,这还是Singleton模式吗?在此实现中,将在第一次引用类的任何成员时创建实例。公共语言运行库负责处理变量初始化。该类标记为 sealed 以阻止发生派生,而派生可能会增加实例。此外,变量标记为 readonly,这意味着只能在静态初始化期间(此处显示的示例)或在类构造函数中分配变量。

  该实现与前面的示例类似,不同之处在于它依赖公共语言运行库来初始化变量。它仍然可以用来解决 Singleton 模式试图解决的两个基本问题:全局访问和实例化控制。公共静态属性为访问实例提供了一个全局访问点。此外,由于构造函数是私有的,因此不能在类本身以外实例化 Singleton 类;因此,变量引用的是可以在系统中存在的唯一的实例。

  由于 Singleton 实例被私有静态成员变量引用,因此在类首次被对 Instance 属性的调用所引用之前,不会发生实例化。

  这种方法唯一的潜在缺点是,您对实例化机制的控制权较少。在 Design Patterns 形式中,您能够在实例化之前使用非默认的构造函数或执行其他任务。由于在此解决方案中由 .NET Framework 负责执行初始化,因此您没有这些选项。在大多数情况下,静态初始化是在 .NET 中实现 Singleton 的首选方法。

  5.延迟初始化

public sealed class Singleton {     Singleton()     {     }public static Singleton Instance     {get        {return Nested.instance;        }    }class Nested    {static Nested()        {        }internal static readonly Singleton instance = new Singleton();    }}

  这里,初始化工作有Nested类的一个静态成员来完成,这样就实现了延迟初始化,并具有很多的优势,是值得推荐的一种实现方式。

  实现要点:
  Singleton模式是限制而不是改进类的创建。
  Singleton类中的实例构造器可以设置为Protected以允许子类派生。
  Singleton模式一般不要支持Icloneable接口,因为这可能导致多个对象实例,与Singleton模式的初衷违背。
  Singleton模式一般不要支持序列化,这也有可能导致多个对象实例,这也与Singleton模式的初衷违背。
  Singleton只考虑了对象创建的管理,没有考虑到销毁的管理,就支持垃圾回收的平台和对象的开销来讲,我们一般没必要对其销毁进行特殊的管理。
  理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的构造器的任意调用”。
  可以很简单的修改一个Singleton,使它有少数几个实例,这样做是允许的而且是有意义的。

  优点
  实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例
  灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程

  缺点:
  开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题,上面的五种实现方式中已经说过了。
  可能的开发混淆:使用 singleton 对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用 new 关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
  对象的生存期:Singleton 不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于 .NET Framework 的语言),只有 Singleton 类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致 Singleton 类中出现悬浮引用。

  适用性
  当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
  当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

  应用场景
  每台计算机可以有若干个打印机,但只能有一个Printer Spooler,避免两个打印作业同时输出到打印机。(摘自吕震宇的C#设计模式(7)-Singleton Pattern)
   PC机中可能有几个串口,但只能有一个COM1口的实例。
  系统中只能有一个窗口管理器。
  .NET Remoting中服务器激活对象中的Sigleton对象,确保所有的客户程序的请求都只有一个实例来处理。

  完整示例:
  这是一个简单的计数器例子,四个线程同时进行计数。

using System;using System.Threading;namespace SigletonPattern.SigletonCounter{/**//// <summary>/// 功能:简单计数器的单件模式/// 编写:Terrylee/// 日期:2005年12月06日/// </summary>    public class CountSigleton    {/**////存储唯一的实例        static CountSigleton uniCounter = new CountSigleton();  /**////存储计数值        private int totNum = 0;  private CountSigleton()         { /**////线程延迟2000毫秒            Thread.Sleep(2000);        } static public CountSigleton Instance()         { return uniCounter;         } /**////计数加1        public void Add()        {             totNum ++;        }  /**////获得当前计数值        public int GetCounter()        { return totNum;        }     }}

 

using System;using System.Threading;using System.Text;namespace SigletonPattern.SigletonCounter{/**//// /// 功能:创建一个多线程计数的类/// 编写:Terrylee/// 日期:2005年12月06日///     public class CountMutilThread    {public CountMutilThread()        {        }/**//// /// 线程工作///         public static void DoSomeWork()        {/**////构造显示字符串            string results = "";/**////创建一个Sigleton实例            CountSigleton MyCounter = CountSigleton.Instance();/**////循环调用四次            for(int i=1;i<5;i++)            {/**////开始计数                MyCounter.Add();                results +="线程";                results += Thread.CurrentThread.Name.ToString() + "——〉";                results += "当前的计数:";                results += MyCounter.GetCounter().ToString();                results += "\n";                Console.WriteLine(results);/**////清空显示字符串                results = "";            }        }public void StartMain()        {            Thread thread0 = Thread.CurrentThread;             thread0.Name = "Thread 0";             Thread thread1 =new Thread(new ThreadStart(DoSomeWork));             thread1.Name = "Thread 1";             Thread thread2 =new Thread(new ThreadStart(DoSomeWork));             thread2.Name = "Thread 2";             Thread thread3 =new Thread(new ThreadStart(DoSomeWork));             thread3.Name = "Thread 3";             thread1.Start();             thread2.Start();             thread3.Start(); /**////线程0也只执行和其他线程相同的工作            DoSomeWork();         }    }}

 

using System;using System.Text;using System.Threading;namespace SigletonPattern.SigletonCounter{/**//// /// 功能:实现多线程计数器的客户端/// 编写:Terrylee/// 日期:2005年12月06日///     public class CountClient    {public static void Main(string[] args)        {       CountMutilThread cmt = new CountMutilThread();

cmt.StartMain();

Console.ReadLine();        }    }}

  总结
  Singleton设计模式是一个非常有用的机制,可用于在面向对象的应用程序中提供单个访问点。文中通过五种实现方式的比较和一个完整的示例,完成了对Singleton模式的一个总结和探索。用一句广告词来概括Singleton模式就是“简约而不简单”。

  源码下载:/Files/Terrylee/SigletonPattern.rar

时间: 2024-10-02 04:53:35

艾伟_转载:.NET设计模式:单件模式(Singleton Pattern)的相关文章

单件模式Singleton来控制窗体被重复或多次打开

本文转载:http://blog.csdn.net/a0700746/article/details/4473796 一般在百度搜一下,会出来一下内容,看来很好用.Singleton很方便的一个用处就是控制窗体被多次或重复打开.下面是它的用法. 一般Singleton模式通常有几种形式: public class Singleton { private Singleton(){} //在自己内部定义自己一个实例,是不是很奇怪? //注意这是private 只供内部调用 private stati

艾伟_转载:单件模式的陷阱

看过很多单件模式的文章,书上有,网上更多一些.一般来说,只有如何实现单件模式,而没有介绍具体情况单件模式的使用,也没有介绍过单件模式会出现问题.单件模式似乎不会产生逻辑上的问题.但是,这仅仅是似乎. 在描述我遇到的问题之前,先讲讲我对其原理的理解. 首先单件模式是自我创建的一个对象,并且在运行期始终保持只有唯一的对象.抛开什么东西能够自我创建不说,保持唯一对象要怎么理解呢?先看看一个普通的类: package singleton; public class SimpleClass { } 对其进

艾伟_转载:.NET设计模式:抽象工厂模式(Abstract Factory)

概述 在软件系统中,经常面临着"一系列相互依赖的对象"的创建工作:同时由于需求的变化,往往存在着更多系列对象的创建工作.如何应对这种变化?如何绕过常规的对象的创建方法(new),提供一种"封装机制"来避免客户程序和这种"多系列具体对象创建工作"的紧耦合?这就是我们要说的抽象工厂模式. 意图 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 模型图 逻辑模型: 物理模型: 生活中的例子 抽象工厂的目的是要提供一个创建一系列相关或

.NET设计模式-单件模式(Singleton Pattern)

单件模式(Singleton Pattern) --.NET设计模式系列之二 Terrylee,2005年12月07日 概述 Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点.这就提出了一个问题:如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?客户程序在调用某一个类时,它是不会考虑这个类是否只能有一个实例等问题的,所以,这应该是类设计者的责任,而不是类使用者的责任. 从另一个角度来说,Singleton模式其实也是一种职责型模式.因为我们创建了一个对象,这

设计模式(二)单件模式Singleton(创建型)

SINGLETON(单件)-对象创建型模式        几乎所有面向对象的程序中,总有一些类的对象需要是唯一的,例如,通过数据库句柄到数据库的连接是独占的.您希望在应用程序中共享数据库句柄,因为在保持连接打开或关闭时,它是一种开销.再如大家最经常用的IM,如QQ,在同一台电脑,一个帐号只能有唯一的登录. 1. 问题 怎样确保一个特殊类的实例是独一无二的(它是这个类的唯一实例),并且这个实例易于被访问呢? 2. 解决方案 1)全局变量:一个全局变量使得一个对象可以被访问,但它不能防止你实例化多个

艾伟_转载:揭示同步块索引(上):从lock开始

大家都知道引用类型对象除实例字段的开销外,还有两个字段的开销:类型指针和同步块索引(SyncBlockIndex).同步块索引这个东西比起它的兄弟类型指针更少受人关注,显得有点冷落,其实此兄功力非凡,在CLR里可谓叱咤风云,很多功能都要借助它来实现. 接下来我会用三篇来介绍同步块索引在.NET中的所作所为. 既然本章副标题是从lock开始,那我就举几个lock的示例: 代码1 1: public class Singleton 2: { 3: private static object lock

艾伟_转载:Regex.Replace 方法的性能!

    园子里有很多关于去除Html标签的文章.一个常用的经验是使用 Regex.Replace 方法利用正则去替换.这里有一篇使用该方法的文章 C#中如何去除HTML标记 .下面我贴出该方法的代码,见代码清单1-1 代码清单1-1 引用 http://www.cnblogs.com/zoupeiyang/archive/2009/06/22/1508039.html                ///          /// 去除HTML标记         ///          //

解读设计模式----命令模式(Command Pattern)

本文与作者原文有一定的偏差,其中加入了一部分是个人看法,详细请查看作者原文.*** 原文连接http://www.dofactory.com/Patterns/PatternCommand.aspx 命令模式意图: GOF 在<设计模式>一书中阐述其意图:"将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可取消的操作."这里所谓的"不同的请求"也既意味着请求可能发生的变化,是一个可能扩展的功能点. 命令

解读设计模式----外观模式(Facade Pattern)

一.模式简介 外观模式(Facade Pattern)可以将一系列复杂的类包装成一个简单的封闭接口.也称门面模式. 二.模式意图 每一种设计模式都有它的意图,我们看看设计模式的祖师们是怎么说的.按照GOF的说法,Facade模式的意图是:为了子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 三.模式UML(下图转自http://www.dofactory.com/) 四.模式参与者 门面(Facade)角色:客户端可以调用这个角色的方