单身人士真的那么糟糕吗?

jos*_*erk 51 singleton design-patterns

可能重复:
单身人士有什么不好的?

可以理解的是,许多设计模式在某些情况下可能被滥用,就像妈妈总是说:" 太多好事并不总是好的! "

我注意到这些天,我经常使用Singletons,而且我担心自己可能会滥用设计模式,并且越来越深入地研究一种不良习惯的习惯.

我们正在开发一个Flex应用程序,当用户使用它时,该应用程序在内存中保留了相当大的分层数据结构.用户可以按需加载,保存,更改和刷新数据.

这些数据通过Singleton类集中,该类聚合了几个ArrayCollections,Arrays,value对象以及通过getter和setter公开的一些其他本机成员变量.

要从应用程序的任何位置获取对数据的引用,我们执行整个Model.getInstance()方法类型的事情,我确信每个人都熟悉.这确保了我们始终掌握相同的数据副本,因为在我们设计时,我们说在应用程序生命周期中只允许存在一次实例.

从这个中央数据存储库中,我们可以轻松地调度属性更改事件,并且可以有多个引用中央数据的UI组件,更新其显示以反映已发生的数据更改.

到目前为止,这种方法已经有效并且证明对我们的环境非常实用.

然而,我发现,在创建新课程时,我有点过分了.问题应该是一个类是Singleton,还是应该以其他方式管理,例如可能使用工厂,往往有点变得有点困难,有点不确定.

我在哪里画单线?是否有一个很好的指导方针来决定何时使用单身人士以及何时远离他们.

另外,有人可以推荐一本关于设计模式的好书吗?

jal*_*alf 109

是的,单身人士很糟糕.它们很糟糕,因为它们为你所做的只是结合了两个属性,每个属性在95%的时间都是坏的.(这意味着平均而言,单身人士在99.75%的时间里表现不佳;))

由GoF定义的单例是一种数据结构:

  1. 授予对象的全局访问权限
  2. 强制只能存在一个对象实例.

第一个通常被认为是一件坏事.我们不喜欢全局变量.第二个是更微妙,但一般来说,实际上没有任何情况下这是一个合理的强制执行限制.

有时,只有一个对象实例才有意义.在这种情况下,您选择只创建一个.您不需要单例来强制执行它.

通常情况下,即使只有一个实例"有意义",事实证明它根本没有意义.迟早,你需要不止一个记录器.或者多个数据库.或者您将不得不为每个单元测试重新创建资源,这意味着我们必须能够随意创建它们.在我们理解后果之前,它过早地从我们的代码中消除了灵活性.

单例隐藏依赖关系并增加耦合(每个类都可能依赖于单例,这意味着除非我们还重用所有单例,否则该类不能在其他项目中重用),并且因为这些依赖关系不是立即可见的(作为函数/构造函数参数) ),我们没有注意到它们,通常在我们创建它们时不会考虑它们.只需拉入一个单例就可以了,它几乎就像一个局部变量一样,所以我们倾向于在它们存在时使用它们很多.这使他们几乎不可能再次删除.你结束了,也许不是意大利面条代码,而是意大利面依赖图.迟早,你失控的依赖关系将意味着单身人士开始依赖彼此,然后在尝试初始化时获得循环依赖关系.

他们使单元测试非常困难.(如何测试在单个对象上调用函数的函数?我们不希望运行实际的单例代码,但是我们如何防止这种情况?

是的,单身人士很糟糕.

有时,你真的想要全球化.然后使用全局而不是单身.

有时,非常非常非常罕见,你可能有一种情况,创建一个类的多实例是一个错误,它可以没有导致错误进行.(关于我能想到的唯一一个案例,即使这是设计的,如果你代表一些硬件设备.你只有一个GPU,所以如果你要将它映射到代码中的一个对象,它会理解只有一个实例可以存在).但是,如果您发现自己处于这种情况(并且再次强调,多个实例导致严重错误的情况,而不仅仅是"我无法想到多个实例的任何用例"),那么执行该约束,但不要使对象全局可见.

每个这两个属性可以是有用的,在极少数情况下.但我想不出一个案例,他们的组合将是一件好事.

不幸的是,很多人都认为"Singletons是符合OOP标准的全局变种".不,他们不是.除了介绍其他一些完全不相关的问题之外,他们仍然遇到与全局问题相同的问题.绝对没有理由比普通的全球更喜欢单身人士.

  • 为什么我不想在单元测试中使用两个独立的池?为什么我不希望两个单独的池用于单独的数据库?如果我有两个不应允许彼此饿死的独立任务,为什么我不想要两个独立的池?一个任务可能会占用自己池中的所有连接,所以我想为另一个保留一些连接.但即使你*做*绝对肯定只想要一个游泳池,为什么你需要一个单身来强制执行呢?您经常*意外地*创建新的连接池吗?当然不是.如果你只需要一个,只需*创建一个. (45认同)
  • 执行单身人士有很多次是一个关键的想法.想想数据库连接池.您可能不需要两个单独的池,因为当可能存在某些池时,您可能会打开连接. (15认同)
  • 问题1是真正的杀手,因为一旦你把它全部洒在代码上就很难解决. (4认同)
  • +1不能说得更好.究竟我的感受和教育我的同事们.许多要点也适用于单稳态模式和(ab)全局静态方法的使用. (3认同)
  • @user另一方面,你的同事常识可能和你的一样好.您编写了旨在存在一个实例的对象.他创建了第二个实例.你是谁说*他错了*?他遇到了一种情况,即创建第二个实例是有意义的,你没有预料到.如果它是一个单身人士,那么你的同事就无法做他认为合理的事情.我的论点是"应该存在第二个实例吗?" 是一个只有在使用*时才能合理地做出的决定,而不是最初设计的时候. (3认同)
  • 我想我应该指出我在这个答案后几个月在博客上写的内容:http://jalf.dk/blog/2010/03/singletons-solving-problems-you-didnt-know-you-never-had-自1995 / (2认同)

man*_*eka 39

要记住的关键是设计模式只是帮助您理解抽象概念的工具.一旦掌握了这种理解,将自己专门限制在书中的"食谱"是毫无意义的,并且会损害您编写最适合您目的的代码的能力.

也就是说,阅读像GoF这样的书会给你提供更多思考问题的方法,这样当你自己实现某些东西的时候,你将有更广泛的视角来解决问题.

在你的情况下,如果在每种情况下使用单身人士都有意义,那么就去吧.如果它"适合"并且你必须以一种笨重的方式实现它,那么你需要提出一个新的解决方案.强制不完美的图案有点像在圆孔中敲击方形钉.

鉴于你说"这种方法已经有效并且证明对我们的环境非常实用",我认为你做得很好.

这里有一些好书:

Gang of Four Book - 设计模式的经典书籍

头脑设计模式 - 我听说过少数人推荐的这种模式

  • GoF书的大部分内容都很好,但就单身人士而言,我认真地认为他们一定是在吸食非法的东西.要么是这样,要么是老程序程序员(他们真的想要他们的全局变量)的让步,为了赢得他们OOP("嘿看,你也可以在OOP中制作全局变量!我们只称他们为单身人士") (9认同)
  • 是的,我将紧跟jalf所说的,谨防将单例用作全局变量的oop版本。 (2认同)

kgr*_*ffs 14

软件开发人员似乎分成两个阵营,这取决于他们是赞成理想主义的编码风格还是实用的编码风格:

  • 理想主义:永远不要使用单身模式.
  • 务实:避免单身模式.

就个人而言,我赞成务实的做法.有时违反规则是有道理的,但前提是你真正了解自己在做什么,并愿意接受相关的风险.如果您对以下关于特定用例的问题回答"是",则单例模式可以产生一些实际好处.

  • 单身是你的应用程序的外部吗?数据库,排队服务和ESB都是单例模式的完全有效的宏示例.
  • KISS:你的整个应用仅限于2-3个内部单身人士吗?
  • DRY:那些单身人士本身是否具有全球性,因此不得不在你的应用程序中几乎每个对象中引用参考文献?(例如,记录器或组件介体)?
  • 您的单身人士是否仅依赖于彼此和/或操作环境?
  • 您是否确保了每个单例的正确启动和关闭顺序,包括内存管理注意事项?例如,"Grand Central"样式的线程池可能需要在main()中具有实例Run()和Shutdown()方法,以便保证任务仅在它们操作的对象处于有效状态时运行.

  • 让你的单身成瘾更容易被社会接受的好方法。“我避免使用它们,我只在有意义的地方使用它们”。不,你没有。根据您自己的描述,您可以在任何地方使用它们,任何时候替代方案都只需要少量工作。区别不在于“理想主义”和“务实”,而在于那些*实际上*试图避免单例的人(例如,我的数据库连接*不需要*单例)和*说*的人之间的区别他们“避免”使用它们,而实际上它们的意思只是“当我认为这是正确的做法时我会使用它们” (2认同)

Cad*_*oux 9

单身人士不会杀死程序,程序员会杀死程序.

像任何编程结构一样,如果使用得当,你就不会在脚下射击.

推荐的书很好,但是当你可以选择使用Singleton时,它们并不总能提供足够的经验.

只有在你需要拥有多个实例的时候发现Singleton是一个糟糕的选择时才会出现这种体验,而且突然之间,你在各地注入对象引用时遇到了很多麻烦.

有时最好继续使用对象引用,但是如果你必须将它重构为不同的设计,那么你使用Singleton的事实确实有助于确定你将遇到的问题的范围.我认为这是一件非常好的事情:即只有一个班级(即使设计不合理)也能提供一些能力来看到改变班级的效果.