单独的图案 是一个完全支付了成员 GoF's 模式的书, 但它近来似乎相当孤立的由开发的世界。我仍然使用相当多的单身人士,特别是对于 工厂类, ,同时,你要小心一点关于多线程的问题(如任何一类实际上),我看不出为什么他们都这么可怕的。

堆溢特别是似乎假设,每个人都同意,单是邪恶的。为什么?

请支持你的答案"事实引用,或具体专门知识"

有帮助吗?

解决方案

布莱恩·巴顿解释:

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

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

  3. 它们固有地导致代码严格耦合。在许多情况下,这使得将它们伪装在测试中相当困难。

  4. 它们在应用程序的生命周期中携带状态。测试的另一个打击因为你可能最终会遇到需要订购测试的情况,这对于单元测试来说是一个很大的问题。为什么?因为每个单元测试应该独立于另一个。

其他提示

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

资源争用。

如果你有一些资源

1 )只能有一个实例,

2 )您需要管理该单个实例

您需要单身

没有很多例子。日志文件是最重要的。您不想只放弃单个日志文件。您想要正确刷新,同步和关闭它。这是必须管理的单个共享资源的示例。

你很少需要一个单身人士。他们不好的原因是他们感觉自己像全球并且他们已经全额付清了政府成员 设计模式 书。

当你认为自己需要全局时,你可能会犯一个糟糕的设计错误。

一些编码势利者瞧不起他们只是一个美化的全球化。就像许多人讨厌 goto 语句一样,还有其他人讨厌使用全局的想法。我见过几个开发人员为避免全局而花费了不少时间,因为他们考虑使用一个来承认失败。奇怪但真实。

在实践中, Singleton 模式只是一种编程技术,是您的概念工具包的有用部分。您可能会不时发现它是理想的解决方案,因此请使用它。但是使用它只是为了让你自夸使用设计模式就像拒绝使用它一样愚蠢,因为它只是一个全局

来自谷歌的Misko Hevery有一些关于这个话题的有趣文章......

Singletons is Pathological Liars 有一个单位测试示例,说明单例如何使得很难找出依赖链并启动或测试应用程序。这是滥用的一个相当极端的例子,但他提出的观点仍然有效:

  

单身人士只不过是全球化的国家。全局状态使得您的对象可以秘密地掌握未在其API中声明的内容,因此,单身人士会将您的API变成病态的骗子。

所有单身人士走到哪里指出依赖注入使得向需要它们的构造函数获取实例变得容易,这减轻了第一篇文章中谴责的糟糕的全局Singletons背后的潜在需求。

我认为混淆是由于人们不知道Singleton模式的真实应用这一事实。我不能强调这一点。 Singleton 包装全局变量的模式。单例模式应该仅用于保证在运行时期间存在一个且只有一个给定类的实例。

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

单身人士的一个相当不好的地方是你不能轻易地扩展它们。您基本上必须构建某种装饰模式或某些此类内容,如果您想要更改他们的行为。此外,如果有一天你想要有多种方法来做这件事,那么改变可能会非常痛苦,这取决于你如何布置你的代码。

有一点需要注意,如果你使用单身人士,试着把它们传递给任何需要它们的人,而不是让他们直接访问它们......否则如果你选择有多种方式来完成单身人士所做的事情,如果每个类在直接访问单例时嵌入依赖项,那么更改将会非常困难。

基本上是这样的:

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

而不是:

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

我认为这种模式称为依赖注入,通常被认为是一件好事。

与任何模式一样......考虑一下并考虑它在给定情况下的使用是否不合适......规则通常会被破坏,并且模式不应该毫不犹豫地应用。

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

从测试的角度来看,单身人士也是一个问题。他们倾向于使孤立的单元测试难以编写。 控制权倒置 (IoC)和 依赖注入 是旨在以面向对象的方式克服此问题的模式,有助于单元测试

垃圾收集环境中,单身人士很快就会成为一个问题。记忆管理。

还存在多线程场景,单身人士可能成为瓶颈以及同步问题。

使用静态方法实现单例。进行单元测试的人员可以避免静态方法,因为他们不能被模拟或存根。该网站上的大多数人都是单元测试的重要支持者。通常最受欢迎的公约是使用控制反转模式。

单身人士在群集方面也很糟糕。因为那样,你没有“恰好一个单身人士”。在您的申请中。

请考虑以下情况:作为开发人员,您必须创建一个访问数据库的Web应用程序。要确保并发数据库调用不会相互冲突,可以创建一个thread-save 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)用作全局变量。
  2. 依赖于单身人士的课程相对较难单独进行单元测试。

垄断恶魔和单身与非只读/可变的状态是'真正的'问题...

在阅读 单身是病态的骗子 作为建议 jason的答案 我遇到了这个小小的口,提供了最好提出了例的 如何 单是经常被滥用。

全球性是不好的,因为:

  • a.它会导致命名空间的冲突
  • b.它暴露了国家在一个不必要的时尚

当涉及到单身

  • a.明确OO的方式称呼他们,防止冲突,那么点。是不是一个问题
  • b.单身没有国家都(如工厂)并不是一个问题。单身状态可以再次落在两个类别,那些是不可改变的,或者一次编写和阅读很多(config/财产文件)。这些都不是坏。可变单身,这是一种参照持有人是那些你说的.

在最后一句他是参照的博客的概念单身都是骗子.

这如何适用于垄断?

开始游戏的垄断,第一次:

  • 我们建立的规则,使每个人都在同一页上
  • 每个人都给一个平等的开始,在开始游戏
  • 只有一套规则提出,以避免混淆
  • 该规则不允许改变整个游戏

现在,任何人都没有 真的 发挥的垄断,这些标准是理想的最好的。一个失败垄断是难以下咽,因为垄断是关于钱,如果你输了,你需要精心观看其他队员完成比赛,损失通常是迅速而破碎。因此,该规则通常得到扭转在某一点上来服务于自身利益的一些球员在牺牲他人。

所以你们玩大富翁的朋友鲍勃,乔,Ed.你是迅速建立您的帝国和消费市场份额以指数速度。你的对手正在削弱和你开始闻到血(比喻).你的朋友鲍勃把他所有的钱到gridlocking作为许多低价值的属性作为可能的,但他不接受较高的投资回报的方式,他期望的。鲍勃,如行程不幸,土地上的木板和被切除。

现在游戏,从友好骰子-滚严重的业务。鲍勃,已经取的例的失败和乔和Ed不要像'那家伙'.因此,作为领导者你,突然之间,变成敌人。乔和Ed一开始练习下表交易背后的金钱注射,被低估的房子交换和一般任何东西削弱了你作为一个球员,直到他们中的一个上升到顶端。

然后,而不是他们中的一个奖,该过程开始。突然,一个有限的一套规则成为一个移动的目标和游戏,堕落到这种类型的社会互动,这将弥补该基金会的每一位高级电视真人秀,由于幸存者。为什么,因为该规则正在改变并没有达成共识如何为什么他们应该代表,并且更重要的是,没有一个人作出的决定。每个球员在游戏,在这一点上,他/她自己的规则和随之而来的混乱,直到两个球员都太累了保持这种把戏,慢慢地放弃。

因此,如果一个规则手册为一个游戏准确地表示一个单一实例、垄断的规则手册将是一个例子的虐待。

这如何适用于编程?

除了所有的明显的线的安全和同步的问题的可变的单身礼物...如果你有一个数据集,是能够读/操纵通过多个不同来源的同时并存寿命期间的应用程序的执行,它可能是一个良好的时候后退一步,并问:"我使用正确类型的数据结构"。

就个人而言,我所看到的一个程序性虐待的单独通过使用它的某种扭曲的交叉线数据库存储在一个应用程序。曾在码直接,我可以证明,它是一个缓慢的(因为所有的螺纹锁需要使它的线-safe)和一个噩梦工作(因为不可预知的/有间歇性质的同步错误),以及几乎是不可能的测试"生产"的条件。当然,一个系统可以已经开发了使用询/令要克服的一些性能的问题,但这不会解决问题的测试,为什么要这么麻烦的时候了"真实"数据库已经可以完成相同的功能在一个更加强健的/可扩展的方式。

一个单一实例是 一个选择,如果你需要什么单独提供。写一个只读象的实例。同样的规则应该级的对象的属性/成员。

单身不是关于单一实例的!

不像其他的答案我不想谈论什么是错的单身,但以显示你有多么强大和真棒它们的时候使用正确的!

  • 的问题:单身可以是一个挑战,在多线程的环境
    解决方案:使用单线引导的过程初始化所依赖的单例。
  • 的问题:这是很难模拟单身.
    解决方案:使用的方法 工厂 模式对于嘲讽
    MyModel myModel = Factory.inject(MyModel.class); 你可以地图 MyModelTestMyModel 类继承了它,无处不在的时候 MyModel 将被注入你会得到 TestMyModel instread.
  • 的问题:单身可能会导致记忆韭菜,因为他们从来没有设置。
    解决方案:好了,处理他们!实施一个回你的应用程序中的适当地处理一个单一实例,应删除任何数据与他们最后:除去他们的工厂。

正如我指出在标题单独不是单个的实例。

  • 单身可以提高可读性:你可以看看你的课,看看什么单,它注射到找出它是什么的依赖关系。
  • 单身人士提高了维护:一旦你删除的依赖性,从一个类你只是删除一些单独的注射你并不需要去和编辑一个很大的链接,其他类,只要移动你的依赖周围的(这是臭码对我来说 @吉姆汉堡)
  • 单身人士提高记忆力和性能:当有些事情发生在你的应用程序,它需要一个长链的回调提供的,你是在浪费存储器和性能,通过使用单一切割的中间人,并提高性能和记忆的使用量(通过避免不必要地变量分配).

我的回答关于如何单身是坏是总是,"他们是很难做到的权利"。许多基本组成部分的语言是单(类、功能,名字空间,甚至运营商),为组件在其他方面的计算("localhost",default路线,虚拟文件系统等), 它不是通过事故。虽然他们造成麻烦和挫折时,他们还可以做很多事情的工作得更好。

两个最大的螺ups我看到的是:处理这样一个全球性的和未定义的单独关闭。

每个人都在谈论的单独的全局,因为它们基本上都是。然而,多(可悲的是,不是所有的)不良,在一个全球性的,不是从本质上是全球性的,但你如何使用它。同样适用于单身.实际上更是如此,"单一实例"真的不需要意味着"全球可访问".它更是一个自然的副产品,并给予所有糟糕的是,我们知道来自它,我们不应该在这样一种急于利用全球可访问性。一旦程序员看到一个单一实例,他们似乎总是直接通过它的实例的方法。相反,你应该找到它,就像任何其他目的。大多数代码甚至不应该知道它是处理一个单(松散耦合,对吗?).如果只有一小点代码的访问对象喜欢它是一个全球性的,很多的伤害是撤消。我建议强制执行它通过限制进入的实例功能。

单方面也是非常重要的。该决定性特征的单一实例就是"唯一",但事实真相是它是"只有一个"处于某种上下文/名称空间。他们通常是一种:每个线程、过程、IP地址或集群,但也可以是每个处理器、机器、语言namespace/类装入器/无论如何,子网、互联网等。

其它的、较不常见的,错误是忽略了单独生活方式。只是因为只有一个并不意味着一个单一实例是一些无所不能"一直是而且将永远是",也不是通常可取的(对象没有开始和结束违反了各类有用的假定在代码,应该采用只在最绝望的情况。

如果你避免那些错误,单身人士可能仍然是一个皮塔比特,它已准备好看到很多最糟糕的问题都明显减轻。想象一下,一个Java单,这是明确地定义为每一次类装入器(这意味着它需要一个线安全政策),与定义的创建和销毁方法和生命周期,决定了何时以及如何,他们得到援引,以及他的"实例"的方法具有包装的保护,所以它一般是通过访问其他非全球对象。仍然是一个潜在来源的麻烦,但肯定是很少的麻烦。

可悲的是,而不是教学的好例子如何做到单身.我们教坏的例子,让程序的运行使用了一段时间,然后告诉他们他们是一个糟糕的设计图案。

查看Wikipedia Singleton_pattern

  

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

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

  1. ^ Alex Miller。 我讨厌的模式#1:Singleton ,2007年7月
  2. ^ Scott Densmore。 为什么单身人士是邪恶的,2004年5月
  3. ^ Steve Yegge。 2004年9月单身人士被认为是愚蠢的
  4. ^ J.B. Rainsberger,IBM。 2001年7月明智地使用你的单身人士

单身人士本身并不好,但GoF的设计模式却是如此。唯一真正有效的论点是GoF设计模式在测试方面不适合,特别是如果测试是并行运行的。

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

  1. 确保将用作单例的类实现接口。这允许使用相同的接口

  2. 实现存根或模拟
  3. 确保Singleton是线程安全的。这是给定的。

  4. 单身人士本质上应该简单,不要过于复杂。

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

  6. 在测试期间并确保确定性行为,将单例类创建为单独的实例,作为实际类本身或实现其行为的存根/模拟,并将其原样传递给需要它的类。不要使用在测试期间创建需要单例的测试对象的类因子,因为它会传递它的单个全局实例,这会破坏目的。

  7. 我们在我们的解决方案中使用了Singletons,取得了很大的成功,可以确保并行测试运行流中的确定性行为。

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

单独应考虑只有在所有三个下列条件得到满足:

  • 所有权的单一的实例,无法合理的分配
  • 懒惰的初始化望
  • 全球访问不另外提供为

如果所有权的单个实例中,何时和如何初始化发生时,和全球性的访问都没问题,单是不够有趣的。

我想要的地址了4个点,在所接受的答案,希望有人可以解释为什么我错了。

  1. 为什么是隐藏依赖于你的码不好?已经有几十个隐藏的依赖(C runtime呼吁,OS API调,全球功能的话),以及单独的依赖很容易找到(寻找实例()).

    "使一些全球性的,以避免传递它是一种代码的气味。" 为什么不穿的东西周围,以避免使一个单一代的味道?

    如果你传递一个目通过10起,在一个呼叫堆只是为了避免一个单一实例是如此之大?

  2. 单一责任的原则:我认为这是一个比较含糊的,并且取决于你如何定义的责任。一个相关的问题是,为什么加入这个 具体 "责任"一类问题?

  3. 为什么不会传递一个对象一类使其更紧密地联接比使用对象为单一实例在课吗?

  4. 为什么它不会改变如何长的国家持续?单身可以被创造或摧毁了手,所以控制仍然存在,并且可以使生同样作为一个非单独对象的生命周期。

关于单元的测试:

  • 并不是所有的类需要单元 测试
  • 并不是所有的类需要单元 测试需要改变 执行单
  • 如果他们 需要进行单元测试和 需要改变的实现, 这是很容易改变的一个类 使用单独有的 单传递给它通过依赖 注射。

我不打算评论好/坏的论点,但我没有使用它们,因为春天来了。使用依赖注入几乎完全取消了我对单身人士,服务代理人和工厂的要求。我发现这是一个更高效,更干净的环境,至少对我的工作类型(基于Java的Web应用程序)而言。

模式没有任何内在错误,假设它被用于模型的某些方面,这是真正单一的。

我认为反弹是由于过度使用,反过来,这是因为它是最容易理解和实施的模式。

从纯粹主义的角度来看,单身人士是不好的。

从实际的角度来看,单身是一种权衡发展时间与复杂性的对比

如果你知道你的应用程序不会改变那么多,那么它们就可以了。只要知道如果你的需求以一种意想不到的方式发生变化,你可能需要重构一下(在大多数情况下这是非常好的)。

单身人士有时也会使单元测试复杂化。

Singleton是一种模式,可以像任何其他工具一样使用或滥用。

单身人士的不良部分通常是用户(或者我应该说不适当地使用单身人士来做它不设计的事情)。最大的罪犯使用单身人士作为虚假的全球变量。

使用单例编写代码时,例如记录器或数据库连接,然后发现需要多个日志或多个数据库,您就会遇到麻烦。

单身人士很难从他们转移到常规对象。

此外,编写非线程安全的单例也太容易了。

您应该将所有需要的实用程序对象从函数传递给函数,而不是使用单例。如果将它们全部包装到辅助对象中,可以简化这一点,如下所示:

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

单身人士的问题是范围扩大的问题,因此耦合。无可否认,在某些情况下,您确实需要访问单个实例,并且可以通过其他方式完成。

我现在更喜欢围绕控制反转(IoC)容器进行设计并允许寿命由容器控制。这使得依赖于实例的类的好处是不知道存在单个实例的事实。单身人士的生命周期可以在未来改变。我最近遇到的这样一个例子是从单线程到多线程的简单调整。

FWIW,如果它是PIA,当你尝试进行单元测试时,那么当你尝试调试,错误修复或增强它时,它会进入PIA。

Chris Reath在编码无评论上有关此主题的最新文章。

注意:没有评论的编码不再有效。但是,链接到的文章已被其他用户克隆。

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

还有一个关于单身人士的事情还没有人说过。

在大多数情况下,“单身性”是某个类的实现细节而不是其接口的特征。控制容器的反转可以隐藏类用户的这种特征;你只需要将你的类标记为单例(例如,在Java中使用 @Singleton 注释),就是这样; IoCC将完成剩下的工作。您不需要提供对单例实例的全局访问权限,因为访问权限已由IoCC管理。因此,IoC Singletons没有任何问题。

与IoC Singletons相反的GoF Singletons应该暴露出“单身性”。在接口中通过getInstance()方法,以便他们遭受上述所有事情。

单身人士并不坏。当你创造一种全球唯一的非全球唯一的东西时,这是唯一的不好。

然而,存在“应用范围服务”。 (想想使组件交互的消息传递系统) - 这个CALLS用于单例,“MessageQueue”和 - 具有方法“SendMessage(...)”的类。

然后,您可以从所有地方执行以下操作:

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

当然,这样做:

MessageQueue.Current.RegisterReceiver(本);

在实现IMessageReceiver的类中。

太多人在单件模式中放置非线程安全的对象。我见过DataContext的例子( LINQ&nbsp;到SQL 尽管DataContext不是线程安全的,并且纯粹是一个工作单元对象,但是以单例模式完成。

因为它们基本上是面向对象的全局变量,所以通常可以这样设计类,以便不需要它们。

当几个人(或团队)达到类似或相同的解决方案时,就会出现一种模式。很多人仍然使用原始形式的单身人士或使用工厂模板(在Alexandrescu的现代C ++设计中进行了很好的讨论)。管理对象生命周期的并发性和难度是主要障碍,前者可以按照您的建议轻松管理。

像所有选择一样,辛格尔顿有着相当大的起伏。我认为它们可以适度使用,特别是对于在应用程序生命周期内存活的对象。它们类似于(可能是)全局的事实可能会引发纯粹主义者。

首先,一个班级及其合作者应该首先执行他们的预期目的,而不是专注于deoendents。生命周期管理(当实例在超出范围时被修饰)不应该成为工作人员责任的一部分。最常见的做法是使用依赖注入来创建或配置新组件来管理依赖关系。

软件通常变得更复杂,因此拥有“单身”的多个独立实例是有意义的。不同国家的阶级。在这种情况下,提交代码以简单地获取单例是错误的。使用Singleton.getInstance()对于小型简单系统可能没问题,但是当一个人可能需要同一个类的不同实例时,它不能工作/扩展。

任何类都不应该被认为是单例,而应该是它的用法或它如何用于配置依赖项的应用。对于一个快速而讨厌的事情,这并不重要 - 只是说硬编码说文件路径无关紧要,但对于更大的应用程序,这些依赖关系需要通过使用DI以更合适的方式进行分解和管理。

单身人士在测试中引起的问题是他们的硬编码单一用例/环境的症状。测试套件和许多测试都是单独的,并且与单独硬编码不兼容。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top