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

时间:2020-03-05 18:39:47  来源:igfitidea点击:

单例是一个备受争议的设计模式,因此我对Stack Overflow社区对它们的想法很感兴趣。

请提供我们提出意见的理由,而不仅仅是"单字适合懒惰的程序员!"

关于此问题,这是一篇不错的文章,尽管它反对使用Singletons:
scienceninja.com:表演者单曲。

有人有其他好的文章吗?也许支持辛格尔顿?

解决方案

回答

Singleton的目的是确保一个类只有一个实例,并提供对其的全局访问点。大多数时候,重点放在单个实例点上。想象一下它是否叫做Globalton。这听起来不那么吸引人,因为这强调了(通常)全局变量的负面含义。

大多数反对单身人士的好论点都与他们在测试中遇到的困难有关,因为为他们创建测试双倍并不容易。

回答

单例有其用途,但是在使用和公开它们时必须小心,因为它们太容易滥用,很难真正地进行单元测试,并且很容易基于相互访问的两个单例创建循环依赖关系。

但是,这对我们很有帮助,因为当我们希望确保所有数据在多个实例之间都是同步的时,例如,分布式应用程序的配置可能依赖单例来确保所有连接使用相同的最新消息。日期数据集。

回答

单身人士只是化装中的一堆全局变量。

全局变量和单身人士一样都有其用途,但是如果我们认为使用单身人士正在做一些很酷且有用的事情,而不是使用讨厌的全局变量(每个人都知道全局变量是不好的),那么我们就会被误导。

回答

Google拥有适用于Java的Singleton Detector,我认为它最初是一种必须在Google生成的所有代码上运行的工具。删除Singletons的简要原因:

because they can make testing
  difficult and hide problems with your
  design

有关更明确的说明,请参阅Google的"为什么单例有争议"。

回答

我发现我们在决定为什么使用单例时必须非常小心。正如其他人提到的那样,这与使用全局变量本质上是相同的问题。我们必须非常谨慎,并考虑使用一个可以做什么。

使用它们非常罕见,通常有更好的方法来做事情。我遇到过这样的情况:我单身完成某件事,然后在发现造成问题的糟糕程度之后(或者在我提出更好,更理智的解决方案之后)不得不仔细检查我的代码以将其取出)

回答

我一直在试图找到一种方法来挽救可怜的辛格尔顿,但我必须承认这很困难。我很少看到它们的合法用法,并且在当前进行依赖注入和单元测试的驱动器中,它们很难使用。他们无疑是具有设计模式的编程的"货物崇拜"表现,我曾与许多从未破解过" GoF"书的程序员合作过,但他们知道" Singelton",因此也知道了"模式"。

但是,我确实不同意Orion,在大多数情况下,我看到singeltons过度使用它不是衣服上的全局变量,而更像衣服上的全局服务(方法)。有趣的是,如果我们尝试通过CLR接口以安全模式在SQL Server 2005中使用Singeltons,则系统将标记该代码。问题是我们拥有的持久数据超出了可能运行的任何给定事务,当然,如果我们将实例变量设置为只读,则可以解决此问题。

这个问题导致我一年的工作量很大。

回答

我真的在化装创意中对一堆全局变量持不同意见。当用于解决正确的问题时,单例确实非常有用。让我给你一个真实的例子。

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

然后,我创建了一个名为company的单例类,该类封装了该场所的所有内容,并且在系统打开时已完全填充了数据。

这不仅是一堆花哨的变量,因为它要承担许多责任,例如与持久层进行通信以保存/检索有关公司的数据,处理员工和收取价格等。

另外,它是拥有公司数据的固定的,系统范围的,易于访问的点。

回答

It was not just a bunch of variables in a fancy dress because this was had dozens of responsibilities, like communicating with persistence layer to save/retrieve data about the company, deal with employees and prices collections, etc.

我必须说,我们并不是真正在描述应该是单个对象的事物,并且有争议的是,除了"数据序列化"之外,其他任何事物都应该是一个罪恶。

我可以看到我通常会设计的至少3组类,但是我倾向于使用较小的简单对象,这些对象可以很好地完成一组狭窄的任务。我知道这不是大多数程序员的本性。 (是的,我每天要处理5000种线路类的怪兽,并且我特别喜欢某些人编写的1200条线路方法。)

我认为关键是,在大多数情况下,我们不需要singelton,而通常只是生活变得更加艰难。

回答

圣战!好的,让我看看。.上次我检查设计警察时说..

单例很糟糕,因为它们阻碍了无法为每个测试用例重新创建自动测试实例。
相反,逻辑应该在可以轻松实例化和测试的(A)类中。另一个类(B)应该负责约束创建。单一责任原则脱颖而出!应该知道我们应该通过B来访问A团队惯例,这是团队知识。

我大部分都同意。

回答

我认为使用Singleton模式存在很大的误解。这里的大多数评论都将其称为访问全局数据的地方。我们在这里需要小心,因为Singleton不用于访问全局变量。

Singleton应该仅用于给定类的一个实例。模式存储库提供有关Singleton的大量信息。

回答

我与Spring一起使用过多次Singleton,但并不认为它是拐杖或者懒惰的。

这种模式允许我做的是为一堆配置类型的值创建单个类,然后在我的Web应用程序的多个用户之间共享该特定配置实例的单个(非可变)实例。

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

我觉得为应用程序的每个用户创建(然后垃圾收集)这些"常量"对象的新实例本来会很浪费。

回答

单例非常有用,使用它们本身并不是反模式。但是,它们之所以声名狼藉,主要是因为它们迫使任何使用代码的人都承认它们是单例的,以便与它们进行交互。这意味着,如果我们需要"取消单一化"它们,则对代码库的影响可能非常大。

相反,我建议我们将Singleton藏在工厂后面。这样,如果将来需要更改服务的实例化行为,则可以只更改工厂,而不是更改所有使用Singleton的类型。

更好的是,使用控制容器的倒置!它们中的大多数允许我们将实例化行为与类的实现分开。

回答

捍卫单身人士:

  • 它们没有全局变量那么糟糕,因为全局变量没有标准强制的初始化顺序,并且由于天真或者意外的依赖顺序,我们很容易看到不确定的错误。单例(假设它们已分配在堆上)是在所有全局变量之后创建的,并且在代码中非常容易预测的位置。
  • 它们对于资源延迟/缓存系统(例如到慢速I / O设备的接口)非常有用。如果我们智能地为缓慢的设备构建了单例界面,却没人会调用它,那么我们将不会浪费任何时间。如果另一段代码从多个位置调用它,则单例可以同时优化两者的缓存,并避免任何重复查找。我们还可以轻松避免在单例控制的资源上出现任何死锁情况。

针对单身人士:

  • 在C ++中,没有一种好的方法可以在单例之后自动清除。有一些变通方法,并且有些怪异的方法,但是没有简单,通用的方法来确保始终调用单例的析构函数。就内存而言,这并不是那么糟糕-为此,可以将其视为更多的全局变量。但是如果我们单身人士分配其他资源(例如锁定某些文件)并且不释放它们,则可能会很糟糕。

我个人的意见:

我使用单例,但如果有合理的选择,请避免使用它们。到目前为止,这对我来说效果很好,而且我发现它们是可以测试的,尽管要测试的工作要多一些。

回答

在Google测试博客中,有三篇关于Mi?ko Hevery的Singletons的不错的博客文章。

  • 单身人士是病态的骗子
  • 所有的单身人士都去哪儿了?
  • 单身人士的根本原因

回答

单例的最大问题是它们使单元测试变得困难,尤其是当我们要并行但独立地运行测试时。

第二个原因是,人们通常认为带有双重检查锁定的惰性初始化是实现它们的好方法。

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

回答

例如在Java中,单例的一件令人恐惧的事情是,在某些情况下,我们可能会遇到同一单例的多个实例。 JVM基于两个元素唯一标识:类的完全限定名称,以及负责加载它的类加载器。

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

回答

我与之共事的一位同事非常具有Singleton意识。每当有某种像经理或者老板之类的东西时,他都会将其变成单例,因为他认为应该只有一个老板。而且,每当系统接受一些新要求时,事实证明,存在允许多个实例的完全正当理由。

我要说的是,如果领域模型指示(而不是"建议")存在一个,则应该使用单例。所有其他情况只是一个类的单个实例。

回答

编写正常的,可测试的,可注入的对象,并让Guice / Spring /任何对象处理实例化。严重地。

这甚至适用于高速缓存或者单例的自然使用情况。无需重复编写代码即可尝试强制执行一个实例的恐怖。让依赖项注入框架来处理它。 (如果我们尚未使用Guice,建议我们为轻量级的DI容器使用Guice)。

回答

Singleton作为实现细节很好。 Singleton作为接口或者访问机制是一个巨大的PITA。

不带参数返回对象实例的静态方法与仅使用全局变量仅稍有不同。相反,如果某个对象通过构造函数或者其他方法引用了传入的单例对象,则该单例的实际创建方式与整个模式无关紧要无关紧要。

回答

许多应用程序要求某个类只有一个实例,因此仅具有一个类的实例的模式很有用。但是模式的实现方式有所不同。

有一个静态单例,其中该类强制每个进程只能有一个该类的实例(在Java中,实际上是每个ClassLoader一个)。另一种选择是仅创建一个实例。

静态单例是邪恶的一种全局变量。它们使测试更加困难,因为不可能完全隔离地执行测试。我们需要复杂的设置和拆卸代码才能在每次测试之间清洗系统,并且很容易忘记正确清洗某些全局状态,而这又可能导致测试中未指定行为。

只创建一个实例是好的。我们只需在程序启动时创建一个实例,然后将指向该实例的指针传递给需要该实例的所有其他对象。依赖注入框架使我们只需配置对象的范围即可轻松实现,DI框架将负责创建实例并将其传递给所有需要它的人。例如,在Guice中,我们将使用@Singleton注释该类,并且DI框架将仅创建该类的一个实例(对于每个应用程序,我们可以在同一JVM中运行多个应用程序)。这使测试变得容易,因为我们可以为每个测试创建该类的新实例,并让垃圾回收器在不再使用该实例时销毁该实例。全局状态不会从一项测试泄漏到另一项测试。

欲获得更多信息:
清洁法典谈"全球状态和单身人士"

回答

尽管它被滥用很多,但Singleton并不是一个可怕的模式。我认为这种滥用是因为它是较简单的模式之一,并且对于单例而言,大多数新特性都吸引了它的全局副作用。

埃里希·伽玛(Erich Gamma)曾表示,单身是他希望不包括在GOF书中的一种模式,这是一个糟糕的设计。我倾向于不同意。

如果使用该模式以便在任何给定时间创建一个对象的单个实例,则该模式正在正确使用。如果使用单例以产生全局效果,则使用不正确。

缺点:

  • 如果由于需要多个实例而在以后需要重构代码,则比将单例类传递给使用该实例的对象(使用接口)要痛苦得多

好处:

  • 实例在需要时创建
  • 全局访问是一个副作用

回答

小鸡之所以喜欢我,是因为我很少使用单身人士,而当我这样做时,这通常是不寻常的。不,说真的,我喜欢单例模式。你知道为什么?因为:

  • 我很懒。
  • 一切都不会出错。

当然,"专家"将围绕"单元测试"和"依赖注入"进行大量讨论,但这充斥着丁戈的肾脏。我们说单例很难进行单元测试吗?没问题!只需公开宣布所有内容,然后将课程变成具有全球品味的有趣之屋即可。我们还记得1990年代的节目Highlander吗?单身人士之所以这样,是因为:A.它永远不会死; B。只能有一个。因此,请停止收听所有这些DI区域,并放弃单例。这里有一些更好的理由...

  • 每个人都在做。
  • 单例模式使我们立于不败之地。
  • 带有"胜利"(或者"乐趣",具体取决于口音)的单例押韵。

回答

我正在阅读有关" Singleton",它的问题,何时使用等的很多知识,到目前为止,这些是我的结论:

  • Singleton的经典实现与实际要求之间的混淆:只需要一个类的实例!
  • 通常很难实现。如果我们想要一个唯一的实例,请不要使用使用静态GetInstance()方法返回静态对象的(反)模式。这使一个类负责实例化自身的单个实例并执行逻辑。这违反了单一责任原则。相反,这应该由工厂类实现,其职责是确保仅存在一个实例。
  • 它在构造函数中使用,因为它易于使用,并且不能作为参数传递。这应该使用依赖注入来解决,这是实现良好且可测试的对象模型的绝佳模式。
  • 不是TDD。如果执行TDD,则会从实现中提取依赖项,因为我们希望测试易于编写。这使对象模型变得更好。如果使用TDD,则不会编写静态的GetInstance =)。顺便说一句,如果我们认为职责明确的对象而不是类,我们将获得相同的效果=)。