设计模式SinglePatternsCreational标签03标签04标签05

C#02:(创建型模式)Singleton 单例模式.pdf

引用:

  • 代码及信息借鉴于李建忠老师的MSDN和TerryLee的文章。

说明:

  • 单例模式
  • 创建型模式

视频

点击查看【bilibili】

动机 —Motivation

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

如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?
这应该是类设计者的责任,而不是使用者的责任。**

意图 —Intent

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

结构 —Structure

image.png

生活中的例子

  1. ![](https://cdn.nlark.com/yuque/0/2021/jpeg/446847/1615442431348-f7f26829-6023-43e5-afdd-8b86a7f1ab6c.jpeg#align=left&display=inline&height=166&margin=%5Bobject%20Object%5D&originHeight=166&originWidth=387&size=0&status=done&style=none&width=387)

适用性

  1. 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
  2. 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

    单线程Singleton模式下的几个要点总结

  • Singleton模式中的实例构造器可以设置为Protected以允许子类派生;
  • Singleton模式一般不要支持ICloneable接口,因为这可能会导致多个对象实例,与Singleton模式的初衷违背;
  • Singleton模式一般不要支持序列化,因为这也可能导致多个对象实例,同样与Singleton模式的初衷相互违背;
  • Singleton模式只考虑到了对象创建的管理,没有考虑对象销毁的管理。就支持垃圾回收的平台喝对象的开销来讲,我们一般没有必要对其销毁进行特殊的管理; :::tips

  • 静态构造器只会交给一个线程去执行,这样就避免了在执行IF时多进程造成的混乱。

  • 静态构造器不支持传参数。 :::

    Singleton模式扩展

  • 将一个实例扩展到n个实例,例如对象池的实现;

  • 将new构造器的调用转移到其它类中,例如多个类协同工作环境中,某个局部环境需要拥有某个类的一个实例;
  • 理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的实例构造器的任意调用”。

    推荐书籍

  • 设计模式:可复用面向对象软件的基础

  • 面向对象分析与设计—GradyBooch
  • 敏捷软件开发:原则,模式与实践 Robert C.Martin
  • 重构;改善既有代码的设计 Martin Fowler
  • Refactoring to Patterns -Joshua Kerievsky

    源代码

    单线程Singleton实现

    1. class SingleThread_Singleton
    2. {
    3. private static SingleThread_Singleton instance = null;
    4. private SingleThread_Singleton(){}
    5. public static SingleThread_Singleton Instance
    6. {
    7. get
    8. {
    9. if (instance == null)
    10. {
    11. instance = new SingleThread_Singleton();
    12. }
    13. return instance;
    14. }
    15. }
    16. }

    以上代码在单线程情况下不会出现任何问题。但是在多线程的情况下却不是安全的。
    如两个线程同时运行到 if (instance == null)判断是否被实例化,一个线程判断为True后,在进行创建
    instance = new SingleThread_Singleton();之前,另一个线程也判断(instance == null),结果也为True.
    这样就就违背了Singleton模式的原则(保证一个类仅有一个实例)。
    怎样在多线程情况下实现Singleton?

    多线程Singleton实现

    1. class MultiThread_Singleton
    2. {
    3. private static volatile MultiThread_Singleton instance = null;
    4. private static object lockHelper = new object();
    5. private MultiThread_Singleton() { }
    6. public static MultiThread_Singleton Instance
    7. {
    8. get
    9. {
    10. if (instance == null)
    11. {
    12. lock (lockHelper)
    13. {
    14. if (instance == null)
    15. {
    16. instance = new MultiThread_Singleton();
    17. }
    18. }
    19. }
    20. return instance;
    21. }
    22. }

    此程序对多线程是安全的,使用了一个辅助对象lockHelper,保证只有一个线程创建实例(如果instance为空,保证只有一个线程instance = new MultiThread_Singleton();创建唯一的一个实例)。(Double Check)
    请注意一个关键字volatile,如果去掉这个关键字,还是有可能发生线程不是安全的。
    volatile 保证严格意义的多线程编译器在代码编译时对指令不进行微调。

    静态Singleton实现

    1. class Static_Singleton
    2. {
    3. public static readonly Static_Singleton instance = new Static_Singleton();
    4. private Static_Singleton() { }
    5. }

    以上代码展开等同于

    1. class Static_Singleton
    2. {
    3. public static readonly Static_Singleton instance;
    4. static Static_Singleton()
    5. {
    6. instance = new Static_Singleton();
    7. }
    8. private Static_Singleton() { }
    9. }

    由此可以看出,完全符合Singleton的原则。

    优点: 简洁,易懂
    缺点: 不可以实现带参数实例的创建。

    总结

  • 设计模式描述了软件设计过程中某一类常见问题的一般性的解决方案。面向对象设计模式描述了面向对象设计过程中、特定场景下、类与相互通信的对象之间常见的组织关系。

  • 深刻理解面向对象是学好设计模式的基础,掌握一定的面向对象设计原则才能把握面向对象设计模式的精髓,从而实现灵活运用设计模式。
  • 三大基本面向对象设计原则
    • 针对接口编程,而不是针对实现编程
    • 优先使用对象组合,而不是类继承
    • 封装变化点
    • 使用重构得到模式。敏捷软件开发实践提倡的“RefactoringtoPatterns”是目前普遍公认的最好的使用设计模式的方法。

  • 本文作者:GeekPower - Felix Sun
  • 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!