澳门新葡萄京娱乐场 5

澳门新葡萄京娱乐场设计模式学习之路——Singleton 单件模式

通常来说,类和对象(类的实例)之间是一对多的关系。但是某些情况下,我们只需要一个类的一个实例。比如多个实例的多样性反倒会带来一些不可控因素,多次的实例化,是一种资源的浪费,降低了效率;又如他们是工厂对象(factoryobject),用来创建系统中的其他对象,等等。这时候,较好的处理方式就是让类的实例具有单一性:保障这个类在运行过程中最多生成一个实例(
Singleton 模式),或者让所有的实例保持一致( Monostate 模式)。

在程序设计过程中,有很多情况需要保证一个类只有一个实例.这时候就需要使用单件模式了。

今天我们来探索一下Singleton设计模式的实现及应用场景。

PHP 下实现 Singleton 模式示例:

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

Singleton模式属于Creational
Type(创建型)设计模式的一种。该模式一般用于确保在应用中仅创建一个某类的instance,在应用中的各个地方对该类的实例对象的引用均指向同一instacne。

?phpclassSingleton{privatestatic$instance;privatefunction__construct(){}publicstaticfunctioninstance(){if

结构(Struct)

Singleton模式的优势:它提供了对资源的concurrent(一致)访问,避免了创建多个实例对象,浪费内存空间。

 

Singleton模式实现准则:

澳门新葡萄京娱乐场 1

1、确保仅有一个类的实例对象

 

2、提供对唯一实例对象的全局访问,该规则又可细分为3条规则。

 

  • 声明该类的所有构造器为private
  • 提供返回实例对象的引用的静态方法
  • 实例对象被存储为类的private static字段

1 使用静态方法创建单件

下面通过例子实现Singleton模式,在具体实现时,我们首先实现不使用Singleton模式的代码,然后一步一步地将其改造成Singleton模式。

让一个类只有一个实例,最容易的方法是在类中嵌入一个静态变量,并在第一个类实例中设置该变量,而且每次进入构造函数都要检查。不管类有多少个实例,静态变量只能有一个实例。为了防止类被多次实例化,我们把构造函数声明为私有的,这样只能在类的静态方法里创建一个实例。

 1 namespace Singleton
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Singleton s1 = new Singleton();
 8             s1.PrintMessage("this is first message");
 9 
10             Singleton s2 = new Singleton();
11             s2.PrintMessage("this is second message");
12 
13             Console.ReadKey();
14         }
15     }
16     class Singleton
17     {
18         private static int counter = 0;
19         public Singleton()
20         {
21             counter++;
22             Console.WriteLine("这是第"+counter.ToString()+"次调用构造函数");
23         }
24         public void PrintMessage(String str)
25         {
26             Console.WriteLine(str);
27         }
28     }
29 }

单线程环境下Singleton模式的实现

运行结果如下图:

 

澳门新葡萄京娱乐场 2

澳门新葡萄京娱乐场 3澳门新葡萄京娱乐场 4代码

 

public class Singleton
    {
        private static Singleton instance;
        private Singleton() {}

  可以看出,在不使用Singleton模式时,每次使用类Singleton的PrintMessage方法时,都需要创建一个新的实例对象,这造成了极大的浪费。下面我们按照上面所说的准则将它改造成使用Singleton模式。

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

 1 namespace Singleton
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Singleton s1 = Singleton.getInstance;
 8             s1.PrintMessage("this is first message");
 9 
10             Singleton s2 = Singleton.getInstance;
11             s2.PrintMessage("this is second message");
12 
13             Console.ReadKey();
14         }
15     }
16     public sealed class Singleton
17     {
18         private static int counter = 0;
19         private static Singleton instance=null;
20 
21         public static Singleton getInstance
22         {
23             get
24             {
25                 if (instance == null)
26                     instance = new Singleton();
27                 return instance;
28             }
29         }
30         private Singleton()
31         {
32             counter++;
33             Console.WriteLine("这是第"+counter.ToString()+"次调用构造函数");
34         }
35         public void PrintMessage(String str)
36         {
37             Console.WriteLine(str);
38         }
39     }
40 }

 

运行结果如下图:

 

澳门新葡萄京娱乐场 5 

 

  另外,需要注意的是,不要忘了在Singleton类的声明中添加sealed关键字,它确保了Singleton类不可被继承。当Singleton类可被继承且继承类位于Singleton基类外部时,由于Singleton基类的构造函数声明为private,此时运行程序会产生Singleton.Singleton()受保护级别限制不可访问的编译错误;当Singleton类可被继承且继承类位于Singleton基类内部构成嵌套类时,这时在main()方法中就可以创建多个继承类的instance,这就违背了Singleton模式的设计初衷:仅能创建该类型的一个instance。

单线程Singleton模式的几个要点

  最后,上述代码仅在单线程环境中运行良好,在下篇中介绍多线程环境中的实现,及Double-checked
locking和lazy initialization的相关概念。

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

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

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

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

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

 

 

多线程环境下Singleton模式实现

 

澳门新葡萄京娱乐场 6澳门新葡萄京娱乐场 7代码

public class Singleton
    {
        private static volatile Singleton instance;
        private static object lockHelper = new object();

        private Singleton() { }

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

 

 

 

澳门新葡萄京娱乐场 8澳门新葡萄京娱乐场 9代码

public class Singleton
    {
        public static readonly Singleton Instance;

        static Singleton()
        {
            Instance = new Singleton();
        }

        private Singleton() { }

    }

 

 

上面的方式等同于下面的实现方式:

 

public class Singleton
    {
        public static readonly Singleton Instance= new Singleton();
        private Singleton() { }

    }

 

 

 

 

 

 

2
提供一个单件的全局访问点

 

由于使用单件可以提供一个类的全局访问点,即使C#中没有全局变量,设计程序时也必须为整个程序提供引用单件的方法。

一种解决方案是在程序的开头创建单件,并将其作为参数传递到需要使用它的类中。

 

     Singleton instance =
Singleton.Instance;
     Test test = new
Test(instance);

 

 

 

这种方法的缺点是,在某次程序运行中,可能不需要所有的单件,,这样会影响程序的性能。

另一种更灵活的解决方案是,在程序中创建一个所有单件类的注册表,并使注册表始终是可用的,每次实例化一个单件,都将其记录在注册表中。程序的任何部分都能使用标识字符串访问任何一个单件实例,并能取回相应的实例变量。

注册表方法的缺点是减少了类型检查,因为注册表中的单件表可能把所有的单件都保存成对象类型,例如,hashtable中的对象类型,另外,注册表本身也有可能是一个单件,必须使用构造函数或者其他set函数把他传递给程序的所有部分。

提供一个全局访问点的最常用方式是使用类的静态方法。类名始终是可用的,静态方法只能由类调用,不能由类的实例调用,所以,不管程序中有多少个地方调用该方法,永远只能有一个这样的类实例。

发表评论

电子邮件地址不会被公开。 必填项已用*标注