单身人士:好的devise还是拐杖?

单身人士是一个激烈争论的devise模式,所以我对Stack Overflow社区对他们的看法感兴趣。

请提供您的意见的理由,不只是“单身是懒惰的程序员!”

这是一个相当不错的文章,虽然它是反对使用单身人士 : scientificninja.com:性能单身人士 。

有没有人有任何其他好的文章呢? 也许支持单身人士?

为了保护单身人士:

  • 它们并不像全局variables那样糟糕,因为全局variables没有标准强制的初始化顺序,而且由于天真或意外的依赖性顺序,您可以轻松地看到非确定性错误。 单身人士(假设他们被分配在堆上)是在所有的全局variables之后创build的,并且在代码中非常可预测的地方。
  • 它们对资源惰性/高速caching系统非常有用,如缓慢I / O设备的接口。 如果你智能地构build一个单一的接口到一个慢速的设备,而且没有人叫它,你不会浪费任何时间。 如果另一段代码从多个地方调用它,那么您的单例可以同时优化caching,并避免任何双重查找。 您也可以轻松地避免在单例控制资源上的任何死锁状况。

反对单身人士:

  • 在C ++中,单例之后没有很好的自动清理方法。 有一些解决办法,还有一些方法可以做,但是没有简单的通用方法来确保你的单例的析构函数总是被调用。 这不是太可怕的记忆 – 只是把它看作更多的全球variables,为此目的。 但是如果你的单例分配其他资源(例如locking一些文件)并且不释放它们,可能会很糟糕。

我个人的看法:

我使用单身,但如果有一个合理的select,避免它们。 到目前为止,这对我来说效果很好,而且我发现它们是可以testing的,尽pipe稍微有更多的工作要testing。

Google有一个Java的Singleton检测器 ,我认为它是一个必须在Google生成的所有代码上运行的工具。 简单的理由删除单身人士:

因为他们可以使testing变得困难并且隐藏devise中的问题

有关更明确的解释,请参阅Google 为什么“单身人士有争议 ”。

一个单身人士只是一群穿着奇装异服的全球变数。

全局variables和单身人士一样都有自己的用处,但是如果你认为你正在做一些很酷而且有用的单身人士,而不是使用一个糟糕的全局variables(每个人都知道全局variables是糟糕的),那么你就不幸被误导了。

单例的目的是确保一个类只有一个实例,并提供一个全局访问点。 大部分时间关注于单个实例点。 想象一下,如果它被称为一个Globalton。 这听起来不太吸引人,因为这强调了全球variables的(通常)消极内涵。

对单身人士来说,大多数好的论点都与他们在testing中出现的困难有关,因为为他们创buildtesting双打并不容易。

在Googletesting博客中,有三篇关于MiškoHevery的 Singletons的相当不错的博客文章。

  1. 单身人士是病理性的说谎者
  2. 所有的单身人士都去哪了?
  3. 单身人士的根本原因

辛格尔顿不是一个可怕的模式,虽然它被滥用了很多。 我认为这种滥用是因为它是一种较为容易的模式,对单身人士来说,最新的东西被全球的副作用所吸引。

Erich Gamma曾经说过,这个单身人士是一个他不希望被包含在GOF书籍中的模式,这是一个糟糕的devise。 我倾向于不同意。

如果在任何给定时间使用该模式来创build对象的单个实例,那么该模式正在被正确使用。 如果为了达到全局效果而使用单身人士,则会被错误地使用。

缺点:

  • 在调用单例的代码中,您将耦合到一个类
    • 创build一个unit testing的麻烦,因为它很难用模拟对象replace实例
    • 如果由于需要多个实例而需要对代码进行重构,则比单例类被传递到使用它的对象(使用接口)更加痛苦

优点:

  • 一个类的一个实例在任何给定的时间点都被表示出来。
    • 按照devise,你正在执行这个
  • 实例在需要时被创build
  • 全球访问是一个副作用

小鸡挖我,因为我很less使用单身,当我这样做通常是不寻常的。 不,严重的是,我喜欢单身模式。 你知道为什么? 因为:

  1. 我很懒。
  2. 没有什么可以出错。

当然,“专家”会围绕“unit testing”和“dependency injection”这个话题进行一番讨论,但这些都是stream浪狗肾脏的负担。 你说单身人士很难unit testing? 没问题! 只要宣布一切公开,把你的class级变成一个有趣的全球善良之家。 你还记得20世纪90年代的Highlander吗? 单身人士是这样的,因为:A.它永远不会死; 和B.只能有一个。 所以停止听所有这些DI weenies,放弃你的单身人士。 这里有一些更好的理由

  • 每个人都在这样做。
  • 单身模式让你立于不败之地。
  • 用“胜利”(或“乐趣”取决于你的口音)的单身儿歌。

我认为对Singleton模式的使用有一个很大的误解。 这里的大多数评论都把它称为访问全球数据的地方。 我们在这里需要小心 – 作为模式的Singleton 不是用来访问全局variables的。

单身人士应该只有一个给定类的实例 。 Pattern Repository在Singleton上有很好的信息。

我曾与其中一位同事非常喜欢辛格尔顿。 每当有什么东西像一个经理或老板那样的对象时,他会把它变成一个单身人士,因为他认为应该只有一个老板。 每当系统出现一些新的需求时,就会发现允许多个实例的完全正确的理由。

如果领域模型规定(而不是“build议”),那么应该使用单例。 所有其他情况都只是一个类的单个实例。

我一直在想方法来find可怜的辛格尔顿在这里的救援,但我必须承认这很难。 我已经看到他们的合法用途很less,而且目前的驱动做dependency injection和unit testing,他们很难使用。 他们肯定是用devise模式进行编程的“货物崇拜”performance,我曾与许多从未破解过“GoF”书的程序员一起工作,但他们知道“Singelton”,因此他们知道“模式”。

我不得不不同意Orion,但是大多数时候我看到singelton过度使用它不是全球变数,而更像是服装中的全球服务(方法)。 有趣的是,如果您尝试通过CLR接口以安全模式在SQL Server 2005中使用Singelton,则系统将标记代码。 问题是,除了可能运行的任何给定事务之外,您拥有持久性数据,当然,如果将实例variables只读,则可以解决问题。

这个问题导致我一年的很多返工。

圣战! 好吧,让我看看..上次我检查devise警方说..

单身人士是不好的,因为他们阻碍自动testing – 实例不能重新创build每个testing用例。 相反,逻辑应该是在一个类(A),可以很容易地实例化和testing。 另一个class(B)应该负责限制创作。 单一职责原则脱颖而出! 应该是团队知识,你应该通过B去访问一个团队的约定。

我主要赞同..

许多应用程序要求只有一个类的一个实例,所以只有一个类的实例是有用的。 但是模式的实现方式也有所不同。

有一个静态的单例 ,在这个类中强制每个进程只能有一个类的实例(在Java中实际上每个ClassLoader有一个)。 另一种select是只创build一个实例

静态单例是邪恶的 – 一种全局variables。 它们使testing变得更加困难,因为不可能完全隔离地执行testing。 您需要复杂的设置和拆卸代码以在每次testing之间清理系统,而且很容易忘记正确地清理某些全局状态,从而可能导致testing中的不确定行为。

只创build一个实例是很好的 。 当程序启动的时候,你只需要创build一个实例,然后将指针传递给所有其他需要它的对象。 dependency injection框架使得这一切变得简单 – 只需configuration对象的范围,DI框架将负责创build实例并将其传递给所有需要的实例。 例如在Guice中,你可以用@Singleton来注解这个类,而DI框架将只创build一个类的实例(每个应用程序 – 你可以在同一个JVM中运行多个应用程序)。 这使testing变得简单,因为您可以为每个testing创build一个新的类实例,并让垃圾收集器在不再使用的时候销毁实例。 没有一个全球性的国家会从一个testing泄漏到另一个

欲了解更多信息: 清洁守则谈话 – “全球国家和单身人士”

单例作为实现细节是好的。 单身作为一个接口或作为一个访问机制是一个巨大的PITA。

不带参数返回对象实例的静态方法与仅使用全局variables稍有不同。 如果一个对象有一个对传入的单例对象的引用,无论是通过构造函数还是其他方法,那么实际上如何创build单例并不重要,整个模式都不重要。

这不仅仅是一群漂亮的服饰的变数,因为这有很多责任,比如与持久层进行交stream以保存/检索公司数据,处理员工和价格收集等。

我必须说,你并没有真正地描述应该是单一对象的东西,除了数据序列化之外,其中的任何一个都应该是一个单一的对象,这是值得商榷的。

我可以看到至less有三套我通常会devise的类,但是我倾向于支持更简单的对象,它们可以很好地完成一组狭窄的任务。 我知道这不是大多数程序员的本质。 (是的,我每天工作5000线的怪物,而且我对某些人写的1200线方法有特别的喜爱。)

我认为重要的一点是,在大多数情况下,你不需要一个单纯的疙瘩,而且往往只是让你的生活更加艰难。

单身人士最大的问题是他们很难进行unit testing,尤其是当你想要并行但独立地运行你的testing。

其次是人们经常认为使用双重locking的懒惰初始化是实现它们的好方法。

最后,除非你的单例是不可变的,否则当你试图扩展你的应用程序在多个处理器上的multithreading中运行时,它们很容易成为性能问题。 争用的同步在大多数环境中是昂贵的。

单身人士有他们的用途,但必须小心使用和暴露他们,因为他们太容易滥用 ,难以真正的unit testing,很容易创build基于两个单一的访问对象的循环依赖。

然而,当你想要确保所有数据在多个实例间同步时,例如分布式应用程序的configuration,可能需要依靠单例来确保所有连接使用相同的最新数据,数据的最新集合。

我发现你必须非常小心, 为什么你决定使用一个单身人士。 正如其他人所提到的,这与使用全局variables的问题基本相同。 你一定要非常谨慎,考虑一下你可以做些什么。

使用它们是非常罕见的,通常有更好的方法来做事情。 我遇到了一些情况,我用单例做了一些事情,然后不得不通过我的代码筛选出来,然后发现它有多糟糕(或者在我想出了一个更好,更理智的解决scheme之后)

我和Spring一起使用过很多单例,并没有把它看成是一个拐杖或懒惰。

这个模式允许我做的是为一组configurationtypes值创build一个类,然后在我的Web应用程序的几个用户之间共享该特定configuration实例的单个(不可变)实例。

在我的情况下,单例包含客户端configuration标准 – css文件位置,数据库连接标准,function集等 – 特定于该客户端。 这些类通过Spring实例化和访问,并由具有相同configuration的用户共享(即来自同一个公司的2个用户)。 * **我知道有这种types的应用程序的名称,但它逃避我*

我觉得为应用程序的每个用户创build(然后垃圾回收)这些“常量”对象的新实例是浪费的。

我读了很多关于“Singleton”,它的问题,什么时候使用它,等等,直到现在,我的结论是:

  • Singleton的经典实现和真实需求之间的混淆:只有一个类的实例!

  • 这通常是不好的实施。 如果您想要一个唯一的实例,请不要使用返回静态对象的静态GetInstance()方法的(反)模式。 这使得一个类负责实例化自己的单个实例,并执行逻辑。 这打破了单一责任原则 。 相反,这应该由一个工厂类实现,负责确保只有一个实例存在。

  • 它用在构造函数中,因为它很容易使用,不能作为parameter passing。 这应该使用dependency injection来解决,这是实现良好和可testing的对象模型的一个很好的模式。

  • 不是TDD 。 如果您使用TDD,则依赖性将从实现中提取出来,因为您希望testing易于编写。 这使得你的对象模型更好。 如果使用TDD,则不会写入静态GetInstance =)。 顺便说一句,如果你认为在具有明确责任的对象而不是类,你会得到相同的效果=)。

我真的不同意在一群漂亮的服装理念的全球变数 。 当用来解决正确的问题时,单身真的很有用。 让我给你一个真实的例子。

我曾经开发过一小部分软件到我工作的地方,有些表单需要使用关于公司,员工,服务和价格的一些信息。 在第一个版本中,每次打开表单时,系统都会从数据库中继续加载数据。 当然,我很快意识到这种方法不是最好的。

然后我创build了一个名为company的singleton类,它封装了关于这个地方的所有东西,并且在系统打开的时候它完全被数据填充。

这不仅仅是一群漂亮的服饰的变数,因为这有很多责任,比如与持久层进行交stream以保存/检索公司数据,处理员工和价格收集等。

此外,这是一个固定的,系统的,易于访问的公司数据点。

单身是非常有用的,使用它们本身并不是反模式。 但是,他们的声誉很差,主要是因为他们强迫任何消费代码承认他们是单身人士,以便与他们互动。 这意味着如果您需要“un-singletonize”它们,那么对您的代码库的影响可能非常大。

相反,我build议要么隐藏在工厂后面的单身人士。 这样,如果将来需要更改服务的实例化行为,则可以更改工厂而不是使用所有使用Singleton的types。

更好的是,使用控制容器的反转! 他们中的大多数允许你从实现类中分离实例化行为。

例如Java中的单例的一个可怕的事情是,在某些情况下,最终可能会得到同一个单例的多个实例。 JVM根据两个元素唯一标识:一个类的完全限定名,以及负责加载它的类加载器。

这意味着相同的类可以被两个不知道彼此的类加载器加载,并且你的应用程序的不同部分将具有与它们交互的这个单例的不同实例。

写正常的,可testing的,可注入的对象,让Guice / Spring /处理实例化的任何东西。 认真。

即使在高速caching或单例的自然使用情况下也是如此。 没有必要重复写代码的恐怖来试图强制一个实例。 让你的dependency injection框架来处理它。 (如果你还没有使用的话,我推荐Guice使用轻量级的DI容器)。

在我看来,单身人士被认为是不好的,只是因为很难用现有的testing工具来testing他们。 也许这是当前testing工具的局限性,而不是模式本身。

任何好事都可以被滥用。 我听到的大多数论点是单身人士是基本的全球变数。 他们不是。

是什么让一个全局variables很糟糕的是,尽pipe不在可见范围内,它们仍然在范围之内,所以意外地使用它们或者不小心不使用它们太容易了。 实际上不可能“不小心”使用单例variables。 这是非常格式使其使用相当慎重。

肯定有一些论点认为singleton可以隐藏调用图,因为它允许跨层调用对象,但是通过参数向下传递对象也可以实现同样的效果。

更糟糕的是,对单身人士(如单身人士工厂)的build议“修复”并没有改变任何这些问题,他们只是把它洗牌,甚至更糟糕……他们使得实例化多个单身人士成为可能。