首页 > 其他分享 >单件模式的困境与替代方案

单件模式的困境与替代方案

时间:2025-01-19 09:25:33浏览次数:1  
标签:示例 静态 void 单件 模式 困境 接口 替代

引言

  • 简要介绍单件模式的定义和常见用途。
  • 提出单件模式在实际开发中存在的问题,尤其是多线程环境下的复杂性。
  • 说明本文将探讨单件模式的困境,并提供几种替代方案。

1. 单件模式的困境

1.1 多线程场景下的复杂性

  • 问题

    • 多线程环境下,单件模式的实现需要考虑线程安全问题。
    • 双重检查锁定(Double-Checked Locking)的复杂性。
  • 示例

    Singleton* Singleton::getInstance() {
        if (instance == nullptr) {  // 第一次检查
            std::lock_guard<std::mutex> lock(mutex);
            if (instance == nullptr) {  // 第二次检查
                instance = new Singleton();
            }
        }
        return instance;
    }
    

1.2 暴露不必要的细节

  • 问题

    • 单件模式将“只有一个对象”这一实现细节暴露给使用者。
    • 增加了代码的耦合性。
  • 示例

    • 使用者需要显式调用 Singleton::getInstance()

1.3 线程安全性无法保证

  • 问题

    • 即使单件对象的创建是线程安全的,其成员函数的线程安全性仍需额外保证。
  • 示例

    void Singleton::doSomething() {
        std::lock_guard<std::mutex> lock(mutex);
        // 线程安全的操作
    }
    

1.4 单件模式不符合类的设计初衷

  • 问题

    • 类的设计初衷是封装数据和行为,而单件模式强制只有一个对象,违背了这一原则。
    • 单件模式更像是一个全局变量,而不是一个真正的类。

2. 单件模式的替代方案

2.1 使用类似 C 的接口

  • 描述

    • 将功能封装在一组全局函数中,而不是强制使用单件对象。
  • 优点

    • 避免了单件模式的复杂性。
    • 使用者不需要关心对象的生命周期。
  • 示例

    namespace MyModule {
        void initialize();
        void doSomething();
        void cleanup();
    }
    

2.2 使用静态类

  • 描述

    • 将功能封装在一个静态类中,所有成员函数和变量都是静态的。
  • 优点

    • 避免了单件模式的复杂性。
    • 使用者不需要显式获取单件对象。
  • 示例

    class MyModule {
    public:
        static void initialize();
        static void doSomething();
        static void cleanup();
    private:
        static std::mutex mutex;
        static int sharedData;
    };
    

2.3 依赖注入

  • 描述

    • 通过依赖注入将对象传递给使用者,而不是让使用者直接获取单件对象。
  • 优点

    • 提高了代码的可测试性和灵活性。
    • 避免了全局状态。
  • 示例

    class MyService {
    public:
        void doSomething();
    };
    
    class MyClass {
    public:
        MyClass(MyService& service) : service(service) {}
        void useService() {
            service.doSomething();
        }
    private:
        MyService& service;
    };
    

3. 何时使用静态类?何时使用类似 C 的接口?

3.1 使用静态类的场景

  • 接口固定且内在联系强

    • 如果一组函数或方法在逻辑上紧密相关,且接口(函数签名)相对固定,可以使用静态类来封装这些功能。
    • 示例:MQ 交互类、配置管理类、日志工具类。
  • 需要共享状态

    • 如果多个函数需要共享某些状态(如配置、缓存、连接等),可以使用静态类来管理这些状态。
  • 功能模块化

    • 如果某个功能模块需要独立封装,且不需要实例化对象,可以使用静态类。

3.2 使用类似 C 的接口的场景

  • 接口不固定或功能分散

    • 如果一组函数在逻辑上没有紧密联系,或者接口可能经常变化,可以使用类似 C 的接口。
    • 示例:字符串处理函数、数学工具函数。
  • 不需要共享状态

    • 如果一组函数不需要共享状态,且每个函数都是独立的,可以使用类似 C 的接口。
  • 跨语言兼容性

    • 如果代码需要与其他语言(如 C、Python)交互,可以使用类似 C 的接口,因为 C 风格的接口更容易被其他语言调用。

4. 设计原则与哲学

4.1 单一职责原则(SRP)

  • 描述

    • 一个类或模块应该只有一个职责。
  • 应用

    • 单件模式通常会导致类承担过多的职责(如对象管理、业务逻辑等),而静态类或类似 C 的接口可以更好地分离职责。

4.2 开闭原则(OCP)

  • 描述

    • 软件实体应该对扩展开放,对修改关闭。
  • 应用

    • 单件模式通常难以扩展,而依赖注入和类似 C 的接口可以更容易地扩展功能。

4.3 依赖倒置原则(DIP)

  • 描述

    • 高层模块不应该依赖低层模块,二者都应该依赖抽象。
  • 应用

    • 单件模式通常会导致高层模块直接依赖具体的单件类,而依赖注入可以通过抽象接口解耦依赖。

4.4 哲学思考

  • 全局状态的弊端

    • 单件模式本质上是一种全局状态,而全局状态会降低代码的可测试性和可维护性。
    • 通过依赖注入或类似 C 的接口,可以避免全局状态,使代码更加模块化和可测试。
  • 简单性与复杂性

    • 单件模式看似简单,但实际上隐藏了复杂的线程安全问题和耦合性问题。
    • 使用静态类或类似 C 的接口可以简化设计,降低复杂性。

5. 总结

单件模式在 C++ 开发中存在诸多问题,尤其是在多线程环境下。

为了避免这些问题,可以考虑使用静态类、类似 C 的接口或依赖注入等替代方案。

这些方案不仅简化了代码,还提高了灵活性和可维护性。

从设计原则和哲学的角度来看,单件模式违背了单一职责原则、开闭原则和依赖倒置原则,而替代方案则更好地遵循了这些原则。

通过避免全局状态和简化设计,我们可以编写出更加健壮和可维护的代码。

希望这些整理和建议对你有帮助!如果还有其他问题,欢迎随时交流!

标签:示例,静态,void,单件,模式,困境,接口,替代
From: https://www.cnblogs.com/Rong-/p/18679200

相关文章

  • ADCP414、ADCP416四通道125MSPS速率ADC替代AD9653、AD9253,可提供ZZKK证明
    ADCP416-125/105/80是一款4通道、16位、125/105/80MSPS模数转换器(ADC),内置片内采样保持电路,专门针对低成本、低功耗、小尺寸和易用性而设计。该产品的转换速率最高可达125MSPS,具有杰出的动态性能与低功耗特性,适合比较重视小封装尺寸的应用。ADCP416-125特性和优势--电源供电:1.......
  • MyBatisPlus替代繁琐的SQL语句(文章一)。爽!!!
    前置条件:能够创建SpringBoot项目(不会的可以查看主页之前的文章)内容比较多,会分为四篇文章对MyBatisPlus剖析文章一:标准数据层开发文章二:DQL编程控制文章三:DML编程控制文章四:细节补充步骤一:创建SpringBoot工程,勾选数据库驱动依赖步骤二:依赖管理(springboot2的2.5.0版本支持M......
  • kafka 根据 raft 协议实现了 KRaft 替代 zk
    Kafka是一个分布式流处理平台,传统上使用ApacheZooKeeper(ZK)来管理和协调分布式集群中的集群元数据和配置。ZooKeeper在Kafka中主要用于以下任务:集群元数据管理:存储和更新Kafka代理(broker)和主题的信息。分区领导者选举:帮助选举Kafka分区的领导者。集群成员管理:跟踪活......
  • AI Agent 智能体平台:助力制造业突破数字化转型困境的新法器
    一、工厂数字化转型步履维艰在制造业的广阔版图中,有一家颇具规模的汽车零部件制造企业A公司,一直以来在行业内小有名气。随着市场竞争的日益激烈,A公司决定踏上数字化转型之路,期望通过引入先进的技术和管理模式,提升生产效率、降低成本,增强自身的市场竞争力。A公司投入了大量......
  • Pinia 替代 localStorage 的常规使用场景
    在现代Vue工程化项目中,通常推荐使用Pinia(或Vuex)来管理状态,而不是直接使用localStorage。我来解释下具体原因和使用方式:1、为什么使用Pinia替代localStorage://使用Pinia的优势:-状态集中管理,更容易维护-支持响应式-支持开发工具调试-支持TypeScript-可以配......
  • 在 .NET 9 中使用 Scalar 替代 Swagger
    前言在.NET9发布以后ASP.NETCore官方团队发布公告已经将Swashbuckle.AspNetCore(一个为ASP.NETCoreAPI提供Swagger工具的项目)从ASP.NETCoreWebAPI模板中移除,这意味着以后我们创建WebAPI项目的时候不会再自动生成SwaggerAPI文档了。那么今天咱们一起来试试把我们的Easy......
  • 国内地图商用收费?可以考虑替代方案
    这两年高德、百度、腾讯的官方工作人员陆续打电话给各开发者,三家统一收取5万一年的商业授权费,不交钱就封号,不给用。他们的条款对于日调用量300万的用户和对于像我们这种日调用量几百几千的用户,都统一一刀切。现在这样的大环境,很多公司或个人开发者的收益连维护生活都艰难,这5万......
  • (倍福授权)国产EtherCAT从站控制芯片P2P替代ET1100
    EtherCAT技术是德国的倍福自动化(Beckhoff)开发,处于EtherCAT技术协会(ETG)框架之下,是一项开放但不开源的技术,任何相关设备的开发,都需要向其获取相关授权。就目前来看,获得Beckhoff授权的厂商并不多,而且大部分都是海外半导体厂商。不过近几年,随着国内EtherCAT市场的增长,情况开始有所改......
  • 网络安全市场正面临“红海”困境
    【网络安全行业“困”与“机”系列观察】从1月7日开始,这个冬天的“三九天”来临,这是一年里最冷的一段时间。前几天,一场主题为“冬天有多长”的网络安全行业市场年会举行。显然,这场年会不是探讨天气,而是剖析网络安全行业的长期困境。“经济大环境影响、行业内部碎片化、对IT价值......
  • 【反向代理】使用nps替代frp,反向代理nas更加容易
    #nps#npc#frp#反向代理为什么选择nps而不是frp:frp的语法一直在变动,小白配置很容易出错nps服务端提供了网页的管理配置界面,更加容易上手。而frps的网页只能查看,无法配置使用nps和npc进行反向代理部署教程nps是一款轻量级的反向代理工具,支持内网穿透,结合客户端npc......