单例是一个备受争议的设计模式,因此我对Stack Overflow社区对它们的想法很感兴趣。
请提供您提出意见的理由,而不仅仅是“单字适合懒惰的程序员!”
尽管反对使用Singletons,但关于此问题的文章还是不错的: scienceninja.com:performant-singletons。
有人在上面有其他好文章吗?也许支持辛格尔顿?
单例是一个备受争议的设计模式,因此我对Stack Overflow社区对它们的想法很感兴趣。
请提供您提出意见的理由,而不仅仅是“单字适合懒惰的程序员!”
尽管反对使用Singletons,但关于此问题的文章还是不错的: scienceninja.com:performant-singletons。
有人在上面有其他好文章吗?也许支持辛格尔顿?
Answers:
捍卫单身人士:
针对单身人士:
我个人的意见:
我使用单例,但如果有合理的选择,请避免使用它们。到目前为止,这对我来说效果很好,而且我发现它们是可以测试的,尽管要测试的工作要多一些。
class MySingleton { public: static MySingleton &getInstance() { static MySingleton instance; return instance; } private: MySingleton(); ~MySingleton(); };
。参考-stackoverflow.com/questions/2593324/c-singleton-class
Google拥有适用于Java的Singleton Detector,我相信它最初是一种必须在Google生成的所有代码上运行的工具。删除Singleton的简要原因:
因为它们会使测试变得困难并隐藏设计中的问题
有关更明确的说明,请参见Google的“为何单例引起争议”。
单身人士只是化装中的一堆全局变量。
全局变量和单身人士一样都有其用途,但是如果您认为使用单身人士正在做一些很酷且有用的事情,而不是使用讨厌的全局变量(每个人都知道全局变量是不好的mmkay),那么您就会被误导。
Singleton的目的是确保一个类只有一个实例,并提供对其的全局访问点。大多数时候,重点放在单个实例点上。想象一下它是否叫做Globalton。这听起来不那么吸引人,因为它强调了全局变量的(通常)负面含义。
大多数反对单身人士的好论点都与他们在测试中遇到的困难有关,因为为他们创建测试双倍并不容易。
在Google测试博客中,有三篇关于Misko Hevery的Singletons的不错的博客文章。
尽管它被滥用很多,但Singleton并不是一个可怕的模式。我认为这种滥用是因为它是较容易的模式之一,并且对于单例而言,大多数新特性都吸引了它的全局副作用。
埃里希·伽玛(Erich Gamma)曾表示,单身是他希望不包括在GOF书中的一种模式,这是一个糟糕的设计。我倾向于不同意。
如果使用该模式以便在任何给定时间创建一个对象的单个实例,则该模式正在正确使用。如果使用单例以产生全局效果,则使用不正确。
缺点:
优点:
小鸡之所以喜欢我,是因为我很少使用单例,而当我这样做时,通常是不寻常的。不,真的,我喜欢单例模式。你知道为什么?因为:
当然,“专家”将围绕“单元测试”和“依赖注入”进行大量讨论,但这充斥着丁戈的肾脏。您说单例很难进行单元测试?没问题!只需公开宣布所有内容,然后将您的课程变成具有全球品味的有趣之屋即可。您还记得1990年代的节目Highlander吗?单身人士之所以这样,是因为:A.它永远不会死;B。只能有一个。因此,请停止收听所有这些DI区域,并放弃您的单例。这里有一些更好的理由...
我与之共事的一位同事非常具有Singleton意识。每当有某种像经理或老板之类的东西时,他都会将其变成单例,因为他认为应该只有一个老板。每次系统提出一些新要求时,事实证明,存在允许多个实例的完全正当理由。
我要说的是,如果领域模型指示(不是“建议”)存在一个,则应该使用单例。所有其他情况只是一个类的单个实例。
我一直在试图找到一种方法来拯救可怜的辛格尔顿,但我必须承认这很困难。我见过很少使用它们,并且在当前进行依赖注入和单元测试的驱动器中,它们很难使用。它们绝对是我与许多从未破解过“ GoF”书的程序员一起工作的具有设计模式的编程的“货物崇拜”体现,但他们知道“ Singelton”,因此也知道了“ Patterns”。
不过,我确实不同意Orion,在大多数情况下,我都看到singeltons过度怀疑这不是衣服上的全局变量,而更像衣服上的全局服务(方法)。有趣的是,如果您尝试通过CLR接口以安全模式在SQL Server 2005中使用Singeltons,则系统将标记该代码。问题是您拥有的持久数据超出了可能运行的任何给定事务,当然,如果您将实例变量设置为只读,则可以解决此问题。
这个问题导致我一年的工作量很大。
许多应用程序要求某个类只有一个实例,因此仅具有一个类的实例的模式很有用。但是该模式的实现方式有所不同。
有一个静态单例,其中类强制每个进程只能有一个该类的实例(在Java中实际上是每个ClassLoader一个)。另一种选择是仅创建一个实例。
静态单例是邪恶的-一种全局变量。它们使测试更加困难,因为不可能完全隔离地执行测试。您需要复杂的设置和拆卸代码才能在每次测试之间清洗系统,并且很容易忘记正确清洗某些全局状态,这又可能导致测试中出现未指定的行为。
只创建一个实例是好的。您只需在程序启动时创建一个实例,然后将指向该实例的指针传递给需要它的所有其他对象。依赖注入框架使此操作变得容易-只需配置对象的范围,DI框架将负责创建实例并将其传递给所有需要它的人。例如,在Guice中,您将使用@Singleton注释该类,并且DI框架将仅创建该类的一个实例(每个应用程序-您可以在同一JVM中运行多个应用程序)。这使测试变得容易,因为您可以为每个测试创建该类的新实例,并让垃圾回收器在不再使用该实例时销毁该实例。全局状态不会从一项测试泄漏到另一项测试。
有关更多信息: 清洁代码讨论-“全局状态和单例”
单例的最大问题是,它们使单元测试变得困难,尤其是当您要并行但独立地运行测试时。
第二个原因是,人们通常认为带有双重检查锁定的惰性初始化是实现它们的好方法。
最后,除非单例是不可变的,否则当您尝试扩展应用程序以在多个处理器上的多个线程中运行时,它们很容易成为性能问题。竞争性同步在大多数环境中都很昂贵。
单例有其用途,但是在使用和公开它们时必须小心,因为它们太容易滥用,很难真正地进行单元测试,并且很容易基于两个互相访问的单例创建循环依赖关系。
但是,这对您很有帮助,因为当您要确保所有数据在多个实例之间同步时,例如,分布式应用程序的配置可能依赖单例来确保所有连接使用相同的最新数据。日期数据集。
我与Spring一起使用过多次Singleton ,但并不认为它是拐杖或懒惰的。
这种模式允许我做的是为一堆配置类型的值创建一个单一的类,然后在我的Web应用程序的多个用户之间共享该特定配置实例的单个(非可变)实例。
就我而言,单例包含特定于该客户端的客户端配置条件-css文件位置,数据库连接条件,功能集等。这些类是通过Spring实例化和访问的,并由具有相同配置的用户(即,来自同一公司的2个用户)共享。* **我知道这类应用程序有个名字,但它在逃避我*
我觉得为应用程序的每个用户创建(然后垃圾收集)这些“常量”对象的新实例会很浪费。
我正在阅读有关“ Singleton”,它的问题,何时使用等的很多知识,到目前为止,这些是我的结论:
Singleton的经典实现与实际要求之间的混淆:只需要一个类的实例!
通常很难实现。如果需要唯一的实例,请不要使用使用静态GetInstance()方法返回静态对象的(反)模式。这使一个类负责实例化自身的单个实例并执行逻辑。这打破了单一责任原则。相反,这应该由工厂类实现,并负责确保仅存在一个实例。
它在构造函数中使用,因为它易于使用,并且不能作为参数传递。这应该使用依赖注入来解决,这是实现良好且可测试的对象模型的好模式。
不是TDD。如果您执行TDD,则会从实现中提取依赖项,因为您希望测试易于编写。这使您的对象模型变得更好。如果使用TDD,则不会编写静态的GetInstance =)。顺便说一句,如果您认为职责明确的对象而不是类,您将获得相同的效果=)。
我真的不同意在化装服装创意中的一堆全局变量。当用于解决正确的问题时,单例确实非常有用。让我给你一个真实的例子。
我曾经在我工作的地方开发过一小段软件,有些表格必须使用有关公司,员工,服务和价格的信息。在第一个版本中,每次打开表单时,系统都会从数据库中加载该数据。当然,我很快意识到这种方法不是最好的方法。
然后,我创建了一个单例课程,命名为company封装了该地点的所有内容,并且在系统打开时已完全填充了数据。
这不仅是一堆花哨的变量,因为它要承担许多责任,例如与持久层进行通信以保存/检索有关公司的数据,处理员工和收取价格等。
另外,它是拥有公司数据的固定的,系统范围的,易于访问的点。
单例非常有用,使用它们本身并不是反模式。但是,它们之所以声名狼藉,是因为它们迫使任何使用代码的人都承认它们是单例的,以便与它们进行交互。这意味着,如果您需要“取消单一化”它们,则对您的代码库的影响可能非常大。
相反,我建议您将Singleton藏在工厂后面。这样,如果将来需要更改服务的实例化行为,则只需更改工厂,而不是更改所有使用Singleton的类型。
更好的是,使用控制容器的反转!它们中的大多数允许您将实例化行为与类的实现分开。
例如在Java中,单例的一件令人恐惧的事情是,在某些情况下,您可能会遇到同一单例的多个实例。JVM基于两个元素唯一标识:类的完全限定名称,以及负责加载它的类加载器。
这意味着可以由两个彼此不知道的类加载器加载同一个类,并且应用程序的不同部分将具有与之交互的此单例的不同实例。