Singleton patterns 单件(创建型模式)

1、模式分类

     1.1  从目的来看:

                    •      – 创建型(Creational)模式:负责对象创建。

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

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

     1.2 从范围来看:

                    •     – 类模式处理类与子类的静态关系。

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

2、动机(Motivation)目的  

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

3、意图(Intent)             

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

                                                                                          ——《设计模式》GoF

4、单例模式多种实现方法

    4.1 单线程Singleton 模式实现

 1  class Singleton
 2     {
 3         private static Singleton instance;   
 4         private Singleton() //防止外界随意new此对象,如果访问级别设置成protected,那么子类可以派生,同时也也不可以实例化此对象
 5         {         
 6         }
 7         public static Singleton Instance
 8         {
 9             get {
10                 if (instance == null)  //起到延迟加载对象的作用,当用户用不到此实例的时候
11                 {
12                     instance = new Singleton();
13                 }
14                 return instance;
15             }
16         }
17     }

单线程Singleton模式的几个要点
                  • Singleton模式中的实例构造器可以设置为protected以允许子类派生。(同样不可实例化对象,受保护级别限制)
                  • Singleton模式一般不要支持ICloneable接口(实例克隆),因为这可能会导致多个对象实例,与Singleton模式的初衷违背。(实现了ICloneable接口,就必须实现Clone()方法,在此方法中我们可以返回新的对象,这样就与Singleton模式相违背了!)
                  • Singleton模式一般不要支持序列化,因为这也有可能导致多个对象实例,同样与Singleton模式的初衷违背。(构造对象的方式:可以通过构造器,也可以通过序列化构造对象,序列化出来的对象和原来的对象地址是不一样的,是完全的深拷贝方式(创建新的对象))
                  • Singletom模式只考虑到了对象创建的管理,没有考虑对象销毁的管理。就支持垃圾回收的平台和对象的开销来讲,我们一般没有必要对其销毁进行特殊的管理。
          (缺点)• 不能应对多线程环境:在多线程环境下,使用Singleton模式仍然有可能得到Singleton类的多个实例对象。(在上面的代码第10行进行if判断的时候,假设现在有两个线程(A,B)同时执行到了此行,当A线程进行if判断,此时判断为null,就去实例化对象,但是A线程还没来得及进行对象的实例化的时候,此时B线程就进行了if的判断,并完成了对此对象的判断,判断为null,所以B线程就会去创建另一个实例,所以就创建了两个实例!)

    4.2、多线程Singleton 模式实现

 1 class Singleton
 2     {
 3         private static volatile Singleton instance = null; //volatile(编译器在对代码进行编译的时候,会对代码进行一些微调,对代码的顺序进行调整,
volatile关键字就保证了编译器不会对instance进行代码的微调,
这样就保证了严格意义上的多线程不会出现创建出多个实例的情况)
4 private static object lockHelper = new object(); //辅助器 5 private Singleton() //防止外界随意new此对象 6 { 7 } 8 public static Singleton Instance 9 { 10 get 11 { 12 if (instance == null) 13 { 14 lock (lockHelper) 15 { 16 if (instance == null)//双检查 17 { 18 instance = new Singleton(); 19 } 20 } 21 } 22 return instance; 23 } 24 } 25 }

优缺点:这种实现方式对多线程来说是安全的,同时线程不是每次都加锁,只有判断对象实例没有被创建时它才加锁(如果最外层的if不加的话,那么其他线程都会进行加锁操作,会增加额外的开销,损失性能),加锁后还得再进行对象是否已被创建的判断。它解决了线程并发问题,同时避免在每个 Instance 属性方法的调用中都出现独占锁定。它还允许您将实例化延迟到第一次访问对象时发生。

volatile:

MSDN上的解释:volatile 关键字表示字段可能被多个并发执行线程修改。声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。这样可以确保该字段在任何时间呈现的都是最新的值

     4.3、 使用.NET类型初始化机制实现多线程Singleton 模式(最好的方式)

1    sealed class Singleton
2     {
3         private static readonly Singleton instance = new Singleton();
4         private Singleton() //防止外界随意new此对象
5         {
6         }
7     }

其实上面的代码实现如果展开来的话等同于下面代码的实现

 1   sealed class Singleton
 2     {
 3         private static readonly Singleton instance;
 4         static Singleton() //在调用instance静态字段时会先去执行此静态构造器,静态构造器能保证多线程环境下只有一个线程执行了此静态构造器,为我们自动加锁
 5         {                  
 6             instance = new Singleton();//在静态构造器中实例化
 7         }
 8         private Singleton() //防止外界随意new此对象
 9         {
10         }
11     }

该类标记为 sealed 以阻止发生派生,而派生可能会增加实例。此外,变量标记为 readonly,这意味着只能在静态初始化期间(此处显示的示例)或在类构造函数中分配变量。

缺点:

  • 我们对实例化机制的控制权较少!在4.1和4.2中能够在实例化之前使用非默认的构造函数或执行其他任务。由于在此实现方法中由 .NET Framework负责执行初始化,因此您没有这些选项。
  • 无法实现延迟初始化,
  • 4.3的实现方法无法进行传参,4.1和4.2的实现方式都可以进行传参,主要是因为对象的实例化是在方法内部创建的(属性实质为方法),所以我们可以把属性写成带参数的方法即可!如下,对4.1方法变为带参数的!
 1  class Singleton
 2     {
 3         private static Singleton instance;
 4         public string Name { get; set; }
 5         public int Age { get; set; }
 6         public static Singleton GetInstance(string name, int age)
 7         {
 8             if (instance == null)
 9             {
10                 instance = new Singleton(name, age);
11             }
12             return instance;
13         }
14         private Singleton(string name, int age)
15         {
16             this.Name = name;
17             this.Age = age;
18         }
19     }

5、延迟初始化

 1 public sealed class Singleton
 2  {
 3      Singleton()
 4      {
 5      }  
 6      public static Singleton Instance
 7      {
 8          get
 9         {
10             return Nested.instance;
11         }
12      }    
13     class Nested
14     {
15         static Nested()
16         {
17         }
18        internal static readonly Singleton instance = new Singleton();
19     }
20 }

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

6、Singleton模式扩展
            • 将一个实例扩展到固定几个实例,例如对象池的实现。这样做是允许的而且是有意义的!
            • 将new 构造器的调用转移到其他类中,例如多个类协同工作环境中,某个局部环境只需要拥有某个类的一个实例。
            • 理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的实例构造器的任意调用”。 

 7、.NET框架中的Singleton应用

  • winform程序,你只需要点击按钮,弹出一个窗口,并且保证此窗口只能有一个!再点击按钮是无法在弹出此窗体的!
  • PC机中可能有几个串口,但只能有一个COM1口的实例。
  • 系统中只能有一个窗口管理器。
  • .NET Remoting中服务器激活对象中的Sigleton对象,确保所有的客户程序的请求都只有一个实例来处理。

 

 8、推荐参考书
            • 《设计模式:可复用面向对象软件的基础》GoF                             --小白看起来会有点困难
            • 《面向对象分析与设计》Grady Booch                                       --翻译有些问题
            • 《敏捷软件开发:原则、模式与实践》Robert C. Martin
            • 《重构:改善既有代码的设计》 Martin Fowler
            • 《Refactoring to Patterns》Joshua Kerievsky                         --没中文版

 9、参考

    《C#面向对象设计模式纵横谈》    李建忠

     http://terrylee.cnblogs.com    TerryLee

 

                                                                                                  作者:MrZivChu

2013-08-02 15:34:03

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/MrZivChu/p/Singleton_patterns.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/274115.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android 第十一课 SQlite 数据库存储

Android 为了让我们能够更加方便的管理数据库,特意提供了一个SQLiteOpenHelper帮助类,通过借助这个类就可以非常简单的对数据库进行创建和升级。 SQLiteOpenHelper是一个抽象类,我们要创建一个自己的帮助类去继承它。SQLiteOpenHelper有两个抽…

浅析SQL Server 2005中的主动式通知机制

一、引言 在开发多人同时访问的Web应用程序(其实不只这类程序)时,开发人员往往会在缓存策略的设计上狠下功夫。这是因为,如果将这种环境下不常变更的数据临时存放在应用程序服务器或是用户机器上的话,可以避免频繁地往…

Android 第十二课 使用LitePal操作数据库(记得阅读最后面的注意事项哦)

一、LitePal简介 1、(新建项目LitePalTest)正式接触第一个开源库---LitePalLitePal是一款开源的Android 数据库框架,它采用了对象关系映射(ORM)的模式。2、配置LitePal,编辑app/build.gradle文件,在dependencies闭包中…

listview频繁刷新报错

在Android编程中使用Adapter时,偶尔会出现如下错误:The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI t…

Android 第十三课 SharedPreferences存储

SharedPreferences是使用键值对的方式来存储数据的。当保存一条数据时,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把相应的值取出来。而且支SharedPreferences还支持多种不同的数据类型存储,例如:如果…

DSP的Gel作用

转自:http://blog.csdn.net/azhgul/article/details/6660960最近刚在研究Davinci系,特此MARK下,以资后续学习之用。 DSP的Gel作用 1 GEL文件基本作用 当CCSStudio启动时,GEL文件加载到PC机的内存中,如果定义了StartUp(…

解决关于登录校园网显示不在IP段的问题方案(要看注意事项哦!)

有时,登录校园网,账号和密码都显示正确,但是却显示出“账号只能在指定IP段登录”的问题。 那我们就提供了一个解决方案: 使用WinR,并在输入框,输入cmd命令:(如下)接着输入&#xff1…

jquery插件编写

jQuery为开发插件提拱了两个方法,分别是: jQuery.fn.extend(object); jQuery.extend(object); jQuery.extend(object); 为扩展jQuery类本身.为类添加新的方法。可以理解为添加静态方法。是全局的(位于jQuery命名空间内部的函数)…

gtk/Glade编程 编译命令不成功 解决方法

摘自:http://blog.chinaunix.net/uid-26746982-id-3433656.html 当我们编写gtk/glade程序,gcc编译时,用如下命令: #gcc -o server server.c pkg-config --cflags --libs gtk-2.0 报错:/tmp/ccoXadAd.o: In function …

Android 第十五课 如何使用LitePal从SQLite数据库中删除数据(十四课用来保留讲解如何向SQLite数据库中存入数据)

使用LitePal删除数据的方式主要有两种,第一种就是直接调用已存对象的delete()方法,所谓已存储对象就是调用过save()方法的对象,或者说是通过LitePal提供的查询API查出来的对象,都是可以直接使用delete方法来删除对象的。这是比较简…

页面返回顶部(方法比较)

下面就说下简单的返回顶部效果的代码实现&#xff0c;附注释说明。 1. 最简单的静态返回顶部&#xff0c;点击直接跳转页面顶部&#xff0c;常见于固定放置在页面底部返回顶部功能 方法一&#xff1a;用命名锚点击返回到顶部预设的id为top的元素 html代码 <a href"#top…

Android 第十六课 使用LitePal查询数据

LitePal在查询API方面做了非常多的优化&#xff0c;基本上可以满足绝大多数场景的查询需求&#xff0c;并且代码也十分整洁。 例如我们需要查询表中的所有数据&#xff1a; List<books> DataSupport.findAll(Book.class); 没有冗长的参数列表&#xff0c;只需要调用一下…

linux创建桌面图标,和开始菜单栏图标

转自&#xff1a;http://blog.csdn.net/qq_25773973/article/details/50514767 ###环境&#xff1a;Mint17&#xff0c;&#xff08;其他类似的linux系统是一样的&#xff09; 如果开始菜单有图标&#xff0c;创建桌面图标很简单&#xff0c;右键添加到桌面即可。 如果没有&am…

ScrollView中使用ListView

转自 http://blog.csdn.net/fzh0803/article/details/7971391 由于scrollview和listview不能直接共存&#xff0c;在scrollview中直接使用lsitview的话只会显示一个条目&#xff0c;要使他们共存&#xff0c; 据我所知&#xff0c;有三种方法&#xff1a; 1。如果listview的高度…

Android 第十四课 使用LitePal添加数据(更新数据)

我们注意到当你登录一个app&#xff0c;是不是需要先注册呢&#xff1f;&#xff0c;所谓注册&#xff0c;简单地来理解是不是就是把输入框中地数据传入数据库中呢&#xff1f; 这里我们设置简单一点&#xff0c;注册的信息只包括两项&#xff0c;一项是用户名&#xff0c;另一…

微信公众平台的服务号和订阅号

微信公众平台 服务号 订阅号 作者&#xff1a;方倍工作室 地址&#xff1a;http://www.cnblogs.com/txw1958/p/ServiceNumber-subscriptionNumber.html 什么是服务号&#xff1f; 服务号给企业和组织提供更强大的业务服务与用户管理能力&#xff0c;帮助企业快速实现全新的公众…

Android 第十七课 碎片的简单用法及动态添加碎片

Fragment(碎片)是一种可以嵌入在活动当中的UI片段&#xff0c;它可以让程序更加合理和充分的利用大屏幕的空间。碎片和活动太像了&#xff0c;同样都包含布局&#xff0c;都有自己的声明周期&#xff0c;可以将碎片理解为一种迷你型的活动。 新建FragmentTest项目。假设项目已经…

在Linux下禁用键盘、鼠标、触摸板(笔记本)等输入设备

在Linux系统下禁用键盘、触摸板、鼠标等输入设备&#xff0c;可以通过xinput命令来实现&#xff1a;主要涉及&#xff1a;#xinput list#xinput list-props list-number#xinput set-prop list-number func-number 1/0具体操作如下&#xff1a;step1&#xff1a;查看系统中有那些…

委托又给我惹麻烦了————记委托链的取消注册、获取返回值

今天改bug碰到了一个问题&#xff0c;有多个方法注册到了一个事件里去&#xff0c;而这些方法本身又有点儿互斥&#xff0c;因而造成了bug&#xff0c;哥调试半天才发现&#xff0c;郁闷至极&#xff0c;遂复习了以前的知识并进行适当延伸&#xff0c;再将成果记录及分享之&…

Python第一课

对python仰慕已久&#xff0c;今日下定决心学习。可能我是一时头脑发热&#xff0c;但我还是愿意坚持。 先了解一下&#xff1a;命令行模式和Python交互模式 在Windows开始菜单选择“命令提示符”&#xff0c;就进入到命令行模式&#xff0c;它的提示符类似C:\>&#xff1a;…