分享

C#设计模式系列:单件模式(Singleton)

 雪柳花明 2016-09-18

1、单件模式简介

1、单件模式简介

1.1>、定义  

  单件模式(Singleton)定义:要求一个类有且仅有一个实例,并且提供了一个全局的访问点,在同一时刻只能被一个线程所访问。

  单件模式的特点:
  1>、单件类只能有一个实例。
  2>、单件类必须自身创建唯一实例。
  3>、单件类必须给所有其它对象提供唯一实例。

1.2>、使用频率

   中高

1.3>、单件模式应用
  1>、每台计算机可以有若干个打印机,但只能有一个Printer Spooler,避免两个打印作业同时输出到打印机;

  2>、Word文档在同一时间内,只能有一个用户对其进行操作,程序设计中需要保证一个文档不能被两个用户同时写入。

2、单件模式结构

2.1>、结构图

2.2>、参与者

  单件模式参与者:

   Singleton

    ° 被调用的单件对象;

    ° 在单件模式中,通常由Instance()或GetInstance()方法负责对象的创建,该方法应保证每个需要(单件)对象的客户端均能访问。

3、单件模式结构实现

   C#单件模式实现要点:

   单件类有一个私有的无参构造函数,这可以防止被其他类实例化,而且单例类也不应该被继承,如果单例类允许继承那么每个子类都可以创建实例,这就违背了单件模式的“唯一实例”原则。

   单件类使用sealed修饰,可以阻止被继承。

   使用一个静态的变量用来保存单实例的引用。

   使用一个公有的静态方法用来获取单一实例的引用,如果实例为null即创建一个。

  1>、非线程安全

  Singleton.cs

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.SingletonPattern.Structural
{
    /// <summary>
    /// 单件模式实现方式:由于该实现方式非线程安全,在实际应用中不推荐使用。
    /// </summary>
    public sealed class Singleton
    {
        private static Singleton _instance;

        // 将构造函数设为private,防止通过new实例化对象
        private Singleton()
        {
        }

        // 获取实例,并加入判断逻辑,保证实例只被创建一次
        public static Singleton Instance()
        {
            // 使用延迟初始化
            // 注: 非线程安全
            if (_instance == null)
            {
                _instance = new Singleton();
            }

            return _instance;
        }
    }
}
复制代码

  Program.cs

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using DesignPatterns.SingletonPattern.Structural;

namespace DesignPatterns.SingletonPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建一个实例s1
            Singleton s1 = Singleton.Instance();
            // 创建一个实例s2
            Singleton s2 = Singleton.Instance();

            if (s1 == s2)
            {
                Console.WriteLine("对象为相同实例");
            }
        }
    }
}
复制代码

  运行输出:

对象为相同实例
请按任意键继续. . .

  实践中Word文档在同一时间内,只能一个用户对其进行写操作实例。

  Singleton.cs

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.SingletonPattern.Practical
{
    /// <summary>
    /// Word文档在同一时间内,只能有一个用户对其进行操作,需要保证一个文档不能被两个用户同时写入。
    /// </summary>
    public sealed class Singleton
    {
        private static Singleton _instance;

        // 将构造函数设为private,防止通过new实例化对象
        private Singleton()
        {
        }

        // 获取实例,并加入判断逻辑,保证实例只被创建一次
        public static Singleton Instance()
        {
            // 使用延迟初始化
            // 注: 非线程安全
            if (_instance == null)
            {
                _instance = new Singleton();
                Console.WriteLine("Word文档打开成功,具有读写权限");
            }
            else
            {
                Console.WriteLine("Word文档已经被锁定,不可写入");
            }

            return _instance;
        }
    }
}
复制代码

  Program.cs

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using DesignPatterns.SingletonPattern.Practical;

namespace DesignPatterns.SingletonPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建一个实例s1
            Singleton s1 = Singleton.Instance();
            // 创建一个实例s2
            Singleton s2 = Singleton.Instance();

            Console.ReadKey();
        }
    }
}
复制代码

  运行输出:

Word文档打开成功,具有读写权限
Word文档已经被锁定,不可写入

  以上的实现方式适用于单线程环境,在多线程的环境下有可能得到Singleton类的多个实例。假如同时有两个线程去判断(null == _singleton),并且得到的结果为真,那么两个线程都会创建类Singleton的实例,这样就违背了Singleton模式“唯一实例”的原则。

   2>、简单线程安全

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.SingletonPattern.Structural
{
    public sealed class Singleton
    {
        private static Singleton _instance;

        // Lock synchronization object
        private static readonly object _syncLock = new object();

        // Constructor is 'private'
        private Singleton()
        {
        }

        public static Singleton Instance()
        {
            // Support multithreaded applications through
            // 'Double checked locking' pattern which (once
            // the instance exists) avoids locking each
            // time the method is invoked
            if (_instance == null)
            {
                lock (_syncLock)
                {
                    if (_instance == null)
                    {
                        _instance = new Singleton();
                    }
                }
            }

            return _instance;
        }
    }
}
复制代码

  以上方式的实现方式是线程安全的,首先创建了一个静态只读的进程辅助对象,由于lock是确保当一个线程位于代码的临界区时,另一个线程不能进入临界区(同步操作)。如果其他线程试图进入锁定的代码,则它将一直等待,直到该对象被释放。从而确保在多线程下不会创建多个对象实例了。但这种实现方式要进行同步操作,将影响系统性能的瓶颈和增加了额外的开销。

4、单件模式应用分析

  单件模式使用注意点:

  1>、不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。

  2>、不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放。

  单件模式适用情形:

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

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

  单件模式具有以下特点:

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

  2>、额外的开销:虽然很小,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销;

  3>、可能的开发混淆:使用Singleton对象时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类;

  4>、对象的生存期问题:Singleton不能解决删除单个对象的问题。在提供内存管理的语言中(如.Net Framework的语言),只有Singleton类能够导致实例被取消分配,因为它包含该对象实例的私有引用。在某些语言中(如C++),其他类可以删除对象实例,但这样会导致Singleton类中出现悬浮引用。

5、参考资料

  http://www./Patterns/Patterns.aspx

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多