C#设计模式之一单例模式(Singleton Pattern)【创建型】

原文:C#设计模式之一单例模式(Singleton Pattern)【创建型】

一、引言

看了李建忠老师的讲的设计模式已经有一段时间了(这段时间大概有一年多了),自己还没有写过自己的、有关设计模式的文章。这次想写一些关于设计模式的文章,用自己的理解和代码来写,算是复习一遍。写作的过程中也会多看看其他大牛的文章,争取让自己的理解正确,否则把大家带跑偏了,就是我的过错了。今天就开始我们第一个设计模式,该模式是:【单例模式】,英文名称:Singleton

Pattern,这个模式很简单,一个类型只需要一个实例,他是创建型的设计模式。为什么叫“创建型”设计模式呢,因为他们有分类。当然了分类的方式不一样,分类的结果也就不一样。

    从目的来看:

-创建型(Creational)模式:负责对象创建

-结构型(Structural)模式:处理类与对象间的组合

-行为型(Behavioral)模式:类与对象交互中的职责分配

从范围来看:

-类模式处理类与子类的静态关系

-对象模式处理对象间的动态关系

以上就是分类的方式,我们按大多数的分类,采用“从目的来看”的分类来对设计模式进行分类,我们就开始今天的学习吧。

二、单例模式的介绍

   2.1、动机(Motivate)

在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。

     如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?

这应该是类设计者的责任,而不是使用者的责任

2.2、意图(Intent)

保证一个类仅有一个实例,并提供一个该实例的全局访问点。                                   --《设计模式GoF》

2.3、结构图(Structure)

2.4、模式的组成

(1)、单件实例(Singleton):这个模式里面只有一个类型,就是Singleton类型,并且这个类只有一个实例,可以通过Instance()方法获取该类型的实例。

2.5、单件模式的代码实现

         既然是单实例,肯定会涉及到多线程的问题,我们就一步一步的来写代码,我们先看看单线程Singleton模式的实现,代码如下:

 1 /// <summary>
 2 /// 单例模式的实现
 3 /// </summary>
 4 public sealed class Singleton
 5 {
 6     // 定义一个静态变量来保存类的实例
 7     private static Singleton uniqueInstance;
 8
 9     // 定义私有构造函数,使外界不能创建该类实例
10     private Singleton()
11     {
12     }
13
14     /// <summary>
15     /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
16     /// </summary>
17     /// <returns></returns>
18     public static Singleton GetInstance()
19     {
20         // 如果类的实例不存在则创建,否则直接返回
21         if (uniqueInstance == null)
22         {
23             uniqueInstance = new Singleton();
24         }
25         return uniqueInstance;
26     }
27 }

私有的实例构造器是屏蔽外界的调用,上面的单例模式的实现在单线程下确实是完美的,也很好的满足了我们单线程环境的需求。

单线程单例模式的几个要点:

    (1)、Singleton模式中的实例构造器可以设置为protected以允许子类派生。

    (2)、Singleton模式一般不要支持ICloneable接口,因为这可能会导致多个对象实例,与Singleton模式的初衷违背。

    (3)、Singleton模式一般不要支持序列化,因为这也有可能导致多个对象实例,同样与Singleton模式的初衷违背。

(4)、Singletom模式只考虑到了对象创建的工作,没有考虑对象销毁的工作。为什么这样做呢,因为Net平台是支持垃圾回收的,所以我们一般没有必要对其进行销毁处理。

    (5)、不能应对多线程环境:在多线程环境下,使用Singleton模式仍然有可能得到Singleton类的多个实例对象

如果放在多线程环境下,问题就出来了。因为在两个线程同时运行GetInstance方法时,此时两个线程判断(uniqueInstance
==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了。要想解决这个问题,只要让GetInstance方法在同一时间只运行一个线程运行就好了,让我们看看多线程Singleton模式的实现,代码如下:

 1     /// <summary>
 2     /// 单例模式的实现
 3     /// </summary>
 4     public sealed class Singleton
 5     {
 6         // 定义一个静态变量来保存类的实例
 7         private static volatile Singleton uniqueInstance;
 8
 9         // 定义一个标识确保线程同步
10         private static readonly object locker = new object();
11
12         // 定义私有构造函数,使外界不能创建该类实例
13         private Singleton()
14         {
15         }
16
17         /// <summary>
18         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
19         /// </summary>
20         /// <returns></returns>
21         public static Singleton GetInstance()
22         {
23             // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
24             // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
25             // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
26             lock (locker)
27             {
28                 // 如果类的实例不存在则创建,否则直接返回
29                 if (uniqueInstance == null)
30                 {
31                     uniqueInstance = new Singleton();
32                 }
33             }
34
35             return uniqueInstance;
36         }
37     }

上面这种解决方案确实可以解决多线程的问题,但是上面代码对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在,对于这个操作完全没有必要的,因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(uniqueInstance==null)为假,此时完全没必要对线程辅助对象加锁之后再去判断,所以上面的实现方式增加了额外的开销,损失了性能,为了改进上面实现方式的缺陷,我们只需要在lock语句前面加一句(uniqueInstance==null)的判断就可以避免锁所增加的额外开销,这种实现方式我们就叫它 “双重锁定(Double Check)”,下面具体看看实现代码的:

 1     /// <summary>
 2     /// 单例模式的实现
 3     /// </summary>
 4     public sealed class Singleton
 5     {
 6         // 定义一个静态变量来保存类的实例
 7         private static volatile Singleton uniqueInstance;
 8
 9         // 定义一个标识确保线程同步
10         private static readonly object locker = new object();
11
12         // 定义私有构造函数,使外界不能创建该类实例
13         private Singleton()
14         {
15         }
16
17         /// <summary>
18         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
19         /// </summary>
20         /// <returns></returns>
21         public static Singleton GetInstance()
22         {
23             // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
24             // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
25             // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
26             // 双重锁定只需要一句判断就可以了
27             if (uniqueInstance == null)
28             {
29                 lock (locker)
30                 {
31                     // 如果类的实例不存在则创建,否则直接返回
32                     if (uniqueInstance == null)
33                     {
34                         uniqueInstance = new Singleton();
35                     }
36                 }
37             }
38             return uniqueInstance;
39         }
40     }

volatile修饰:编译器在编译代码的时候会对代码的顺序进行微调,用volatile修饰保证了严格意义的顺序。一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

三、C#中实现了单例模式的类

    现在我们看看,如何使用C#语言的特性来实现单例的Singleton模式。

//Singleton模式的实现
public sealed class Singleton
{
   public static readonly Singleton instance=new Singleton();

   private Singleton(){}
}

以上是内联初始化(生成的同时进行初始化)的单例模式,它等同于:

public sealed class Singleton
{
    public static readonly Singleton instance;

    //静态构造函数,CLR只执行一次
    static Singleton()
    {
       instance=new Singleton();
    }

   //私有构造函数,防止外界调用
   private Singleton(){}
}

内联初始化其实是把静态的字段放到静态构造器去初始化。只要想访问静态字段,必定已经在之前执行了静态构造器。这样也能够精确地保证使用的时候一定能拿到实例,如果不使用也不会实例化对象,也就是延时加载的功能。他同样能够支持多线程环境,因为只可能有一个线程执行静态构造器,不可能有多个线程去执行静态构造器,感觉就是程序已经自动为我们加锁了。

它的一点弊端就是它不支持参数化的实例化方法。在.NET里静态构造器只能声明一个,而且必须是无参数的,私有的。因此这种方式只适用于无参数的构造器。

     需要说明的是:HttpContext.Current就是一个单例,他们是通过Singleton的扩展方式实现的,他们的单例也并不是覆盖所有领域,只是针对某些局部领域中,是单例的,不同的领域中还是会有不同的实例。

四、Singleton模式的扩展

(1)、将一个实例扩展到n个实例,例如对象池的实现。(n不是指无限个实例,而是固定的某个数)

(2)、将new构造器的调用转移到其他类中,例如多个类协同工作环境中,某个局部环境只需要拥有某个类的一个实例。

(3)、理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的实例构造器的任意调用”。

五、单例模式的实现要点

1、Singleton模式是限制而不是改进类的创建。

2、Singleton类中的实例构造器可以设置为Protected以允许子类派生。

3、Singleton模式一般不要支持Icloneable接口,因为这可能导致多个对象实例,与Singleton模式的初衷违背。

4、Singleton模式一般不要支持序列化,这也有可能导致多个对象实例,这也与Singleton模式的初衷违背。

5、Singleton只考虑了对象创建的管理,没有考虑到销毁的管理,就支持垃圾回收的平台和对象的开销来讲,我们一般没必要对其销毁进行特殊的管理。

6、理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的构造器的任意调用”。

7、可以很简单的修改一个Singleton,使它有少数几个实例,这样做是允许的而且是有意义的。

       1】、单例模式的优点:

             (1)、实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例

(2)、灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程

        2】、单例模式的缺点:

(1)、开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。

(2)、可能的开发混淆:使用 singleton 对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用 new 关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。

(3)、对象的生存期:Singleton 不能解决删除单个对象的问题。因为它包含对该静态的私有字段的引用,静态字段是不能被CLR回收内存的,该实例会和应用程序生命周期一样长,一直存在。

       3】、单例模式的使用场合:

(1)、当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。

(2)、当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

六、总结

到这里,单例模式就介绍完了,这个模式很简单,理解起来也不是很难,只要把握住代码的实现技巧,一般问题都不大,但是要找好使用的时机,如果使用错误,一些逻辑错误比较难排查。

原文地址:https://www.cnblogs.com/lonelyxmas/p/8342657.html

时间: 2024-11-08 11:35:36

C#设计模式之一单例模式(Singleton Pattern)【创建型】的相关文章

设计模式之单例模式Singleton pattern

单例模式Singleton pattern 一种软件设计模式.在核心结构中只包含一个被称为单例的特殊类. 一个类只有一个对象实例,并且自行实例化向整个系统提供. 动机 一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务:一个系统只能有一个窗口管理器或文件系统:一个系统只能有一个计时工具或ID(序号)生成器.如在Windows中就只能打开一个任务管理器.如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源:如果这些窗口显示的内容不

C#面向对象设计模式纵横谈——2.Singleton 单件(创建型模式)

一:模式分类 从目的来看: 创建型(Creational)模式:负责对象创建. 结构型(Structural)模式:处理类与对象间的组合. 行为型(Behavioral)模式:类与对象交互中的职责分配. 从范围来看: 类模式处理类与子类的静态关系. 对象模式处理对象间的动态关系. 二:Singleton (创建型模式) 单件 1.动机(Motivation) 软件系统中,经常有这样一些特殊的类,必须保证他们在系统中只存在一个实例,才能确保它们的逻辑正确性,以及良好的效率. 如何绕过常规的构造器,

二十四种设计模式:单例模式(Singleton Pattern)

单例模式(Singleton Pattern) 介绍保证一个类仅有一个实例,并提供一个访问它的全局访问点. 示例保证一个类仅有一个实例. Singleton using System; using System.Collections.Generic; using System.Text; namespace Pattern.Singleton { /// <summary> /// 泛型实现单例模式 /// </summary> /// <typeparam name=&q

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

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

设计模式(四) : 创建型模式--单例模式

单例模式的话,类图上来看是最简单的设计模式,就是一个类只能有一个自己的实例. 单例模式通常来说我们就有Lazy loading的和不是Lazy loading的.<java与模式>里面的关于这两种的类图,: 可以看到一个是现开始就实例话的,这样的话不符合我们的lazy loading,还有一种是在getinstance方法里头去new的,这样的话会有线程安全的问题,我们提供了双重检查锁. 下面看示意代碼︰ 1. 静态初始化: package com.javadesignpattern.Sing

(转)设计模式之——单例模式(Singleton)的常见应用场景

单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此有些设计大师并把把其称为设计模式之一. 这里又不具体讲如何实现单例模式和介绍其原理(因为这方便的已经有太多的好文章介绍了),如果对单例模式不了解的可以先看下:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html .当然也可以自己搜索.

设计模式 - 单件模式(singleton pattern) 详解

单件模式(singleton pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/28595349 单件模式(singleton pattern) : 确保一个类只有一个实例, 并提供一个全局访问点. 单价模式包括3个部分: 私有构造器, 静态变量, 静态方法. 具体方法: 1. 标准的单例模式: /** * @time 2014.6.5 */ package singleton; /** * @author

23种设计模式介绍(一)---- 创建型模式

由于设计模式篇幅比较大,如果在一篇文章讲完所有的设计模式的话不利于阅读.于是我把它分为三篇文章 23种设计模式介绍(一)---- 创建型模式 23种设计模式介绍(二)---- 结构型模式 23种设计模式介绍(三)---- 行为型模式 由于设计模式都是比较抽象的概念,所以大家一定要确保看懂类图,而后再自己写代码加强记忆. 简介 设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接

php设计模式(一):简介及创建型模式

我们分三篇文章来总结一下设计模式在PHP中的应用,这是第一篇创建型模式. 一.设计模式简介 首先我们来认识一下什么是设计模式: 设计模式是一套被反复使用.容易被他人理解的.可靠的代码设计经验的总结. 设计模式不是Java的专利,我们用面向对象的方法在PHP里也能很好的使用23种设计模式. 那么我们常说的架构.框架和设计模式有什么关系呢? 架构是一套体系结构,是项目的整体解决方案:框架是可供复用的半成品软件,是具体程序代码.架构一般会涉及到采用什么样的框架来加速和优化某部分问题的解决,而好的框架代

设计模式之单例模式——Singleton

                    设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有公开的调用方法.而且,别的类不能实例化它,所以构造方法要设置为私有的. 单例模式的要点 一是某个类只能有一个实例: 二是它必须自行创建这个实例: 三是它必须自行向整个系统提供这个实例. 例如: 有一个"单例对象",而"客户甲"."客户乙" 和&quo