关于设计模式:单件singletons的坏处是什么?

What is so bad about singletons?

singleton模式是gof模式书中一个完全付费的成员,但最近它似乎被开发人员世界孤立了。我仍然使用大量的单线程,尤其是工厂类,虽然您必须对多线程问题(实际上像任何类一样)稍微谨慎一些,但我不明白为什么它们如此糟糕。

堆栈溢出似乎特别假定每个人都同意单例是邪恶的。为什么?

请以"事实、参考资料或特定专业知识"支持您的答案。


从布莱恩·巴顿转述:

  • 它们通常被用作全局实例,为什么这么糟糕?因为您将应用程序的依赖项隐藏在代码中,而不是通过接口公开它们。使某个东西全局化以避免传递它是一种代码味道。

  • 他们违反了单一责任原则:因为他们控制自己的创造和生命周期。

  • 它们本质上导致代码紧密耦合。这使得在许多情况下,在测试中伪造它们相当困难。

  • 它们在应用程序的生命周期中携带状态。这是对测试的另一个冲击,因为您最终可能会遇到需要订购测试的情况,这对于单元测试来说是一个很大的禁忌。为什么?因为每个单元测试都应该独立于其他单元测试。


  • 单例解决一个(而且只有一个)问题。

    资源争用。

    如果你有一些资源

    (1)只能有一个实例,并且

    (2)您需要管理单个实例,

    你需要一个单身汉。

    例子不多。日志文件是大文件。您不想只放弃一个日志文件。您要正确刷新、同步和关闭它。这是必须管理的单个共享资源的示例。

    你很少需要单身。他们不好的原因是,他们感觉自己像一个全球性的,他们是一个完全付费的GOF设计模式书的成员。

    当你认为你需要一个全球性的,你可能会犯一个可怕的设计错误。


    一些编程的势利小人看不起他们只是一个光荣的全球。正如许多人讨厌goto语句一样,还有一些人讨厌使用global的想法。我看到过一些开发人员为了避免一个全局而付出了非常大的努力,因为他们认为使用一个全局开发人员会承认失败。奇怪但真实。

    实际上,单例模式只是一种编程技术,它是概念工具箱的有用部分。有时你可能会发现它是理想的解决方案,所以使用它。但是仅仅为了使用一个设计模式而使用它,就像拒绝使用它一样愚蠢,因为它只是一个全球性的。


    来自谷歌的Misko Hevery就这个话题发表了一些有趣的文章…

    单例是病态的说谎者,有一个单元测试的例子,说明了单例如何使理解依赖链和启动或测试应用程序变得困难。这是一个相当极端的虐待例子,但他提出的观点仍然有效:

    Singletons are nothing more than global state. Global state makes it so your objects can secretly get hold of things which are not declared in their APIs, and, as a result, Singletons make your APIs into pathological liars.

    所有的单例都消失了,这就说明依赖注入使得向需要它们的构造函数获取实例变得容易,这就减轻了在第一篇文章中谴责的糟糕的全局单例背后的潜在需求。


    我认为这种混淆是由于人们不知道单例模式的实际应用。我压力太大了。单件不是一种包装全局的模式。单例模式应该只用于确保在运行时存在给定类的一个且只有一个实例。

    人们认为独生子是邪恶的,因为他们把它用于全球。正是因为这种困惑,辛格尔顿才被人看不起。拜托,别把单身和环球混为一谈。如果用于预期目的,您将从单例模式中获得极大的好处。


    单件的一个相当糟糕的地方是你不能很容易地扩展它们。如果你想改变他们的行为,基本上你必须构建某种装饰图案或者类似的东西。另外,如果有一天你想有多种方法来做一件事,那么根据你的代码布局,改变可能会很痛苦。

    需要注意的一点是,如果你确实使用单件,试着把它们传递给任何需要它们的人,而不是让他们直接访问它……否则,如果您曾经选择使用多种方法来完成singleton所做的事情,那么很难进行更改,因为如果每个类直接访问singleton,则会嵌入依赖项。

    所以基本上:

    1
    2
    3
    public MyConstructor(Singleton singleton) {
        this.singleton = singleton;
    }

    而不是:

    1
    2
    3
    public MyConstructor() {
        this.singleton = Singleton.getInstance();
    }

    我相信这种模式被称为依赖注入,通常被认为是一件好事。

    就像任何模式…想一想,并考虑在给定的情况下使用它是否不当…规则通常是被打破的,模式不应该被随意地不加思考地应用。


    单例模式本身并不是问题。问题是,这种模式经常被使用面向对象工具开发软件的人使用,而没有对OO概念有一个扎实的理解。在这种上下文中引入单例时,它们往往会成长为不可管理的类,这些类中每使用一点辅助方法就包含一个辅助方法。

    从测试的角度来看,单例也是一个问题。它们往往使独立的单元测试难以编写。控制反转(IOC)和依赖注入(DependencyInjection)是以面向对象的方式来解决这个问题的模式,这有助于单元测试。

    在垃圾收集环境中,单例可以很快成为内存管理方面的问题。

    还有多线程场景,其中单例可能成为瓶颈和同步问题。


    使用静态方法实现单例。进行单元测试的人避免使用静态方法,因为它们不能被模仿或存根化。这个站点上的大多数人都是单元测试的支持者。避免它们的最普遍的惯例是使用控制模式的反转。


    单例在集群方面也很糟糕。因为这样,您的应用程序中就不再有"恰好一个单例"。

    考虑以下情况:作为开发人员,您必须创建一个访问数据库的Web应用程序。为了确保并发数据库调用不会相互冲突,可以创建一个线程save SingletonDao

    1
    2
    3
    4
    5
    6
    7
    8
    public class SingletonDao {
        // songleton's static variable and getInstance() method etc. omitted
        public void writeXYZ(...){
            synchronized(...){
                // some database writing operations...
            }
        }
    }

    因此,您可以确定应用程序中只存在一个单例,并且所有数据库都通过这个单例,并且只通过SingletonDao。您的生产环境现在如下所示:Single Singleton

    到目前为止一切都很好。

    现在,考虑在集群中设置Web应用程序的多个实例。现在,你突然有了这样的东西:

    Many singletons

    这听起来很奇怪,但现在您的应用程序中有许多单例。这正是单身汉不应该做的:拥有许多对象。如果您(如本例所示)希望对数据库进行同步调用,则这一点尤其糟糕。

    当然,这是一个单例的错误用法的例子。但是这个示例的信息是:您不能相信应用程序中只有一个单例实例,尤其是在集群方面。


  • 它很容易被用作全局变量。
  • 依赖于单例的类相对来说更难单独进行单元测试。

  • 垄断是魔鬼,而具有非只读/可变状态的单子是"真正"的问题…好的。

    在读过《单身汉是病态的说谎者》之后,正如杰森的回答所暗示的那样,我遇到了这个小消息,它提供了一个最好的例子,说明了单身汉经常被误用。好的。

    Global is bad because:

    Ok.

    • a. It causes namespace conflict
    • b. It exposes the state in a unwarranted fashion

    When it comes to Singletons

    Ok.

    • a. The explicit OO way of calling them, prevents the conflicts, so point a. is not an issue
    • b. Singletons without state are (like factories) are not a problem. Singletons with state can again fall in two categories, those which are immutable or write once and read many (config/property files). These are not bad. Mutable Singletons, which are kind of reference holders are the ones which you are speaking of.

    在最后一句话中,他指的是博客中"单身汉都是骗子"的概念。好的。

    这如何适用于垄断?好的。

    要开始垄断游戏,首先:好的。

    • 我们先制定规则,这样每个人都在同一个页面上
    • 每个人在比赛开始时都有平等的开始
    • 为了避免混淆,只提供了一组规则
    • 规则不允许在整个比赛中改变

    现在,对于任何没有真正玩过垄断的人来说,这些标准充其量是理想的。垄断的失败是很难被接受的,因为垄断是关于金钱的,如果你输了,你就必须苦心经营地看着其他玩家完成游戏,而损失通常是迅速的和压倒性的。因此,规则通常会在某一点上被扭曲,以牺牲其他玩家的利益为代价,为某些玩家服务。好的。

    所以你在和朋友鲍勃、乔和艾德玩垄断游戏。你正在迅速建立自己的帝国,以指数级的速度消费市场份额。你的对手正在削弱,你开始闻到血的味道(形象地说)。你的朋友鲍勃把他所有的钱都投入到尽可能多的低价值房地产的僵局中,但他的投资回报并不像他预期的那样高。鲍勃,作为一个不幸的打击,降落在你的木板路上,被从游戏中删除。好的。

    现在游戏从友好的掷骰子到严肃的生意。鲍勃是失败的榜样,乔和埃德不想最后变成"那个家伙"。所以,作为领头羊,你突然成为敌人。乔和艾德开始练习桌下交易,背后的资金注入,低估房屋交换和一般任何削弱你作为一个球员,直到其中一个上升到顶部。好的。

    然后,不是他们中的一个获胜,而是整个过程开始。突然,一组有限的规则变成了一个移动的目标,游戏退化成了社会互动的类型,这将成为每个幸存者的高等级真人秀节目的基础。为什么,因为规则正在改变,而且对于它们应该如何/为什么/代表什么没有共识,更重要的是,没有一个人做出决定。在这一点上,游戏中的每一个玩家都在制定自己的规则,混乱随之发生,直到两个玩家疲惫不堪,无法继续玩游戏,慢慢地放弃游戏。好的。

    因此,如果一个游戏的规则书准确地代表了一个单例,那么垄断规则书就是一个滥用的例子。好的。

    这如何适用于编程?好的。

    除了可变单例存在的所有明显的线程安全和同步问题之外…如果您有一组数据,能够同时被多个不同的源读取/操作,并且在应用程序执行的整个生命周期中都存在,那么现在可能是退后一步并询问"我是否在使用正确类型的数据结构"的好时机。好的。

    就我个人而言,我见过一个程序员滥用单例,将它用作应用程序中某种扭曲的跨线程数据库存储。在直接处理代码之后,我可以证明这是一个缓慢的过程(因为所有线程锁都需要使其线程安全)和一个噩梦(因为同步错误的不可预测/间歇性质),并且几乎不可能在"生产"条件下进行测试。当然,可以使用轮询/信令来开发一个系统来克服一些性能问题,但这并不能解决测试中的问题,而且,如果"真实"数据库已经能够以更健壮/可扩展的方式完成相同的功能,为什么还要费心呢?好的。

    如果您需要Singleton提供的功能,那么Singleton只是一个选项。对象的只写一个只读实例。同样的规则也应该级联到对象的属性/成员。好的。好啊。


    我对单身者如何糟糕的回答总是"他们很难做对"。语言的许多基本组件都是单例的(类、函数、名称空间甚至运算符),计算的其他方面(localhost、默认路由、虚拟文件系统等)的组件也是单例的,这并不是偶然的。虽然它们会不时地引起麻烦和沮丧,但它们也能使很多事情变得更好。

    我看到的两个最大的错误是:将其视为一个全球性的,并未能定义单例闭包。

    每个人都把单身汉当作全球性的,因为他们基本上是这样。然而,在一个全球,许多(可悲的是,不是全部)的坏处本质上不是来自于全球,而是你如何使用它。单身也一样。实际上,"单实例"并不意味着"全局可访问"。它更像是一种自然的副产品,考虑到我们所知道的来自它的所有坏处,我们不应该如此匆忙地开发全球可访问性。一旦程序员看到一个单例,他们似乎总是通过它的实例方法直接访问它。相反,您应该像导航其他对象一样导航到它。大多数代码甚至不应该意识到它正在处理单例(松耦合,对吗?)。如果只有一小部分代码像访问全局对象一样访问对象,那么会造成很多危害。我建议通过限制对实例函数的访问来实现它。

    单例上下文也非常重要。单例的定义特征是"只有一个",但事实是它在某种上下文/名称空间中是"只有一个"。它们通常是:每个线程、进程、IP地址或集群一个,但也可以是每个处理器、机器、语言命名空间/类加载器/任何东西、子网、Internet等。

    另一个不太常见的错误是忽视独生子女的生活方式。仅仅因为只有一个并不意味着一个独生子是一个无所不能的"一直是并且永远是",也不意味着它是普遍可取的(没有开始和结束的对象违反了代码中所有有用的假设,并且应该只在最绝望的情况下使用。

    如果你避免这些错误,单身汉仍然可以是一个皮塔,有点准备看到很多最坏的问题都得到了显著的缓解。想象一个Java SuntLon,它被显式定义为每个类加载器一次(这意味着它需要一个线程安全策略),具有定义的创建和销毁方法,以及一个生命周期,它指示何时以及如何调用它们,并且其"实例"方法具有包保护,因此它一般通过其他非全局对象访问。仍然是潜在的麻烦来源,但肯定更少的麻烦。

    遗憾的是,这并不是教你如何做单身汉的好例子。我们教坏的例子,让程序员先用一下,然后告诉他们他们是一个坏的设计模式。


    参见维基百科的singleton_模式

    It is also considered an anti-pattern by some people, who feel that it is overly used, introducing unnecessary limitations in situations where a sole instance of a class is not actually required.[1][2][3][4]

    参考文献(仅本文中的相关参考文献)

  • Alex Miller。我讨厌的模式1:singleton,2007年7月
  • ^斯科特·登斯莫尔。为什么单身汉是邪恶的,2004年5月
  • Steve Yegge。2004年9月,单身汉被认为是愚蠢的。
  • ^ J.B.Rainsberger,IBM公司。2001年7月,明智地使用你的单件衣服

  • 单实例不是单实例!

    与其他答案不同的是,我不想谈论单身汉的问题,而是想向你展示他们在正确使用时是多么强大和令人敬畏!

    • 问题:在多线程环境中,单例可能是一个挑战解决方案:使用一个单线程引导进程来初始化单实例的所有依赖项。
    • 问题:很难模仿单身汉。解决方案:使用方法工厂模式进行模拟MyModel myModel = Factory.inject(MyModel.class);
      你可以把MyModel映射到继承它的TestMyModel类,当MyModel被注入时,你会得到TestMyModel指令。
    • 问题:单子可以导致记忆韭菜,因为他们从来没有解决。解决办法:好吧,把它们处理掉!在应用程序中实现回调以正确地处理单例,您应该删除与它们链接的任何数据,最后:从工厂中删除它们。

    正如我在标题中所说,单例并不是单例的。

    • singleton提高了可读性:你可以查看你的类,看看它注入了什么singleton来找出它的依赖关系。
    • singleton改进了维护:一旦你从一个类中删除了一个依赖项,你就不需要去编辑其他类的一个大链接,这些类只是转移了你的依赖项(这是我的臭味代码@jim burger)
    • singleton提高了内存和性能:当应用程序中发生某些事情,并且需要一长串回调才能实现时,您将浪费内存和性能,通过使用singleton,您将减少中间人,并提高性能和内存使用率(通过避免不必要的局部变量分配)。


    这并不是说单身汉本身就不好,而是GOF的设计模式。唯一有效的论点是,GOF设计模式不适合用于测试,尤其是在并行运行测试的情况下。

    只要在代码中应用以下方法,使用类的单个实例就是有效的构造:

  • 确保将用作singleton的类实现接口。这允许使用相同的接口实现存根或模拟

  • 确保singleton是线程安全的。这是给定的。

  • 单身汉的性格应该简单,而不是过于复杂。

  • 在应用程序的运行时,如果需要将单例传递给给定的对象,请使用一个类工厂来构建该对象,并让类工厂将单例实例传递给需要它的类。

  • 在测试和确保确定性行为的过程中,将singleton类创建为单独的实例,作为实际类本身或实现其行为的存根/模拟,并按原样传递给需要它的类。不要使用在测试期间需要单例的创建被测试对象的类因子,因为它将通过它的单个全局实例,这会破坏目标。

  • 我们在解决方案中使用了单例,取得了巨大的成功,这些成功是可测试的,可以确保并行测试运行流中的确定性行为。


    我想在接受的答案中说明4点,希望有人能解释我为什么错了。

  • 为什么在代码中隐藏依赖项很糟糕?已经有几十个隐藏的依赖项(C运行时调用、OS API调用、全局函数调用)和单例依赖项很容易找到(search for instance())。

    "使某个东西全局化以避免传递它是一种代码味道。"为什么不传递某个东西以避免使它单件化为一种代码味道呢?

    如果您通过调用堆栈中的10个函数传递一个对象,只是为了避免一个单独的函数,这是不是太好了?

  • 单一责任原则:我认为这有点含糊,这取决于你对责任的定义。一个相关的问题是,为什么要将这种特定的"责任"添加到类中?

  • 为什么将一个对象传递给一个类会使它比从类内将该对象作为一个单例使用更紧密地耦合?

  • 为什么它会改变状态持续的时间?可以手动创建或销毁singleton,因此控件仍然存在,您可以使生存期与非singleton对象的生存期相同。

  • 关于单元测试:

    • 不是所有的课程都需要单元测试
    • 不是所有需要成为单元的类测试需要更改单件的实现
    • 如果它们确实需要单元测试,确实需要改变实施,很容易把一门课从用一个单件singleton通过依赖关系传递给它注射。


    文斯休斯顿有这些标准,我觉得这是合理的:

    Singleton should be considered only if all three of the following criteria are satisfied:

    • Ownership of the single instance cannot be reasonably assigned
    • Lazy initialization is desirable
    • Global access is not otherwise provided for

    If ownership of the single instance, when and how initialization occurs, and global access are not issues, Singleton is not sufficiently interesting.


    我不想对善恶之争发表评论,但自从春天到来后,我就没有用过它们。使用依赖注入几乎消除了我对单例、服务定位器和工厂的需求。我发现这是一个更高效和清洁的环境,至少对于我所做的工作(基于Java的Web应用程序)来说。


    模式本身没有任何问题,假设它被用于模型的某个方面,而这个方面是真正的单一的。

    我认为这种反冲是由于它的过度使用,而这又是因为它是最容易理解和实现的模式。


    从纯粹主义的观点来看,单身是不好的。

    从实践的角度来看,单例开发是一个权衡开发时间与复杂性的过程。

    如果你知道你的申请不会改变那么多,他们是相当不错的选择。只需知道,如果您的需求以一种意外的方式发生变化(在大多数情况下,这是非常正常的),您可能需要对事物进行重构。

    单例测试有时也会使单元测试复杂化。


    singleton是一种模式,可以像其他任何工具一样使用或滥用。

    单例程序的坏部分通常是用户(或者我应该说单例程序不适合于它设计的用途)。最大的违规者是使用一个单例作为假全局变量。


    当您使用单例(比如日志记录器或数据库连接)编写代码,然后发现需要多个日志或多个数据库时,就会遇到麻烦。

    单子使它们很难从普通物体移动到普通物体上。

    另外,编写一个非线程安全的单例也太容易了。

    您应该将所有需要的实用程序对象从一个函数传递给另一个函数,而不是使用单例。如果您将它们全部包装成一个助手对象,这可以简化,如下所示:

    1
    2
    3
    4
    5
    void some_class::some_function(parameters, service_provider& srv)
    {
        srv.get<error_logger>().log("Hi there!");
        this->another_function(some_other_parameters, srv);
    }


    单例的问题是范围的增加,因此是耦合的问题。不可否认,在某些情况下,您确实需要访问单个实例,并且可以通过其他方式完成。

    我现在更喜欢围绕控制反转(IOC)容器进行设计,并允许容器控制生命周期。这使得依赖实例的类受益于不知道存在单个实例这一事实。单身汉的一生可以在未来改变。我最近遇到过一个这样的例子,它很容易从单线程调整到多线程。

    fwiw,如果你尝试单元测试它时它是一个pia,那么当你尝试调试、修复或增强它时,它会变成pia。


    克里斯·赖斯最近在《没有评论的编码》杂志上发表了一篇关于这个主题的文章。

    注意:没有注释的编码不再有效。但是,链接到的文章已被另一个用户克隆。

    http://geekswithblogs.net/angeleyes/archive/2013/09/08/singleton-i-love-you-but-you re-bring-me-down-re-uploaded.aspx


    单身也不错。只有当你使某个全球独一无二的东西不是全球独一无二的时候,这才是不好的。

    但是,有"应用程序范围服务"(考虑一个使组件交互的消息传递系统)-这需要一个singleton、一个"messagequeue"-类,它有一个方法"sendmessage(…)"。

    然后,您可以在各地执行以下操作:

    messagequeue.current.sendmessage(新的mailarriedMessage(…);

    当然,也要做到:

    messagequeue.current.registerreceiver(this);

    在实现IMessageReceiver的类中。


    还有一件关于单件的事没人说。

    在大多数情况下,"单一性"是一些类的实现细节,而不是其接口的特性。控件容器的反转可以隐藏类用户的特性;只需要将类标记为单体(例如,用Java中的EDCOX1×0注释),就可以了;IOCC将完成其余的操作。您不需要提供对singleton实例的全局访问,因为该访问已经由iocc管理。因此,国际奥委会单打没有什么问题。

    与ioc singletons相反的gof singletons应该通过getInstance()方法在接口中公开"singletity",这样它们就会受到上面所说的一切的影响。


    太多人将不安全线程的对象放在单例模式中。我见过以单例模式完成的DataContext(Linq-to-SQL)的示例,尽管事实上,DataContext不是线程安全的,只是工作对象的一个单元。


    如果你使用得当且最少的话,单身并不是坏事。还有许多其他优秀的设计模式可以在某种程度上取代单例的需求(同时也提供了最佳的结果)。但是一些程序员不知道这些好的模式,并且在所有的情况下都使用了singleton,这使得singleton对他们有害。


    首先,一个阶级及其合作者应该首先实现他们的目标,而不是专注于义务人。生命周期管理(当实例超出范围时创建SND)不应是CLADSES职责的一部分。对此,公认的最佳实践是使用依赖注入来创建或配置一个新组件来管理依赖项。

    通常,软件会变得更复杂,让"singleton"类的多个独立实例具有不同的状态是有意义的。在这种情况下,提交代码来简单地获取单例是错误的。对于小型的简单系统来说,使用singleton.getInstance()可能是可以的,但在需要同一类的不同实例时,它不能工作/伸缩。

    任何类都不应该被认为是单例类,而应该是它的用法或如何使用它来配置从属项的应用。对于快速和讨厌的情况,这并不重要——只是Luke硬编码说文件路径并不重要,但是对于更大的应用程序,这些依赖性需要分解出来,并使用DI以更合适的方式进行管理。

    单例在测试中导致的问题是它们的硬编码单例/环境的症状。测试套件和许多测试都是各自独立的,它们分别是与硬编码单例不兼容的东西。


    因为它们基本上是面向对象的全局变量,所以您通常可以以这样的方式设计类,这样您就不需要它们了。


    当几个人(或团队)到达相似或相同的解决方案时,就会出现一种模式。很多人仍然使用单一形式的原件或使用工厂模板(Alexandrescu的现代C++设计中的良好讨论)。并发性和管理对象生命周期的困难是主要的障碍,前者按照您的建议容易管理。

    像所有的选择一样,单身汉也有相当的起伏。我认为它们可以适度地使用,特别是对于在应用程序寿命期内生存的对象。事实上,它们类似于(也可能是)地球人,这可能引发了纯粹主义者。


    这就是我认为到目前为止答案中所缺少的:

    如果每个进程地址空间需要这个对象的一个实例(并且您尽可能确信这个需求不会改变),那么您应该将它设置为单例。

    否则,就不是单身了。

    这是一个非常奇怪的要求,用户几乎从未感兴趣。进程和地址空间隔离是一个实现细节。它们只在希望停止使用kill或任务管理器的应用程序时对用户产生影响。

    除了构建一个缓存系统之外,没有太多的理由可以让您如此确定每个进程应该只有某个实例。测井系统怎么样?对于每个线程或更细粒度的线程来说,这样做可能更好,这样您就可以更自动地跟踪消息的来源。应用程序的主窗口怎么样?这取决于;也许出于某种原因,您希望所有用户的文档都由同一进程管理,在这种情况下,该进程中会有多个"主窗口"。


    单打&ndash;反模式!Mark Radford(超载期刊57&ndash;2003年10月)很好地解释了为什么单件被视为反模式。本文还讨论了两种替代单例的设计方法。


    作者的一些反对意见:

    如果你将来要让这门课不再单身,你会陷入困境。一点也不-在这种情况下,我只有一个数据库连接单例,我想把它变成一个连接池。请记住,通过标准方法访问每个单例:

    myclass.instance实例

    这类似于工厂方法的签名。我所做的只是更新实例方法以从池返回下一个连接-不需要其他更改。如果我们不使用单件的话,那就更难了。

    单身汉只是一个花哨的世界不能反驳,但是所有的静态字段和方法也是如此——从类而不是实例访问的任何东西本质上都是全局的,我看不出有那么多关于静态字段的使用的压力?

    不是说单身是好的,只是在这里推回一些"传统智慧"。


    它模糊了关注的分离。

    假设您有一个单例,您可以从类内的任何地方调用这个实例。你的课不再像原来那样纯洁了。您的类现在将不再对其成员和显式接收的成员进行操作。这会造成混乱,因为类的用户不知道类需要什么样的足够信息。封装的全部思想是向用户隐藏方法的方式,但是如果在方法中使用了单例,则必须知道单例的状态才能正确使用该方法。这是反OOP。


    在我的头顶上:

  • 它们强制紧密耦合。如果您的singleton驻留在与其用户不同的程序集上,则使用程序集在没有包含singleton的程序集的情况下无法运行。
  • 它们允许循环依赖,例如,程序集A可以具有依赖程序集B的单例,程序集B可以使用程序集A的单例。所有这些都不会破坏编译器。