Design Pattern in C#: Singleton

 

 

最近在学习微软MSDN WebCast讲座中关于面向程序对象设计的课程,觉得内容非常不错,在Blog上面留下学习笔记。

GoF(Gang of Four)的经典著作中一共提到了23种经典的设计模式,今天是第一节课,主要讲解的是Singleton单件设计模式。

首先简单说说对于所有设计模式的分类,按照目的和范围两种观点来看,设计模式可以归纳为这么几种。

从目的来看:(即设计类或者说对象的时候,我们的出发点)

    创建型(Creational):负责对象的创建,即生成。

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

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

从范围来看:

    类模式处理类与子类的静态关系。(其实我觉得可以理解为设计的逻辑)

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

 

Singleton

    开始进入正题,首先说Singleton这种设计模式是什么意思呢。

 

Motivation

    由于某种设计需要,我们需要保证一个或一些特殊的类,在系统中只存在一个或确定个数的实例,才能保证程序的正确性阿,效率阿等等,总之其实就是我们需要在设计的过程中限定了对于类实例化的个数。

    有了这样一个课题之后,我们就要讨论怎么去实现这么一个东西呢,有两种方法,第一种是放在业务逻辑里面,本身在设计这样一个特殊的类的时候,不进行特定的限定或使用特殊的机制,而是让类的使用者,也就是其他类的实现来自己控制生成该类的实例化的数量,很明显这是一种不负责任的方法。第二种是在设计这种特殊的类的时候,加入特别的限定和机制,使得其他的类或者方法调用这个特定的类的时候,可以取得唯一正确的访问,我们称这种方法为Singleton设计模式。

    其实总结一下,这是个责任的问题,就是说实现这个东西这个机制是谁来负责的问题。用一句话来说:

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

                                                                                                  ------《设计模式》GoF

    其实个人觉得,讨论设计模式的这个话题的时候,有点想讨论管理学,本身因为这两种东西从某种角度来讲都是方法论。让人奇怪的是,虽然说是方法论,随着发展进步的过程中,体现出了很大复杂性。就好象说设计模式与算法不同,不是一个按部就班把内容套进去就OK的东西,而是需要很多实践的经验,在这之上才能争取的理解和运用设计方式。而管理学呢,越来越觉得本身这门科学其实随着流派的增加和变化,没有了一定之规,不能说那种方法放在那个环境下就一定要成功,或者一定行不通,好像和程序设计很相通的需要我们对设计方法,也就是管理学中的管理方法进行叠代,进行重构。(题外话一大篇)

   

    前面说的其实相对理论性比较多,那么来看看怎么实现这么一种机制。

 

1. Singleton.cs

using System;

 

namespace Test

{

    public class Singleton

    {

        //Private Constructor of Singleton Class

        private Singleton()

        { }

 

        private static Singleton instance;

 

        public static Singleton Instance

        {

            get

            {

                if (instance == null)

                {

                    instance = new Singleton();

                }

                return instance;

            }

        }

 

     }

    }

    这是最简单的一个例子,我们可以看到Singleton这个类中,我们使用了一个私有的字段和一个共有的属性来实现了在调用Instance这个属性的时候返回唯一的一个实例化后的Singleton类。其实还有一个很关键的地方需要注意的是,在这个类中我们使用了私有的构造器来避免在外部实例化这个类,也就是说只有调用Instance这个属性的时候才有可能对这个类进行实例化,这种机制也保证了代码的最简化,也就是当我们不需要实例化这个类的时候,不会有任何资源分配给这个类,private static Singleton instance= new Singleton();来得简洁。

    讨论这个简单结构的缺点,其实可以说是很明显的就是说这个类不能应用于多线程的应用系统中,可以试想一下,当两个线程同时调用Instance属性且这个时候恰恰这个类没有被实例化成对象的时候,就会造成生成了两个这个类的实例化的问题,违反了我们设计的初衷,我们再来看看第二个例子是如何解决这个问题的。

2. MultiThreadSingleton.cs

using System;

 

namespace Test

{

    public class Singleton

    {

        //Private Constructor of Singleton Class

        private Singleton()

        { }

 

        private static volatile Singleton instance;

        private static object lockHelper = new object();

 

        public static Singleton Instance

        {

            get

            {

                if (instance == null)

                {

                    lock (lockHelper)

                    {

                        if (instance == null)

                        {

                            instance = new Singleton();

                        }

                    }

                   

                }

                return instance;

            }

        }

 

    }

}

我们可以看出来,第二个程序解决了多线程的问题,它使用了一个lock关键字来进行一个双重的判断,相信对于多线程编程有一定了解的程序员可以非常清楚的看明白在这段代码中使用了非常经典的多线程的编程模式。

但是这段代码依旧不是完美的,它看起来太厚重了,有点太繁琐了,实现的怎么那么夸张呢,这是我的直接感觉,有没有更好的方法能够更好的实现我们需要的Singleton机制呢。

3. StaticSingleton.cs

namespace Test

{

    public class Singleton

    {

        public static readonly Singleton Instance = new Singleton();

 

        private Singleton()

        { }

 

        //public static readonly Singleton Instance;

 

        //static Singleton()

        //{

        //    Instance = new Singleton();

        //}

 

        //private Singleton()

        //{ }

 

    }

}

可以,这段代码看起来非常的简单,但他却利用C#内部的机制实现了我们需要的设计模式,简单的说,它使用了公有静态只读的成员来控制访问,同时使用了私有的构造器来防止外部的实例化。那为什么可以实现我们需要的Singleton模式呢,其实大家可以看一下被注释掉的几行代码,其实这几行代码是等价于上面的两行代码的,首先有一个静态的构造器,我们知道静态的构造器有很多特性,没有参数,只会被执行一次,永远都在静态成员被引用之前执行,利用这几个C#静态构造函数的特性,我们实现了我们需要实现的设计模式,它非常简洁,非常的灵巧甚至有点投机取巧的感觉,但是却很实际,我个人也觉得是一种非常好的实现方式。

    但是,还是说没有什么东西可以是Perfect的,我也坚信在任何领域可能这个道理都是没错的,这种实现的方法,同样有它的缺点,失去了一个灵活性,我们在引用这个实例的时候不能给他传参数了,也就是说我们只能通过Singleton.Instance这个调用得到这个实例,我们不能Singleton.Instance(int x, int y)的方式来得到我们对这个实例的取得了,但是在某种时候这恰恰是我们需要的,可以看得出来在前两种实现中虽然显得有点笨重,但是是可以实现这种调用的,这个时候我们的问题变成了使用静态的构造器和字段的方法怎么解决传参数的问题。

    未完,待续

posted on 2007-05-16 15:33  Big Angel  阅读(313)  评论(0)    收藏  举报

导航