关于设计模式:我什么时候应该使用单例Singletons呢?

On design patterns: When should I use the singleton?

光荣的全局变量-成为光荣的全局类。有人说打破了面向对象的设计。

给我一些场景,除了使用单例的好的旧记录器之外。


在我对真相的质询中,我发现,实际上有很多"可以接受的"理由使用一个单词。

有一个原因是,往往来到和再次来到内部,这就是一个"记录"类(你所说的)。在本案中,可以从一个单一的课题中使用一个单一的单一课题,因为一个单一的课题通常需要在一个项目中的每一个课题上重复使用。如果每一类使用这一测井类,依赖注射成为cumbersome。

Logging is a specific example of an"acceptable"Singleton,because it doesn't effect the execution of your code.无法测井,代码执行相同。这是可以的,萨米人。Misko把它放到了歌手的根源上,"这里的信息流通一条途径:从应用到编辑器。即使标志是全球性的,因为从标志流到您的应用,标志是可以接受的。"

我肯定还有其他的理由Alex Miller,in"Patterns I hate",talks of service locators and client side UI's also being possibly"acceptable"choices.

我爱你,但你却让我失望


单人候选人必须满足三个要求:

  • 控制对共享资源的并发访问。
  • 将从系统的多个不同部分请求对资源的访问。
  • 只能有一个对象。

如果您建议的单例只有一个或两个这样的要求,重新设计几乎总是正确的选择。

例如,打印机后台处理程序不太可能从多个位置(打印菜单)调用,因此可以使用互斥体来解决并发访问问题。

一个简单的记录器是一个可能有效的单例的最明显的例子,但是这可以随着更复杂的日志方案而改变。


正在读取只应在启动时读取的配置文件,并将其封装到一个单独的实例中。


当需要管理共享资源时,可以使用singleton。例如打印机后台处理程序。您的应用程序应该只有一个后台处理程序实例,以避免对同一资源的请求冲突。

或数据库连接或文件管理器等。


只读单例存储一些全局状态(用户语言、帮助文件路径、应用程序路径)是合理的。小心使用单例控制业务逻辑-单例几乎总是以多重结束。


管理到数据库的连接(或连接池)。

我还将使用它来检索和存储外部配置文件上的信息。


使用singleton的方法之一是覆盖一个实例,其中必须有一个"代理"来控制对资源的访问。在记录器中,单例是很好的,因为它们代理对文件的访问,例如,只能以独占方式写入文件。对于类似于日志记录的内容,它们提供了一种将写入内容抽象为类似于日志文件的内容的方法——您可以将缓存机制包装到singleton,等等……

还可以考虑这样一种情况:您的应用程序有许多窗口/线程等,但需要一个单一的通信点。我曾经用一个来控制我希望我的应用程序启动的作业。单工负责将作业序列化,并将它们的状态显示给程序中感兴趣的任何其他部分。在这种情况下,您可以将单例视为类似于在应用程序中运行的"服务器"类…高温高压


当整个应用程序共享的资源被管理存取时,应当使用一个单元,而该单元对同一类别的多个实体具有潜在的破坏性。确保安全共享资源的获取是一个很好的例子,说明这种模式在何处是重要的。

当你用单词时,你应该确保你不是意外的依赖。Ideally,the singletons(like most static variables in an application)be set up during your initiation code for the application(static void main()for C 355;executable,static void main()for Java executible)and then passed in all other classes that are instantiated which require.这有助于你保持可测试性。


在test::builder中可以找到一个单例实例,这个类支持几乎所有现代Perl测试模块。测试::builder singleton存储和代理测试过程的状态和历史(历史测试结果,统计测试运行的数量)以及测试输出将要进行的位置。这些都是协调由不同作者编写的多个测试模块以在单个测试脚本中协同工作所必需的。

测试的历史:建设者的独身是有教育意义的。调用new()总是会得到相同的对象。首先,所有数据都存储为类变量,对象本身不包含任何内容。这一直有效,直到我想用它自己测试test::builder。然后,我需要两个test::builder对象,一个设置为虚拟对象,来捕获和测试它的行为和输出,另一个设置为真正的测试对象。此时,测试::Builder被重构为一个真实的对象。singleton对象存储为类数据,new()将始终返回它。添加了create()来制作一个新的对象并进行测试。

目前,用户希望在自己的模块中更改test::builder的一些行为,但不要影响其他的行为,而测试历史记录在所有测试模块中仍然是相同的。现在正在发生的是单片测试::Builder对象被分解成更小的部分(历史记录、输出、格式…),测试::Builder实例将它们收集在一起。现在,test::builder不再是单例的了。它的组成部分,如历史,可以是。这使得单粒子的不灵活的必要性降低了一个水平。它使用户能够更灵活地混合和匹配工件。较小的单例对象现在可以只存储数据,其中包含的对象决定如何使用数据。它甚至允许一个非test::builder类使用test::builder历史记录和输出单例来配合使用。

似乎在数据协调和行为灵活性之间存在着推挽作用,可以通过将单例数据放在共享数据周围,尽可能减少行为量来确保数据完整性来减轻这种影响。


我认为单例使用可以被认为与数据库中的多对一关系相同。如果代码中有许多不同的部分需要处理对象的单个实例,那么使用单例就很有意义。


当您从数据库或文件加载配置属性对象时,将其作为单例有助于实现;没有理由继续重新读取服务器运行时不会更改的静态数据。


正如大家所说,共享资源——特别是不能处理并发访问的资源。

我看到的一个具体例子是Lucene搜索索引编写器。


共享资源。尤其是在PHP中,一个数据库类、一个模板类和一个全局变量depot类。所有这些都必须由代码中使用的所有模块/类共享。

这是一个真正的对象用法->template类包含正在构建的页面模板,它由添加到页面输出的模块进行塑造、添加和更改。它必须作为单个实例保存,这样才能发生这种情况,数据库也是如此。使用共享数据库单例,所有模块的类都可以访问查询并获取它们,而无需重新运行它们。

全局变量仓库singleton为您提供了一个全局、可靠且易于使用的变量仓库。它可以很好地整理您的代码。想象一下,将数组中的所有配置值都放在一个单例中,比如:

$gb->config['hostname']

或在数组中具有所有语言值,如:

$gb->lang['ENTER_USER']

在运行页面代码的最后,您会得到一个现在已经成熟的:

$template

singleton是一个$gbsingleton,其中包含用于替换的lang数组,所有输出都已加载并就绪。您只需将它们替换为成熟模板对象的页面值中现在存在的键,然后将其提供给用户。

它的最大优点是你可以对任何东西进行任何你喜欢的后处理。您可以将所有语言值通过管道传输到Google Translate或其他翻译服务,然后将其返回,并将其替换到相应的位置,例如翻译。或者,可以根据需要替换页面结构或内容字符串中的内容。


在实现状态模式时,可以使用singleton(以GOF手册中所示的方式)。这是因为具体的状态类没有自己的状态,并且根据上下文类执行它们的操作。

您还可以将抽象工厂设置为单例。


将特定的基础结构问题配置为单例或全局变量可能非常实用。我最喜欢的例子是依赖注入框架,它利用单例作为框架的连接点。

在这种情况下,您需要依赖基础结构来简化库的使用并避免不必要的复杂性。


以代码为例,Perhaps.

在这里,具体的例子是一个扑克游戏中的一个单词,它允许所有的行为在包装树上到达FEW,游戏核心界面(I.E.,The Facades for the Model,View,Controller,Environment等):

http://www.edmundkirwan.com/servlet/contral/cs1/frac-cs40.html

艾德


在处理可插拔模块时,我将它用于封装命令行参数的对象。主程序不知道加载模块的命令行参数是什么(甚至不知道加载的是什么模块)。例如,main加载a,它本身不需要任何参数(所以为什么需要一个额外的指针/引用/不管什么,我不确定-看起来像污染),然后加载模块x、y和z。其中两个模块,比如x和z,需要(或接受)参数,所以它们调用命令行singleton来告诉它要接受什么参数,以及a t运行时,它们会回电以查明用户是否确实指定了其中的任何一个。

在许多方面,如果每个查询只使用一个进程,那么处理CGI参数的单例程序也会起到类似的作用(其他mod_*methods不会这样做,因此在这里就不好了——因此,如果您连接到mod_perl或其他世界,那么在mod_cgi世界中不应该使用单例程序的论点是这样的)。


1-对第一个答案的评论:

我不同意静态记录器类。这对于一个实现是可行的,但是它不能替换为单元测试。静态类不能替换为测试double。如果你不进行单元测试,你就不会在这里看到问题。

2-我尽量不手工创建一个单例。我只是创建一个简单的对象,其中包含允许我将合作者注入对象的构造函数。如果我需要一个单体,我会使用一个依赖项IyEngy框架(Spring .net,Unity for .NET,Spring for Java),或者其他一些。