Sington(单例模式)

一、使用Sington单例模式的动机(Motivation)

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

大多数类用的是常规的构造器,所以往往能创建很多实例,那么如何绕过常规的构造器,并且提供一种机制(设计模式)来保证一个类只有一个实例。

二、使用Singtong(单例设计模式)的意图

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

三、结构

四、使用Singleton(单例设计模式)需要注意的几个点

1、Singleton模式中的实例构造器可以设置成protected,方便子类继承

2、Singleton模式一般不要支持ICloneable接口,因为这可能会创建出多个实例,这与Singleton模式的初中所违背

3、Singleton模式也不要支持序列化,这也可能创建出多个对象实例

4、Singleton只考虑了对象创建的管理,并没有考虑对象销毁的管理,就支持垃圾回收的平台和对象来讲,这么点开销,一般没有必要对其进行特殊的管理,除非这个类超级大(但是如果这个类如果很大的话,那这个类需要重构).

5、不能应对多线程的情况,如果在多线程环境下,下面的紧接着的实例代码可能会创建出多个实例。

五、代码演示

1、单线程Singleton(单例模式)实现         (最基本的一种)

using System;
namespace Singleton
{
    class Program
    {
        static void Main(string[] args)
        {
            Singleton1 s1 = Singleton1.getInstance();
            Singleton1 s2 = Singleton1.getInstance();
            Console.WriteLine("s1和s2{0}同一个实例", (Object.ReferenceEquals(s1, s2) == true) == true ? "是" : "不是");//输出:s1和s2是同一个实例
        }
    }
    /*
     * (单线程)单例模式的第一种实现方式(最基本的实现方法)
     * 目地:实现用单例模式实现的类只有一个实例,而且全局共享这个实例
     */
    class Singleton1
    {
        private static Singleton1 instance { get; set; }
        //这里使用私有构造函数的原因是:因为如果我们不给类定义构造函数,那么C#编译器会给当前类加一个默认的共有的构造器函数,但是如果我们在类中定义了构造函数那么C#编译器
        //将不会在该类中添加默认的共有构造器函数,所以我们在这里定义一个私有构造器,那么C#编译器将不会给类添加共有的构造器,而且这个构造器将不会被外界调用
        //所以该类无法被实例化,也就是new出来
        private Singleton1() { }

        //1、既然Singleton1类无法在外部被实例化,那么我们就必须在内部Singleton1实例化,然后提供一个公有的方法将该实例返回
        
        public static Singleton1 getInstance() {

            //这里的if判断是保证该实例全局唯一,保证外部调用的Singleton1的实例是唯一的
            if (instance == null)
            {
                instance = new Singleton1();
            }
            return instance;
        }

    }
}

分析:根据控制台的输出可以肯定的是,Singleton1.getInstance()创建出来的实例都是同一个实例,但是这里存在一个问题,这只是在单线程的情况下是这样的,如果在多线程的情况下,假设两个线程同时判断if(instance==null),那么接下来会new出两个不同的实例。所以上面的代码仅适用于单线程的情况!

2、多线程单例模式

using System;
namespace Singleton
{
    class SingleByManyThread
    {
        static void Main(string[] args) 
        { 
        
        }
    }
    class Singleton 
    {
        //volatile关键字的作用是让编译器严格按照下面代码的逻辑来执行
        //如果不加volatile,那么C#编译器在莫种情况下可能会对下面的多线程处理代码做微调,那么也可能出现两个实例的情况

        private static volatile Singleton instance { get; set; }
        //给lock语句提供的Object对象,不能是值或者string类型,具体原因参考多线程lock语句的规范
        private static Object lockHelper = new object();
        //1、将构造器函数设置私有的原因是:外部对象调用本类时,无法通过new的方式,只能通过本类内部提供的方法来获取本类的实例
        //2、这里注意:如果不给类定义构造器函数,那么C#编译器会给类添加一个默认的公有构造器函数,如果我们自己定义了,那么C#编译器就不会自己定义了
        private Singleton() { }

        //将本类的实例通过本类公开的属性给外部类(对象)访问
        public static Singleton Instance
        {   
            //这里做双重检查来保证多个线程不会同时进入if语句
            get 
            {
                if (instance == null) 
                {   
                    //在多线程环境中,给单个线程加锁,防止在一个线程在访问lock内代码时,另一个线程也在方法,起到线程隔离的作用
                    //这样就解决了多线程环境下可能创建出两个Singleton实例的情况
                    lock (lockHelper) 
                    {
                        if (instance==null)
                        {
                            instance = new Singleton();
                        }
                    }
                }
                return instance;
            }
        }
    }
}

3、既能解决多线程环境,也能实现Singleton模式的实现方法(单线程和多线程下都能实现Singleton模式(单例模式))的实现方法

using System;
namespace Singletons
{
    class SingletonBests
    { 
        static void Main(string[] args)
        {
        
        }
    }
    class SingletonBest
    {
        //这是一种专业术语叫"内联初始化"的创建的实例的方式
        //用这种方式创建的SingletonBest实例C#编译器会在New SingletonBest()之前调用SingleBest1类中static静态构造器给Instance实例赋值
        //C#编译器会给静态构造器加锁,所以不需要使用lock来解决多线程创建多个实例的问题
        //这种模式结合前面两种模式共同的功能,但是存在一个问题
        //因为静态构造器是个C#运行时生成,给系统调用的,所以无法利用构造函数对其进行初始化
        public static readonly SingletonBest Instance = new SingletonBest();
        private SingletonBest() { }
    }
    
    //上面的内联初始化的方式等同于下面的创建方式
    class SingleBest1
    {
        //现在内存中开辟Instance实例的空间,在通过静态构造器给Instance赋值
        public static readonly SingleBest1 Instance;
        //静态构造函数无法参数化
        static SingleBest1()
        {
            Instance = new SingleBest1();
        }
        private SingleBest1() { }
    }
}

4、解决"内联初试化"实现单例模式静态构造函数无法参数化的问题

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Singleton
{
    class SingletonParametric
    {   
        //当我们编写一个类时,大多数情况下,数据初始化的任务都会交给构造器函数
        //但是这里我们使用的是内联初始化的方式来实现的单例模式,这种方式,创建实例会交给静态构造函数来实现
        //而静态构造函数不能给他传递参数,应为静态构造函数是给系统调用的
        //所以如果要使用内联初始化的方式来实现单例模式的情况下,初始化参数只能交给公开的方法和属性来实现
        public static readonly SingletonParametric Instance = new SingletonParametric();
        private SingletonParametric() { }

        //通过公开一个OnInit()来实现当前类的参数化
        public void OnInit() { 
        
        }
        //通过公开属性的方式给当前实例传参
        private int _x;
        private int _y;
        public int X 
        {
            get { return _x; }
            set { _x = value; }
        }
        public int Y 
        {
            get { return _y; }
            set { _y = value; }
        }

    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏哲学驱动设计

GIX4中懒加载

在使用OpenExpressApp框架的GIX4项目中,对象的懒加载按照对象的性质不同,分为以下两种方式: 根对象类:     Get方法:获取根对象,并附带上...

1958
来自专栏耕耘实录

Bash shell中四种算术运算方式的区别与联系简介

版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转载请注明出处,谢谢

762
来自专栏蓝天

带有通配符的字符串匹配算法-C/C++

日前某君给我出了这样一道题目:两个字符串,一个是普通字符串,另一个含有*和?通配符,*代表零个到多个任意字符,?代表一个任意字符,通配符可能多次出现。写一个算法...

2213
来自专栏nummy

单例模式

如果想使得某个类从始至终最多只有一个实例,使用new方法会很简单。Python中类是通过new来创建实例的:

892
来自专栏python百例

44-列表方法

941
来自专栏Java学习123

Java transient关键字使用小记

4842
来自专栏ml

NYOJ-----最少乘法次数

最少乘法次数 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 给你一个非零整数,让你求这个数的n次方,每次相乘的结果可以在后面使用...

29911
来自专栏H2Cloud

智能指针shared_ptr【无锁设计基于GCC】

1. shared_ptr 介绍   使用过Boost的话对shared_ptr一定有很深的印象。多个shared_ptr指向同一个对象,每个shared_pt...

3953
来自专栏成猿之路

Java面试题-基础篇一

可以有多个类,但只能有一个public的类,并且public的类名必须和文件名一致。

993
来自专栏技术沉淀

Python: json模块实例详解

2334

扫码关注云+社区

领取腾讯云代金券