单身人士有什么不好?

单身模式是GoF 模式书的完全付费成员,但最近似乎被开发者世界所遗弃。 我仍然使用了很多单例,特别是对于工厂类来说 ,虽然你必须小心multithreading问题(实际上就像任何一个类),但是我不明白为什么它们太可怕了。

特别是堆栈溢出,似乎认为每个人都认为单身人士是邪恶的。 为什么?

请以“ 事实,参考或具体的专业知识 ”来支持您的答案

从Brian Button解释:

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

  2. 他们违背了单一责任原则 :凭借自己控制自己的创作和生命周期的事实。

  3. 它们固有地使代码紧密耦合 。 这在很多情况下使得它们在testing中变得相当困难。

  4. 它们在应用程序的整个生命周期内都保持状态。 另一个打击testing,因为你可以结束的情况下,testing需要订​​购,这是一个很大的unit testing不是。 为什么? 因为每个unit testing应该是独立的。

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

资源争夺。

如果你有一些资源

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

2 )你需要pipe理那个单一的实例,

你需要一个单身人士

没有太多的例子。 日志文件是最大的一个。 你不想只放弃一个日志文件。 你想刷新,同步和正确closures它。 这是必须pipe理的单个共享资源的示例。

你很less需要一个单身人士。 他们糟糕的原因是他们觉得自己像一个全球性的 ,他们是一个完全付费的GoF devise模式书的成员。

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

一些编码的势利眼看着他们只是一个荣耀的全球。 就像许多人讨厌goto声明一样,还有一些人讨厌使用全球性的想法。 我曾经见过几位开发人员为了避免一个全球性而做了大量的工作,因为他们认为使用一个来承认失败。 奇怪而真实。

在实践中, Singleton模式只是一个编程技术,是您的概念工具箱的一个有用的部分。 有时您可能会发现它是理想的解决scheme,所以使用它。 但是,使用它只是让你可以吹嘘使用devise模式是愚蠢的,拒绝使用它,因为它只是一个全球性的

来自谷歌的Misko Hevery在这个主题上有一些有趣的文章。

单身人士是病理性的说谎者有一个unit testing的例子,说明单身人士如何可以很难找出依赖链和启动或testing一个应用程序。 这是一个相当极端的虐待的例子,但他所提出的观点仍然有效:

单身人士只不过是全球的国家。 全局状态使得你的对象能够秘密地获得未在其API中声明的东西,因此,单例将你的API变成病态的骗子。

所有的Singletons Gone都指出,dependency injection使得向需要它们的构造函数获取实例变得容易,这减轻了第一篇文章中谴责的不好的全局Singleton背后的潜在需求。

我认为这个混乱是由于人们不知道Singleton模式的真正应用。 我不能强调这一点。 单例不是一个换行全局的模式。 单一模式只能用来保证在运行时存在给定类的唯一一个实例

人们认为辛格尔顿是邪恶的,因为他们正在将它用于全局。 正是由于这种混乱,辛格尔顿才被看不起。 请不要混淆单身和全局。 如果用于它的目的,您将从Singleton模式中获得极大的好处。

单身人士的一个相当不好的事情是,你不能很容易地扩大他们。 如果你想改变他们的行为,你基本上必须build立在某种装饰模式或一些这样的事情。 另外,如果有一天你想要有多种方式来做这件事情,那么根据你如何布置你的代码,改变可能是相当痛苦的。

有一件事要注意,如果你使用单例,试着将它们传递给需要它们的人,而不是让他们直接访问它。否则,如果你select有多种方式来做单件事情,它将是如果直接访问单例,每个类embedded一个依赖关系就很难改变。

所以基本上:

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

而不是:

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

我相信这种模式被称为dependency injection ,通常被认为是一件好事。

就像任何一种模式一样…考虑一下,并考虑它在特定情况下的使用是否不合适……规则通常会被打破, 模式不应该不加思索地运用。

单身模式本身不是问题。 问题在于这种模式经常被人们用面向对象的工具来开发软件,而没有牢牢把握面向对象的概念。 当在这种情况下引入单例时,它们往往会变成无法pipe理的类,每个小用途都包含辅助方法。

从testing的angular度来看,单身也是一个问题。 他们往往使孤立的unit testing很难写。 控制反转 (IoC)和dependency injection是旨在以面向对象的方式克服这个问题的模式,适用于unit testing。

在垃圾收集环境中,单身人士可能很快成为内存pipe理的一个问题。

还有multithreading场景,单身人士可能成为瓶颈以及同步问题。

使用静态方法实现一个单例。 静态方法是由单位testing的人避免,因为他们不能被嘲弄或残缺。 这个网站上的大多数人都是unit testing的大支持者。 通常最被接受的惯例是避免使用控制模式的反转 。

集群方面,单身人士也不好。 因为那样,你的应用程序中就再也没有“完全一个单身人士”。

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

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

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

一切都很好。

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

许多单身人士

这听起来很奇怪,但现在你的应用程序中有很多单身人士 。 而这正是一个单身人士所不应该做的:拥有许多对象。 如果您像本例中所示的那样想要对数据库进行同步调用,那么这是特别糟糕的。

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

  1. 它很容易(ab)用作全局variables。
  2. 依赖于单例的类相对较难孤立地进行unit testing。

垄断是非只读/可变状态的魔鬼和单身人士是“真正的”问题…

在阅读贾森的答案中提到的单身人士是病理性的说谎者之后 ,我遇到了这个小小的消息,提供了单身人士经常被滥用的最好的例子。

全球不好,因为:

  • 一个。 它导致命名空间冲突
  • 湾 它以不正当的方式暴露了国家

当涉及到单身

  • 一个。 明确的OO方式叫他们,防止冲突,所以指出一个。 不是一个问题
  • 湾 没有国家的单身人士(如工厂)不是问题。 具有状态的单身人士可以再次分为两类,一类是不可变的,一类是读取(config / property文件)。 这些都不错 可变的单身人士,这是参考持有人是你说的那些。

在最后的声明中,他指的是博客的“单身是说谎者”的概念。

这是如何适用于垄断?

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

  • 我们首先制定规则,所有人都在同一页面上
  • 在比赛开始的时候,每个人都有相同的开始
  • 只有一套规则是为了避免混淆
  • 游戏中不允许改变规则

现在,对于没有真正垄断的人来说,这些标准是最好的。 垄断的失败是难以吞噬的,因为垄断是关于金钱的,如果你输了,你不得不费力地看着其余的球员完成比赛,而损失通常是迅速和粉碎。 所以,这些规则通常会在某些时候被扭曲,以牺牲其他玩家的利益来服务于一些玩家的自身利益。

所以你和朋友Bob,Joe和Ed打了垄断。 你迅速build立你的帝国,并以指数速度消费市场份额。 你的对手正在削弱,你开始闻到血液(比喻)。 你的好友鲍勃把所有的钱都投入了尽可能多的低价值财产,但是他没有像他预期的那样获得高额投资回报。 鲍勃,作为一个坏运气的中风,登上你的木板路,并从游戏中删除。

现在游戏从友好的掷骰子转变为严肃的商业。 鲍勃已经成为失败的榜样,乔和埃德不想像“那个人”那样结束。 所以,作为主angular的你,一下子成了敌人。 乔和埃德开始练习桌下交易,背后的钱注入,低估的房子交换和一般的任何东西削弱你作为一个球员,直到其中一个上升到顶部。

然后,而不是其中一个获胜,整个过程开始。 突然之间,一套有限的规则成为一个移动的目标,游戏退化为社会互动的types,这将成为自幸存者以来每一个高评分真实电视节目的基础。 为什么呢?因为规则正在改变,对于他们如何/为什么/他们应该代表什么,没有共识,更重要的是,没有一个人做决定。 在这个比赛中,每个球员都在制定他们自己的规则和混乱,直到两名球员疲惫不堪,以至于不能继续追逐,慢慢放弃。

所以,如果一个游戏的规则手册准确地代表了一个单身人士,垄断规则手册将是一个滥用的例子。

这如何适用于编程?

除了可变单例存在的所有显而易见的线程安全和同步问题之外…如果有一组数据,它们可以被多个不同的数据源同时读取/操作,并且在应用程序执行期间存在,现在可能是回过头来问“我在这里使用正确types的数据结构”的好时机。

就我个人而言,我曾经看到一个程序员在一个应用程序中使用它作为某种扭曲的跨线程数据库存储来滥用单例。 直接使用代码,我可以certificate这是一个缓慢的(因为所有的线程锁需要使它线程安全)和一个噩梦工作(由于同步错误的不可预知性/间歇性),和几乎不可能在“生产”条件下进行testing。 当然,可以使用轮询/信令来开发系统来克服一些性能问题,但是这不会解决testing中的问题,并且为什么当“真实”数据库已经能够以更强健/可扩展的方式。

单身人士只是一个select,如果你需要什么单身人士提供。 写一个对象的只读实例。 同样的规则也应该级联到对象的属性/成员。

请参阅Wikipedia Singleton_pattern

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

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

  1. ^ Alex米勒。 我讨厌的模式#1:Singleton ,2007年7月
  2. ^ Scott Densmore。 为什么单身人士是邪恶的 ,2004年5月
  3. ^史蒂夫·叶吉。 单身人士认为愚蠢 ,2004年9月
  4. IBM JB Rainsberger,IBM。 聪明地使用你的单身人士 ,2001年7月

我对单身人士不好的回答总是,“他们很难做正确的事”。 语言的许多基本组件是单例(类,函数,命名空间,甚至操作符),还有计算的其他方面的组件(localhost,默认路由,虚拟文件​​系统等),这并非偶然。 虽然他们不时地引起麻烦和挫折,但他们也可以使许多事情更好地工作。

我所看到的两个最大的麻烦是:把它看作是一个全局性的,并且不能定义Singleton闭包。

每个人都在谈论Singleton作为全局variables,因为它们基本上是。 然而,全球范围内的许多(不幸的是,并非全部)坏事本质上并不是全球化的,而是如何使用它。 同样的单身人士。 实际上更像“单一实例”并不意味着“全球可访问”。 这更多的是一种天然的副产品,而我们所知道的一切不好的东西都是来自它的,所以我们不应该急于开拓全球的可达性。 一旦程序员看到一个单例,他们似乎总是直接通过它的实例方法来访问它。 相反,您应该像导入任何其他对象一样导航到它。 大多数代码甚至不应该意识到它正在处理一个Singleton(松耦合,对吗?)。 如果只有一小部分代码访问这个对象就像是一个全局的对象,那么很多的伤害就会被取消。 我build议通过限制对实例函数的访问来强制执行它。

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

另一个不太常见的错误就是忽视了辛格尔顿的生活方式。 因为只有一个并不意味着一个单身是一种万能的“永远永远是永远的”,也不是一般的愿望(没有开始和结束的对象违反了代码中的各种有用的假设,只应该使用在最绝望的情况下。

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

可悲的是,而不是教如何做单身的好例子。 我们教不好的例子,让程序员跑了一段时间,然后告诉他们这是一个糟糕的devise模式。

并不是说单身人士本身不好,而是GoF的devise模式。 唯一有效的论点是,GoFdevise模式并不适用于testing,尤其是在testing并行运行的情况下。

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

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

  2. 确保Singleton是线程安全的。 这是一个给定的。

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

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

  5. 在testing过程中,为了确保确定性的行为,创build单独的类作为单独的实例作为实际的类本身或者实现其行为的存根/模拟,并将其作为原样传递给需要它的类。 在testing过程中,不要使用创build那个需要单例testing的对象的类因子,因为它会通过它的单个全局实例,从而破坏了目的。

我们已经在我们的解决scheme中使用了单例,并取得了很大的成功,可以testing确保并行testing运行stream中的确定性行为。

文斯·休斯顿有这些标准,这对我来说似乎是合理的:

只有满足以下三条标准,才能考虑单身人士:

  • 单一实例的所有权不能合理分配
  • 延迟初始化是可取的
  • 全球访问不另外规定

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

辛格尔顿不是单一的实例!

不像其他的答案,我不想谈论单身人士怎么了,但是告诉你他们是多么强大和真棒,他们是正确的使用!

  • 问题 :单线程可能是multithreading环境中的一个挑战
    解决scheme :使用单线程引导进程来初始化您的单例的所有依赖关系。
  • 问题 :很难嘲笑单身人士。
    解决scheme :使用方法工厂模式进行模拟
    MyModel myModel = Factory.inject(MyModel.class); 您可以将MyModel映射到inheritance它的TestMyModel类,到处注入MyModel时,您将获得TestMyModel读信息。
  • 问题 :单身人士可能会导致记忆韭菜,因为他们从来没有处置。
    解决scheme :好,处置它们! 在您的应用程序中实施callback以正确处理单身人士,您应该删除链接到他们的任何数据,最后:将其从工厂中删除。

正如我在标题单身人士所说的不是单一的事例。

  • 单身人士提高了可读性 :你可以看看你的类,看看它注入了什么单身人士,找出它的依赖。
  • 单身人士改善维护 :一旦你从一个类中删除了一个依赖项,你刚刚删除了一些单例注入,你不需要去编辑一个刚刚移动你的依赖关系的其他类的大链接(这对我来说是臭的代码@Jim Burger )
  • 单身人士提高了内存和性能 :当你的应用程序发生某些事情,并且需要长时间的callback来传递时,你正在浪费内存和性能,通过使用Singleton来裁剪中间人,并提高性能和内存使用率通过避免不必要的局部variables分配)。

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

  1. 为什么在代码中隐藏依赖关系很糟糕? 已经有数十个隐藏的依赖关系(C运行时调用,OS API调用,全局函数调用),并且很容易find单例依赖关系(search实例())。

    “做一些全球性的事情,避免传递给代码是一种代码味道。” 为什么不传递一些东西来避免使它成为一个单独的代码味道?

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

  2. 单一责任原则:我认为这有点含糊,取决于你对责任的定义。 一个相关的问题是,为什么要把这个具体的 “责任”join到一个阶级问题中?

  3. 为什么传递一个对象到一个类使得它更紧密的耦合,而不是从类内使用该对象作为单例?

  4. 为什么会改变国家持续的时间? 单身人士可以手动创build或销毁,所以控制仍然存在,并且可以使生命周期与非单身人员对象的生命周期相同。

关于unit testing:

  • 不是所有的类都需要进行unit testing
  • 不是所有需要unit testing的类都需要改变单例的实现
  • 如果他们确实需要进行unit testing,并且需要更改实现,则很容易将使用单例的类更改为通过dependency injection将单例传递给它。

我不打算评论善恶的论点,但自从Spring出现以来,我就没有使用它们。 使用dependency injection几乎消除了我对单例,servicelocators和工厂的需求。 我发现这是一个更高效和更干净的环境,至less对于我所做的工作types(基于Java的Web应用程序)而言。

单纯从纯粹的angular度来看是不好的。

从实践的angular度来看, 单身是一个权衡发展时间与复杂性

如果你知道你的应用程序不会改变那么多,他们可以随时使用。 只要知道,如果你的需求以一种意想不到的方式改变,那么你可能需要重构一些东西(这在大多数情况下是非常好的)。

单身人士有时也会使unit testing复杂化。

Singleton is a pattern and can be used or abused just like any other tool.

The bad part of a singleton is generally the user (or should I say the inappropriate use of a singleton for things it is not designed to do). The biggest offender is using a singleton as a fake global variable.

There is nothing inherently wrong with the pattern, assuming it is being used for some aspect of your model which is truly single.

I believe the backlash is due to its overuse which, in turn, is due to the fact that it's the easiest pattern to understand and implement.

When you write code using singletons, say, a logger or a database connection, and afterwards you discover you need more than one log or more than one database, you're in trouble.

Singletons make it very hard to move from them to regular objects.

Also, it's too easy to write a non-thread-safe singleton.

Rather than using singletons, you should pass all the needed utility objects from function to function. That can be simplified if you wrap all them into a helper object, like this:

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

Recent article on this subject by Chris Reath at Coding Without Comments .

Note: Coding Without Comments is no longer valid. However, The article being linked to has been cloned by another user.

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

Singletons are NOT bad. It's only bad when you make something globally unique that isn't globally unique.

However, there are "application scope services" (think about a messaging system that makes components interact) – this CALLS for a singleton, a "MessageQueue" – class that has a method "SendMessage(…)".

You can then do the following from all over the place:

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

And, of course, do:

MessageQueue.Current.RegisterReceiver(this);

in classes that implement IMessageReceiver.

Too many people put objects which are not thread safe in a singleton pattern. I've seen examples of a DataContext ( LINQ to SQL ) done in a singleton pattern, despite the fact that the DataContext is not thread safe and is purely a unit-of-work object.

The problems with singletons is the issue of increased scope and therefore coupling . There is no denying that there are some of situations where you do need access to a single instance, and it can be accomplished other ways.

I now prefer to design around an inversion of control (IoC) container and allow the the lifetimes to be controlled by the container. This gives you the benefit of the classes that depend on the instance to be unaware of the fact that there is a single instance. The lifetime of the singleton can be changed in the future. Once such example I encountered recently was an easy adjustment from single threaded to multi-threaded.

FWIW, if it a PIA when you try to unit test it then it's going to PIA when you try to debug, bug fix or enhance it.

Here is one more thing about singletons which nobody said yet.

In most cases "singletonity" is a detail of implementation for some class rather than characteristic of its interface. Inversion of Control Container may hide this characteristic from class users; you just need to mark your class as a singleton (with @Singleton annotation in Java for example) and that's it; IoCC will do the rest. You don't need to provide global access to your singleton instance because the access is already managed by IoCC. Thus there is nothing wrong with IoC Singletons.

GoF Singletons in opposite to IoC Singletons are supposed to expose "singletonity" in the interface through getInstance() method, and so that they suffer from everything said above.

Because they are basically object oriented global variables, you can usually design your classes in such a way so that you don't need them.

A pattern emerges when several people (or teams) arrives at similar or identical solutions. A lot of people still use singletons in their original form or using factory templates (good discussion in Alexandrescu's Modern C++ Design). Concurrency and difficulty in managing the lifetime of the object are the main obstacles, with the former easily managed as you suggest.

Like all choices, Singleton has its fair share of ups and downs. I think they can be used in moderation, especially for objects that survive the application life span. The fact that they resemble (and probably are) globals have presumably set off the purists.

Firstly a class and its collaborators should firstly perform their intended purpose rather than focusing on deoendents. Lifecycle management (when instances are creared snd when they go out of scope) should not be part of the cladses responsibility. The accepted best practice for this is to craft or configure a new component to manage dependencies using dependency injection.

Often software gets more complicated it makes sense to have multiple independent instances of the "singleton" class with different state. Committing code to simply grab the singleton is wrong in such cases. Using Singleton.getInstance() might be ok for small simple systems but it doesn't work / scale when one might need a different instance of the same class.

No class should be thought of as a singleton but rather that should be an aplication of it's usage or how it is used to configure dependents. For a quick and nasty this does not matter- just luke hardcoding say file paths does not matter but for bigger applications such dependencies need to be factored out and managed in more appropriate way using DI.

The problems that singleton cause in testing is a symptom of their hard coded single usage case/environment. The test suite and the many tests are each individual and separate something that is not compatible with hardcoding a singleton.