【愚公系列】2021年12月 二十三种设计模式(五)-单例模式(Singleton Pattern)

简介: 【愚公系列】2021年12月 二十三种设计模式(五)-单例模式(Singleton Pattern)

文章目录

前言

一、单例模式(Singleton Pattern)

二、使用步骤

角色

示例

总结

优点

缺点

使用场景

前言

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。


提示:以下是本篇文章正文内容,下面案例可供参考


一、单例模式(Singleton Pattern)

单例模式属于创建型模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。


一个类有且仅有一个实例,并且自行实例化向整个系统提供。


二、使用步骤

角色

1、单例类(Singleton)


保证唯一并提供全局访问点的单例类。


示例

image.png

命名空间SingletonPattern中包含7个单例类,本案例将介绍这7种常见的单例实现方法。

public sealed class Singleton {
    private static Singleton _instance = null;
    public static Singleton GetInstance() {
        if(_instance == null) {
            _instance = new Singleton();
            Console.WriteLine("Singleton.GetInstance()!");
        }
        return _instance;
    }
}

最常见的单例类,但是无法保证线程安全。因为首次运行时,n个线程可同时到达if(_instance == null),导致_instance可能会被多初始化n-1次(有1次是需要初始化的)。在_instance被初始化之后新启动的线程不会使该情况重现。

public sealed class SingletonSafe {
    private static SingletonSafe _instance = null;
    private static readonly object _lock = new object();
    public static SingletonSafe GetInstance() {
        lock (_lock) {
            if (_instance == null) {
                _instance = new SingletonSafe();
                Console.WriteLine("SingletonSafe.GetInstance()!");
            }
        }
        return _instance;
    }
}

使用私有静态object类型的锁(微软推荐),lock关键字会占有该锁,之后请求该锁的其它线程必需等待其释放才能进入。该方法可实现线程安全的单例模式,但是锁属于昂贵资源,“占有锁”和“释放锁”都比较耗时,并会在一定程度上阻止其它线程的执行,会显著影响程序的并发性,所以有了下面的优化。

public sealed class SingletonSafe2 {
    private static SingletonSafe2 _instance = null;
    private static readonly object _lock = new object();
    public static SingletonSafe2 GetInstance() {
        if (_instance == null) {
            lock (_lock) {
                if (_instance == null) {
                    _instance = new SingletonSafe2();
                    Console.WriteLine("SingletonSafe2.GetInstance()!");
                }
            }
        }
        return _instance;
    }
}

通过优先使用if (_instance == null)这种耗费资源较少的比较来决定是否进入锁,可大幅度提高性能。因为_instance不为null时,直接返回即可。

public sealed class SingletonLazy {
    private static readonly Lazy<SingletonLazy> _instance =
        new Lazy<SingletonLazy>(() => {
            Console.WriteLine("SingletonLazy.GetInstance()!");
            return new SingletonLazy();
        });
    public static SingletonLazy GetInstance() {
        return _instance.Value;
    }
}

带泛型的Lazy式单例实现,这是线程安全的,仅提供给大家参考。

public sealed class SingletonReadOnly {
    private static readonly SingletonReadOnly _instance =
        new SingletonReadOnly();
    public SingletonReadOnly() {
        Console.WriteLine("SingletonReadOnly.GetInstance()!");
    }
    public static SingletonReadOnly GetInstance() {
        return _instance;
    }
}

静态只读式单例实现(由运行时保证唯一),这是线程安全的,仅提供给大家参考。

public abstract class SingletonGenericBase<T> where T : class, new() {
    private static T _instance = null;
    private static readonly object _lock = new object();
    public static T GetInstance() {
        if (_instance == null) {
            lock (_lock) {
                if (_instance == null) {
                    _instance = new T();
                    Console.WriteLine("SingletonGeneric.GetInstance()!");
                }
            }
        }
        return _instance;
    }
}
public sealed class SingletonGeneric : SingletonGenericBase<Singleton> {
    public SingletonGeneric() { }
}

复杂的泛型实现,这是线程安全的,仅提供给大家参考。

public abstract class SingletonGenericBase2<T> where T : class {
    private static readonly Lazy<T> _instance = new Lazy<T>(() => {
        var ctors = typeof(T).GetConstructors(
            BindingFlags.Instance
            | BindingFlags.NonPublic
            | BindingFlags.Public);
        if (ctors.Count() != 1)
            throw new InvalidOperationException(
                String.Format("Type {0} must have exactly one constructor.",
                              typeof(T)));
        var ctor = ctors.SingleOrDefault(
            c => !c.GetParameters().Any() && c.IsPrivate);
        if (ctor == null)
            throw new InvalidOperationException(
                String.Format("The constructor for {0} must be private and take no parameters.",
                              typeof(T)));
        Console.WriteLine("SingletonGeneric2.GetInstance()!");
        return (T)ctor.Invoke(null);
    });
    public static T GetInstance() {
        return _instance.Value;
    }
}
public sealed class SingletonGeneric2 : SingletonGenericBase2<SingletonGeneric2> {
    private SingletonGeneric2() { }
}

复杂的泛型实现,这是线程安全的,仅提供给大家参考。

public class Program {
    public static void Main(string[] args) {
        var singleton = Singleton.GetInstance();
        singleton = Singleton.GetInstance();
        var singletonSafe = SingletonSafe.GetInstance();
        singletonSafe = SingletonSafe.GetInstance();
        var singletonSafe2 = SingletonSafe2.GetInstance();
        singletonSafe2 = SingletonSafe2.GetInstance();
        var singletonReadOnly = SingletonReadOnly.GetInstance();
        singletonReadOnly = SingletonReadOnly.GetInstance();
        var singletonLazy = SingletonLazy.GetInstance();
        singletonLazy = SingletonLazy.GetInstance();
        var singletonGeneric = SingletonGeneric.GetInstance();
        singletonGeneric = SingletonGeneric.GetInstance();
        var singletonGeneric2 = SingletonGeneric2.GetInstance();
        singletonGeneric2 = SingletonGeneric2.GetInstance();
        Console.ReadKey();
    }
}

以上是调用方的代码,每个GetInstance方法均调用2次以展示效果。以下是这个案例的输出结果:

Singleton.GetInstance()!
SingletonSafe.GetInstance()!
SingletonSafe2.GetInstance()!
SingletonReadOnly.GetInstance()!
SingletonLazy.GetInstance()!
SingletonGeneric.GetInstance()!
SingletonGeneric2.GetInstance()!

总结

优点

1、单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例;

2、因为类控制了实例化过程,所以类可以灵活更改实例化过程。


缺点

1、没有接口,不能继承,与单一职责原则冲突。


使用场景

1、需要频繁的进行创建和销毁的对象;

2、创建对象时耗时过多或耗费资源过多,但又经常用到的对象;

3、工具类对象;

4、频繁访问数据库或文件的对象。


相关文章
|
12月前
|
设计模式 缓存 安全
【设计模式】【创建型模式】单例模式(Singleton)
一、入门 什么是单例模式? 单例模式是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。它常用于需要全局唯一对象的场景,如配置管理、连接池等。 为什么要单例模式? 节省资源 场景:某些对象创
408 15
|
设计模式 Java 数据安全/隐私保护
Java 设计模式:装饰者模式(Decorator Pattern)
装饰者模式属于结构型设计模式,允许通过动态包装对象的方式为对象添加新功能,提供比继承更灵活的扩展方式。该模式通过组合替代继承,遵循开闭原则(对扩展开放,对修改关闭)。
|
设计模式 安全 Java
设计模式:单例模式
单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点。它通过私有化构造函数、自行创建实例和静态方法(如`getInstance()`)实现。适用于数据库连接池、日志管理器等需要全局唯一对象的场景。常见的实现方式包括饿汉式、懒汉式、双重检查锁、静态内部类和枚举。线程安全问题可通过`synchronized`或双重检查锁解决,同时需防止反射和序列化破坏单例。优点是避免资源浪费,缺点是可能增加代码耦合度和测试难度。实际开发中应优先选择枚举或静态内部类,避免滥用单例,并结合依赖注入框架优化使用。
|
设计模式 存储 安全
设计模式2:单例模式
单例模式是一种创建型模式,确保一个类只有一个实例,并提供全局访问点。分为懒汉式和饿汉式: - **懒汉式**:延迟加载,首次调用时创建实例,线程安全通过双重检查锁(double check locking)实现,使用`volatile`防止指令重排序。 - **饿汉式**:类加载时即创建实例,线程安全但可能浪费内存。 示例代码展示了如何使用Java实现这两种模式。
308 4
|
设计模式 存储 安全
设计模式-单例模式练习
单例模式是Java设计模式中的重要概念,确保一个类只有一个实例并提供全局访问点。本文详解单例模式的核心思想、实现方式及线程安全问题,包括基础实现(双重检查锁)、懒汉式与饿汉式对比,以及枚举实现的优势。通过代码示例和类图,深入探讨不同场景下的单例应用,如线程安全、防止反射攻击和序列化破坏等,展示枚举实现的简洁与可靠性。
211 0
|
设计模式 存储 前端开发
前端必须掌握的设计模式——单例模式
单例模式是一种简单的创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。适用于窗口对象、登录弹窗等场景,优点包括易于维护、访问和低消耗,但也有安全隐患、可能形成巨石对象及扩展性差等缺点。文中展示了JavaScript和TypeScript的实现方法。
638 13
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
226 2
|
12月前
|
设计模式 Java 数据库连接
【设计模式】【创建型模式】工厂方法模式(Factory Methods)
一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
327 16
|
12月前
|
设计模式 负载均衡 监控
并发设计模式实战系列(2):领导者/追随者模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
331 0
|
12月前
|
设计模式 监控 Java
并发设计模式实战系列(1):半同步/半异步模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第一章半同步/半异步(Half-Sync/Half-Async)模式,废话不多说直接开始~
451 0