当前位置:  开发笔记 > 编程语言 > 正文

单身人士有什么不好的?

如何解决《单身人士有什么不好的?》经验,为你挑选了28个好方法。

该单例模式是一个缴足成员四人帮的模式书,但最近似乎而是由开发者世界孤立.我仍然使用相当多的单例,特别是对于工厂类,虽然你必须对多线程问题(实际上是任何类)有点小心,但我不明白为什么它们如此可怕.

Stack Overflow特别假设每个人都同意Singletons是邪恶的.为什么?

请用" 事实,参考或特定专业知识 " 支持您的答案



1> Jim Burger..:

从Brian Button转述:

    它们通常用作全局实例,为什么这么糟糕?因为您在代码中隐藏了应用程序的依赖关系,而不是通过接口公开它们.制作一些全局的东西以避免传递它是一种代码味道.

    他们违反了单一责任原则:由于他们控制着自己的创造和生命周期.

    它们固有地导致代码紧密耦合.在许多情况下,这使得将它们伪装在测试中相当困难.

    它们在应用程序的生命周期中处于状态.测试的另一个打击因为你可能最终会遇到需要订购测试的情况,这对于单元测试来说是一个很大的问题.为什么?因为每个单元测试应该独立于另一个.


恕我不能赞同.由于评论只允许600个字符,我写了一篇博客帖子对此发表评论,请看下面的链接.http://jorudolph.wordpress.com/2009/11/22/singleton-considerations/
在第1点和第4点,我认为单例是有用的,实际上几乎完美的缓存数据(特别是来自数据库).性能的提高远远超过了建​​模单元测试所涉及的复杂性.
@Dai Bok:"缓存数据(特别是来自数据库)"使用代理模式来执行此操作...
哇,很好的回应.我可能在这里使用了过于激进的措辞,所以请记住我正在回答一个负面的问题.我的回答是一个简短的列表,列出了因单独使用不良而产生的"反模式".全面披露; 我也经常使用Singletons.关于SO的问题还有更多中立的问题,这些问题可以成为单身人士被认为是个好主意的绝佳论坛.例如,http://stackoverflow.com/questions/228164/on-design-patterns-when-to-use-the-singleton
它们在多线程环境中的缓存效果不是很好.您可以通过多个线程在有限的资源上进行战斗来轻松击败缓存.[由单身人士维持]
对单身人士的不良意见是OO的结果.静态/全局数据仍然有它的位置,只是没有包含在metanonsense中.
在某种意义上,DI容器是单件.单身人士单身人士.那也不好吗?他们也很难测试.
我不同意这四点.在某些方面可能是单身人士是一个糟糕的选择.但这四点并不是实际原因.我们可以用单身模式本身轻松修复这些事情(4分).
加一,但第二点是不正确的.类中的构造函数控制对象的创建,并且它们不被认为是坏的.

2> S.Lott..:

单身人士解决了一个(也是唯一一个)问题.

资源争用.

如果你有一些资源

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

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

你需要一个单身人士.

没有很多例子.日志文件是最重要的.您不想只放弃单个日志文件.您想要正确刷新,同步和关闭它.这是必须管理的单个共享资源的示例.

你很少需要一个单身人士.他们不好的原因是他们觉得自己是全球性的,他们是GoF 设计模式书的全额付费会员.

当你认为自己需要全局时,你可能会犯一个可怕的设计错误.


完全同意.有很多坚定的"这种做法很糟糕"的谈话在没有任何认识到这种做法可能取而代之的情况下浮出水面.通常,这种做法只是"糟糕",因为它经常被误用.Singleton模式没有任何内在错误,只要它被适当地应用.
真正的,原则上的单例非常罕见(打印机队列当然不是一个,也不是日志文件 - 请参阅log4j).通常情况下,硬件是巧合而不是原则的单身.连接到PC的所有硬件充其量只是巧合的单件(想想多个显示器,鼠标,打印机,声卡).即使是一个500万美元的粒子探测器也是巧合和预算约束的单例 - 它们不适用于软件,因此:没有单例.在电信领域,电话号码和手机都不是单身人士(想想ISDN,呼叫中心).
硬件也是一个例子吗?嵌入式系统有很多可能使用单例的硬件 - 或者可能是一个大单元?<笑容>
硬件可能只有一个各种资源的实例,但硬件不是软件.没有理由将开发板上的单个串行端口建模为Singleton.实际上,对其进行建模只会使端口更难以移植到具有两个端口的新电路板上!
所以你要写两个相同的类,复制很多代码,这样你就可以有两个代表两个端口的单例?如何使用两个全局SerialPort对象?如何不将硬件建模为类?为什么你会使用单身,这也意味着全球访问?您是否希望代码中的*every*函数能够访问串行端口?
@ S.Lott我不同意_need_部分.单例是该问题的解决方案,但不是该问题的唯一解决方案,因为单例很容易导致可测试性和耦合问题,我认为即使对于它旨在解决的问题,它也是一个糟糕的解决方案.最多_can use_但没有_need_
@Rune FS:我认为你可能会混淆因果关系,必要和充分.单身设计不等同于"只有一个实例可以存在".单身人士并不总是遵循"只有一个实例可以存在",因为 - 正如你所说 - 可能有其他选择.但是,对于Singleton设计来说,"只有一个实例可以存在"是必需的*,因为Singleton不可能存在其他目的."只有一个实例可以存在"对于Singleton设计来说不够*,因为有其他选择.
强制执行一次只能存在一个实例的规则与_needing_ a singlton不同.大多数DI框架支持单一创建,它为您提供可测试性的所有优点并强制实施一个实例.
@JoeBlow我认为单身人士的定义正在变形.虽然所有这些事情可能*通常*仅由单个类实例表示,但我认为这不足以有资格成为单身人士.如果他们真的是单身,那么我们的手机上永远不会有多个屏幕,相机等,除非他们添加类似Screen2或Camera2或类似的东西,然后需要开发人员明确地称这些单身人士.但是之前我还没有在手机上进行编程......他们真的是这样做的吗?
两个串口违反了**Singleton**原则.现在有尽可能多的实例,你有硬件,这不是**Singleton**.它只是一个绑定硬件的类.
@ S.Lott:那么让我非常具体,因为我似乎不清楚我的原始评论可能是:"如果你有一些资源(1)只能有一个实例,(2)你需要要管理这个单一实例,你需要一个Singleton." 是一个错误,因为你可以解决单个问题的问题,所以没有_need_为单身人士,因为你把它
没有必要,因为你没有必要,所以没有必要使用单身人士.从来没有,但很多开发人员似乎认为他们需要一个单身实际上他们没有.我强调的是,他们既不需要也不需要nesecarry
这个答案是完全正确的,直到它说...**罕见**...罕见,WTF?!这个星球上有大约20亿(我相信)ios和Android手机.所有设备编程 - 整个设备编程 - 只不过是单身人士.(正是在这个答案中完全解释的原因.)我的意思是**应用程序是单身**,为了善良,在iOS(还有什么可能?)**每个服务**(像gps,时间,"手机上的屏幕"等等"是单身人士.我猜还有一些主机fortran工作,但"罕见"?!

3> Phil Wright..:

一些编码势利者瞧不起它们只是一个美化的全球化.就像许多人讨厌goto声明一样,还有其他人讨厌使用全局的想法.我见过几个开发人员为了避免全球化而竭尽全力,因为他们考虑使用一个作为承认失败.奇怪但真实.

在实践中,Singleton模式只是一种编程技术,是您的概念工具包的有用部分.您可能会不时发现它是理想的解决方案,因此请使用它.但是使用它只是为了让你夸耀使用设计模式就像拒绝使用它一样愚蠢,因为它只是一个全球性的.


@Pacerier,只要满足以下所有条件:(1)你只需要一个东西,(2)你需要在大量方法调用中将那一个东西作为参数传递,(3)你愿意接受一个有一天不得不重构的机会,以换取立即减少代码的大小和复杂性,而不是在任何地方传递令人不快的东西.
"我们"瞧不起他们的原因是"我们"经常看到他们使用错误,而且经常使用."我们"确实知道他们有自己的理由.
我在Singleton中看到的失败是人们使用它们而不是全局因为它们在某种程度上"更好".问题是(正如我所看到的),在这些情况下,Singleton带来的东西是无关紧要的.(例如,构造在第一次使用时非常容易在非单例中实现,即使它实际上并没有使用构造函数来执行它.)
我觉得这没有任何答案,它只是说'有时它可能适合,有时可能不适合'.好的,但为什么,什么时候?是什么让这个答案多于[适度论证](http://en.wikipedia.org/wiki/Argument_to_moderation)?
@Phil,你说"你可能会不时发现它是理想的解决方案,所以使用它".好的,确切地说***我们会发现单例有用吗?

4> jason..:

来自谷歌的Misko Hevery有一些关于这个主题的有趣文章......

单身人士是Pathological Liars有一个单元测试示例,说明了单身人士如何难以找出依赖链并开始或测试应用程序.这是滥用的一个相当极端的例子,但他提出的观点仍然有效:

单身人士只不过是全球化的国家.全局状态使得您的对象可以秘密地获取未在其API中声明的内容,因此,单身人士会将您的API变成病态的骗子.

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


第一个链接实际上并没有解决单例的问题,而是在类中假设一个静态依赖.可以通过传入参数来修复给定的示例,但仍然使用单例.
@DGM:确切地说 - 事实上,文章的"基本原理"部分与"单身人士是原因"部分之间存在巨大的逻辑脱节.
Misko关于这个主题的文章是目前最好的文章.
阅读第二篇文章单身实际上是所描述的对象模型的一部分.但是,它们不是全局可访问的,而是Factory对象的私有.

5> Cem Catikkas..:

我认为混淆是由于人们不知道Singleton模式的真实应用.我不能强调这一点.Singleton 不是一个包装全局变量的模式.单例模式应该仅用于保证在运行时期间存在给定类的一个且仅一个实例.

人们认为Singleton是邪恶的,因为他们将它用于全局变量.正是由于这种混乱,单身人士被人瞧不起.请不要混淆Singletons和全局.如果用于它的目的,您将从Singleton模式中获得极大的好处.


当然,您可以在应用程序启动时自由创建该类的一个实例,并通过接口将该实例注入到使用它的任何内容中.实施不应该只关心一个.
什么是一个实例,可在整个应用程序中使用?哦.*全球*."Singleton不是**用于包装全局变量的模式"或者是天真的或误导性的,因为*根据定义*,模式围绕全局实例包装类.
@Dainius:最后真的没有.当然,你不能随意用另一个替换实例.(除了那些引人注目的时刻*你做*.我之前实际上已经看过`setInstance`方法了.)尽管如此,那个"需要"单身的weenie也不知道一个怪异的事情.封装或可变全局状态有什么问题,所以他帮助(?)为每一个提供了setter.单.领域.(是的,这种情况发生了.A*很多*.几乎我在野外见过的每一个单身都是可变的设计,而且常常令人尴尬.)
@Dainius:在很大程度上,我们已经拥有了."优先考虑构成而不是继承"已经有一段时间了.但是,当继承*是*可证明是最佳解决方案时,您当然可以自由使用它.单身,全局,线程,'goto`等同样的事情.在许多情况下他们可能*工作*但坦率地说,"工作"是不够的 - 如果你想违背传统智慧,你最好能够演示您的方法比传统解决方案更好*.我还没有看到Singleton模式的这种情况.
为了避免我们互相交谈,我不只是谈论全球可用的实例.有很多案例.我所说的(特别是当我说"capital-S Singleton")是GoF的Singleton模式,它将单个全局实例嵌入到类本身中,通过`getInstance`或类似命名的方法公开它,并防止存在二次实例.坦率地说,那时你甚至可能没有*one*实例.

6> Mike Stone..:

关于单身人士的一个相当不好的事情是你不能很容易地扩展它们.如果你想改变他们的行为,你基本上必须建立某种装饰模式或某些东西.另外,如果有一天你想要有多种方法来做这件事,那么改变可能会非常痛苦,这取决于你如何布置你的代码.

有一点需要注意,如果你使用单身人士,试着把它们传递给任何需要它们的人,而不是让他们直接访问它......否则,如果你选择有多种方式来完成单身人士所做的事情,那么它将是因为每个类在直接访问单例时嵌入依赖项,所以很难改变.

所以基本上:

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

而不是:

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

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

像任何模式一样......考虑一下并考虑它在给定情况下的使用是否不合适......规则通常会被破坏,并且不应该毫无思想地应用模式.


嘿,如果你到处都这样做,那么你也必须在各处传递对singelton的引用,因此你不再有单例.:)(这通常是IMO的一件好事.)
@ M.Mimpen:我的评论更多地引用了语义效应.一旦你取消对`getInstance()`的调用,你就有效地抛弃了Singleton模式和普通引用之间的一个有用的区别.就其余代码而言,单一性不再是属性.只有`getInstance()`的调用者才需要知道甚至关心有多少个实例.如果只有一个调用者,那么为了可靠地强制执行单一性而不是让调用者只存储引用并重用它,它在成本和灵活性方面的成本更高.
@ BjarkeFreund-Hansen-废话,你在说什么?单例只是一个实例化一次的类的实例。引用这样的Singleton并不会复制实际的对象,它只是引用它-您仍然拥有相同的对象(阅读:Singleton)。
@ M.Mimpen:不,资本-S Singleton(这里讨论的是)一个类的实例,(a)*保证*只有一个实例存在,并且(b)通过class自己内置的全局访问点.如果你声明什么都不应该调用`getInstance()`,那么(b)就不再那么真了.
@cHao我没有关注您,或者您不了解我向谁发表评论-这是Bjarke Freund-Hansen的。Bjarke指出,具有多个单例引用会导致具有多个单例。这肯定是不正确的,因为没有深层副本。

7> Kimoz..:

单身模式本身不是问题.问题在于,人们经常使用这种模式来开发具有面向对象工具的软件,而没有扎实地掌握OO概念.当在这种情况下引入单例时,它们往往会成长为无法管理的类,每个小用途都包含辅助方法.

从测试的角度来看,单身人士也是一个问题.他们倾向于使孤立的单元测试难以编写.控制反转(IoC)和依赖注入是一种模式,旨在以面向对象的方式克服这个问题,这有助于单元测试.

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

还存在多线程场景,其中单例可能成为瓶颈以及同步问题.


我知道这是多年的线程.嗨@Kimoz你说: - 单身人士很快就会成为内存管理方面的问题.我想更详细地解释一下单身和垃圾收集会出现什么样的问题.

8> jwanagel..:

使用静态方法实现单例.进行单元测试的人员可以避免静态方法,因为他们不能被模拟或存根.该网站上的大多数人都是单元测试的重要支持者.通常最接受的避免它们的惯例是使用控制模式的反转.


这听起来更像是可以测试对象(单元),函数(单元),整个库(单元)的单元测试的问题,但是在类(也是单元)中有任何静态的失败.
你不是想要所有外部参考吗?如果你是,那么moc singleton有什么问题,如果没有,你真的在​​做单元测试吗?

9> Uooo..:

集群方面,单身人士也很糟糕.因为那样,你的应用程序中就没有"完全一个单独"了.

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

public class SingletonDao {
    // songleton's static variable and getInstance() method etc. omitted
    public void writeXYZ(...){
        synchronized(...){
            // some database writing operations...
        }
    }
}

因此,您确定应用程序中只存在一个单例,并且所有数据库都只通过此单例SingletonDao.您的生产环境现在看起来像这样: 单身单身人士

到目前为止一切都很好.

现在,考虑您要在群集中设置Web应用程序的多个实例.现在,你突然间有这样的事情:

很多单身人士

这听起来很奇怪,但现在你的申请中有很多单身人士.而这正是单身人士不应该做的事情:有很多对象.如果您(如此示例中所示)想要对数据库进行同步调用,则这尤其糟糕.

当然,这是单身人士使用不当的一个例子.但是这个例子的信息是:你不能依赖应用程序中只有一个单例实例 - 特别是在集群方面.


如果您不知道如何实现单例,则不应该这样做.如果你不知道自己在做什么,你应该先找到它,然后才能找到你需要的东西.
这很有趣,我有一个问题。那么,如果单例(每个都位于不同的机器/ JVM上)正在连接到单个数据库,那到底是什么问题呢?Singletons范围仅适用于该特定的JVM,即使在集群中也是如此。不仅仅是从哲学上说这种特殊情况很糟糕,因为我们的意图是整个应用程序中的单个对象,我很高兴看到由于这种安排而可能出现的任何技术问题。

10> jop..:

    它很容易(ab)用作全局变量.

    依赖于单例的类相对更难以单独进行单元测试.



11> Evan Plaice..:

垄断是魔鬼,而非只读/可变状态的单身人士是"真正的"问题......

看完后单身人士病态说谎者在建议贾森的回答我碰到这个小珍闻,提供最佳的呈现例子就是如何单身常常被误用.

全球是坏的,因为:

一个.它导致命名空间冲突

湾 它以无根据的方式暴露了国家

说到单身人士

一个.调用它们的显式OO方式可以防止冲突,所以指向a.不是问题

湾 没有国家的单身人士(像工厂)不是问题.具有状态的单身人士可以再次分为两类,一类是不可变的或一次写入并且读取很多(配置/属性文件).这些都不错.作为参考持有者的可变单身人士就是你所说的那些人.

在最后一个声明中,他指的是博客的"单身人士是骗子"的概念.

这如何适用于垄断?

要开始一场垄断游戏,首先:

我们首先建立规则,以便每个人都在同一页面上

在比赛开始时,每个人都有一个平等的开始

只提出一套规则以避免混淆

规则不允许在整个游戏中改变

现在,对于任何没有真正垄断的人来说,这些标准至多是理想的.垄断的失败是难以接受的,因为垄断是关于金钱的,如果你输了,你必须煞费苦心地观察剩下的球员完成比赛,而且损失通常是迅速和惨淡的.因此,规则通常会在某些时候扭曲,以牺牲其他人的利益为一些球员的自身利益服务.

所以你和朋友Bob,Joe和Ed一起垄断.您正迅速建立自己的帝国,并以指数的速度消耗市场份额.你的对手正在减弱,你开始嗅到血(比喻).你的好友鲍勃把他所有的钱都投入到尽可能多的低价值房产中,但他没有像他预期的那样获得高额投资回报.鲍勃,作为一个运气不好的中风,落在你的木板路上,并从游戏中被切除.

现在游戏从友好的骰子滚动到严肃的事业.鲍勃已经成为失败的榜样,乔和埃德不想最终像'那个家伙'.所以,作为领先的玩家,你突然变成了敌人.乔和埃德开始练习桌下交易,背后的钱注射,低估的房屋交换以及一般会削弱你作为一名球员的任何东西,直到其中一人升到顶峰.

然后,不是其中一个获胜,而是从头开始.突然之间,一组有限的规则成为一个移动的目标,游戏退化为社交互动的类型,构成了自幸存者以来每一个高评级真人秀节目的基础.为什么,因为规则正在发生变化,并且没有就如何/为什么/它们应该代表什么达成共识,更重要的是,没有人做出决定.在那一点上,游戏中的每个玩家都在制定他/她自己的规则并且随后发生混乱,直到两个玩家太累而无法跟上游戏并慢慢放弃.

因此,如果游戏的规则手册准确地代表了单身人士,那么垄断规则手册将成为滥用的一个例子.

这如何适用于编程?

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

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

单身是唯一的选择,如果你需要什么单提供.对象的write-one只读实例.同样的规则也应该级联到对象的属性/成员.



12> Ilya Gazman..:

Singleton不是关于单个实例!

与其他答案不同,我不想谈谈单身人士出了什么问题,而是向你展示他们使用时的强大和真棒!

问题:Singleton在多线程环境中可能是一个挑战
解决方案:使用单线程引导过程来初始化单例的所有依赖关系.

问题:很难模仿单身人士.
解决方案:使用方法工厂模式进行模拟
MyModel 您可以映射TestMyModelMyModel继承它的类,无论何时TestMyModel注入,您都将获得MyModelinstread.

问题:单身人士可能因为他们从未处理过而导致记忆韭菜.
解决方案:好吧,处理它们!在您的应用程序中实现回调以正确处理单例,您应该删除链接到它们的任何数据,最后:从工厂中删除它们.

正如我在标题中所述,单身人士不是单身实例.

单身人士提高了可读性:你可以看看你的课程,看看它注入了什么单身人士来弄清楚它的依赖性.

单身人士改进了维护:一旦你从一个类中删除了一个你只删除了一些单例注入的依赖,你就不需要去编辑其他类的大链接了,这些类只是移动了你的依赖(这是我的臭代码@Jim Burger)

单身人士提高了内存和性能:当你的应用程序发生某些事情,并且需要一长串回调来传递时,你就会浪费内存和性能,通过使用Singleton你正在削减中间人,并提高你的性能和内存使用率(通过避免不必要的局部变量分配).


那就是目的......
这并没有解决我对Singletons的主要问题,即它们允许从项目中的数千个类中的任何一个访问全局状态.

13> Christopher ..:

关于单身人士如何做坏的答案总是"他们很难做对".语言的许多基础组件都是单例(类,函数,命名空间甚至运算符),计算的其他方面(localhost,默认路由,虚拟文件​​系统等)中的组件也是如此,并且不是偶然的.虽然他们不时会引起麻烦和挫折,但他们也可以使很多事情变得更好.

我看到的两个最大的问题是:将它视为全局并且无法定义Singleton闭包.

每个人都把单身人士称为全局,因为他们基本上都是.然而,全球范围内的许多(可悲的是,并非全部)不良并非本质上来自于全球性,而是如何使用它.单身人士也是如此.实际上更多,因为"单一实例"实际上并不需要意味着"全球可访问".它更像是一种天然的副产品,考虑到我们所知道的所有坏事,我们不应该急于利用全球可访问性.一旦程序员看到Singleton,他们似乎总是通过其实例方法直接访问它.相反,您应该像对待任何其他对象一样导航到它.大多数代码甚至不应该意识到它正在处理Singleton(松散耦合,对吧?).如果只有一小部分代码访问对象,就像它是全局的一样,那么很多伤害都会被撤消.我建议通过限制对实例函数的访问来强制执行它.

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

另一个不太常见的错误是忽略了Singleton的生活方式.仅仅因为只有一个并不意味着Singleton是一些无所不能的"永远是并且永远都会",也不是一般所希望的(没有开头和结尾的对象违反了代码中的各种有用的假设,并且只应该被使用在最绝望的情况下.

如果你避免这些错误,Singletons仍然可以成为一个PITA,它已经准备好看到很多最糟糕的问题都会大大减轻.想象一下Java Singleton,它被明确定义为每个类加载器一次(这意味着它需要一个线程安全策略),定义的创建和销毁方法以及指示何时以及如何调用它们的生命周期,以及其"实例"方法具有的包保护,因此通常通过其他非全局对象访问.仍然是潜在的麻烦来源,但肯定要少得多麻烦.

可悲的是,而不是教导如何做单身人士的好例子.我们教导不好的例子,让程序员暂时使用它们,然后告诉他们这是一个糟糕的设计模式.



14> GUI Junkie..:

请参阅Wikipedia Singleton_pattern

它也被一些人认为是一种反模式,他们认为它被过度使用,在实际上不需要类的唯一实例的情况下引入了不必要的限制.[1] [2] [3] [4]

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

    ^亚历克斯米勒.模式我讨厌#1:Singleton,2007年7月

    ^斯科特Densmore.为什么单身人士是邪恶的,2004年5月

    ^ Steve Yegge.2004年9月,单身人士认为这是愚蠢的

    ^ JB Rainsberger,IBM.2001年7月明智地使用你的单身人士


关于模式的描述并不能解释为什么它被认为是邪恶的......
几乎没有公平的说法:"有些人认为它过度使用,在实际上并不需要一个类的唯一实例的情况下引入了不必要的限制." 看看参考资料......无论如何,我有RTFM.

15> 小智..:

不是单身人士本身不好,而是GoF的设计模式.唯一真正有效的论点是GoF设计模式在测试方面不适用,特别是如果测试是并行运行的话.

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

    确保将用作单例的类实现接口.这允许使用相同的接口实现存根或模拟

    确保Singleton是线程安全的.这是给定的.

    单身人员应该是简单的,而不是过于复杂.

    在应用程序的运行时期间,需要将单例传递给给定对象,使用构建该对象的类工厂并让类工厂将单例实例传递给需要它的类.

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

我们在我们的解决方案中使用了Singletons,取得了巨大成功,可以测试并行测试运行流中的确定性行为.



16> Jon..:

我想在接受的答案中提出4点,希望有人可以解释为什么我错了.

    为什么隐藏代码中的依赖项不好?已经存在许多隐藏的依赖项(C运行时调用,OS API调用,全局函数调用),并且易于查找单例依赖项(搜索instance()).

    "让一些东西变得全球化以避免传递它是一种代码味道." 为什么不传递一些东西以避免使它成为代码气味的单身?

    如果你通过调用堆栈中的10个函数传递一个对象只是为了避免单例,那么这么棒吗?

    单一责任原则:我认为这有点模糊,取决于您对责任的定义.一个相关的问题是,为什么在课堂上增加这种具体的 "责任"呢?

    为什么将一个对象传递给一个类使它比使用该对象作为一个单独的类更紧密耦合?

    为什么它会改变国家持续多久?单例可以手动创建或销毁,因此控件仍然存在,并且您可以使生命周期与非单例对象的生命周期相同.

关于单元测试:

并非所有课程都需要进行单元测试

并非所有需要进行单元测试的类都需要更改单例的实现

如果他们确实需要进行单元测试并且确实需要更改实现,那么很容易将类从使用单例改为通过依赖注入将单例传递给它.


我不确定"不能手动销毁"是"只能是一个实例"的逻辑含义.如果这就是你如何定义单身人士,那么我们不是在谈论同样的事情.所有类_不应该进行单元测试.这是一个商业决策,有时上市时间或开发成本优先.

17> js...:

Vince Huston有这些标准,这对我来说似乎是合理的:

只有满足以下所有三个标准时,才应考虑单身人士:

无法合理分配单个实例的所有权

延迟初始化是可取的

不提供全球访问权限

如果单个实例的所有权,初始化发生的时间和方式以及全局访问都不是问题,那么Singleton就不够有趣了.



18> tacone..:

从纯粹主义的观点来看,单身人士是坏人.

从实际的角度来看,单身是一种权衡发展时间与复杂性的关系.

如果你知道你的应用程序不会改变那么多,那么它们就可以了.只要知道如果你的需求以一种意想不到的方式发生变化,你可能需要重构一下(在大多数情况下这是非常好的).

单身人士有时也会使单元测试复杂化.


我的经验是,即使在短期内,开始*单身人士也会真的伤害你,不计算长远来看.如果应用程序已经存在了一段时间,并且可能已经充满了其他单例,那么这种效果可能会少一些.从头开始?不惜一切代价避免它们!想要一个对象的单个实例?让工厂管理此对象的实例化.
"工厂"!="一个工厂方法,不能与同一类中其他有用的东西分开"

19> Ben Hoffstei..:

模式没有任何内在错误,假设它被用于模型的某些方面,这是真正单一的.

我认为反弹是由于过度使用,反过来,这是由于它是最容易理解和实施的模式.


我认为这种强烈反对更多地与那些正在进行正式单元测试的人相比,他们意识到他们是一个应对的噩梦.

20> prule..:

我不打算评论好/坏的论点,但是自从Spring出现以来我没有使用它们.使用依赖注入几乎消除了我对单例,服务定位器和工厂的要求.我发现这是一个更高效,更干净的环境,至少对于我所做的工作类型(基于Java的Web应用程序).


所以当别人这样做时,它就好了(如果我之后会使用它),但是如果其他人这样做而且我不会使用它,这是邪恶的吗?
不是Spring bean,默认情况下,Singletons?
是的,但我的意思是'编码'单身人士.我没有'写一个单身'(即按照设计模式使用私人构造函数yada yada yada) - 但是,是的,春天我使用的是单个实例.

21> Martin York..:

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

单身人员的坏部分通常是用户(或者我应该说不适当地使用单身人士来做它不打算做的事情).最大的罪犯使用单身人士作为虚假的全球变量.



22> Roman Odaisk..:

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

单身人士很难从他们移动到常规物体.

此外,编写非线程安全的单例太容易了.

您应该将所有必需的实用程序对象从函数传递给函数,而不是使用单例.如果将它们全部包装到辅助对象中,可以简化这一点,如下所示:

void some_class::some_function(parameters, service_provider& srv)
{
    srv.get().log("Hi there!");
    this->another_function(some_other_parameters, srv);
}


为每种方法传递论证都很精彩,至少应该至少采取前10种方法来污染你的api.

23> David..:

最近关于这个主题的文章由Chris Reath编写,没有评论.

注意:没有注释的编码不再有效.但是,链接到的文章已被其他用户克隆.

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



24> Mark Lindell..:

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

我现在更喜欢围绕控制反转(IoC)容器进行设计,并允许容器控制生命周期.这使得依赖于实例的类的好处是不知道存在单个实例的事实.单身人士的生命周期可以在未来改变.一旦我最近遇到的这样的例子是从单线程到多线程的简单调整.

FWIW,如果它是PIA,当你尝试对它进行单元测试时,那么当你尝试调试,修复bug或增强它时,它会进入PIA.



25> StormianRoot..:

单身人士并不坏.只有当你创造出全球唯一并且不是全球唯一的东西时,这才是最糟糕的.

但是,有"应用程序范围服务"(考虑使组件交互的消息传递系统) - 这个CALLS用于单例,"MessageQueue" - 类具有方法"SendMessage(...)".

然后,您可以从所有地方执行以下操作:

MessageQueue.Current.SendMessage(new MailArrivedMessage(...));

当然,这样做:

MessageQueue.Current.RegisterReceiver(本);

在实现IMessageReceiver的类中.


如果我想创建第二个消息队列,范围较小,为什么不允许我重用您的代码来创建它?单身可以防止这种情况.但是,如果您刚刚创建了一个常规消息队列类,然后创建了一个全局实例作为"应用程序范围",我可以创建第二个实例供其他用途.但是如果你让这个类成为一个单例,我就必须编写一个*second*消息队列类.

26> Aaron Powell..:

太多人将单个模式中非线程安全的对象放置.我已经看到了以单例模式完成的DataContext(LINQ to SQL)的例子,尽管DataContext不是线程安全的,而且纯粹是一个工作单元对象.



27> Volodymyr Fr..:

关于单身人士还有一件事,没有人说过.

在大多数情况下,"单一性"是某些类的实现细节,而不是其接口的特征.控制容器的反转可以隐藏类用户的这种特征; 你只需要将你的类标记为单例(@Singleton例如在Java中使用注释),就是这样; IoCC将完成剩下的工作.您不需要提供对单例实例的全局访问权限,因为访问权限已由IoCC管理.因此,IoC Singletons没有任何问题.

与IoC Singletons相反的GoF Singletons应该通过getInstance()方法在界面中暴露"单一性",这样他们就会遭遇上述所有内容.


在我看来,"单一性"是一个运行时环境细节,编写类代码的程序员不应该考虑它.相反,它是由USER类考虑的.只有USER知道实际需要多少个实例.

28> Sriram..:

如果您正确最少使用它,那么单例并不是邪恶的。还有许多其他好的设计模式可以在某个时候替代单例的需求(并且也可以提供最佳结果)。但是一些程序员没有意识到那些好的模式,并在所有情况下都使用单例,这使单例对他们而言是邪恶的。

推荐阅读
贴进你的心聆听你的世界
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有