有没有任何可行的替代GOF Singleton模式?
面对现实吧。 Singleton模式是一个非常有争议的话题,围栏的两边都是程序员。 有些人觉得辛格尔顿不过是一个荣耀的全球variables,还有一些人是按照模式发誓并不断使用它的。 不过,我不希望辛格尔顿的争议是我的问题的核心。 每个人都可以进行拔河比赛,看看谁赢了我所关心的 。 我想说的是,我不相信有一个正确的答案,我不是故意激怒党派争吵。 当我问这个问题时,我只是对单例替代品感兴趣:
他们是否是GOF Singleton模式的特定select?
例如,过去当我使用单例模式的时候,很多时候我只想保留一个或几个variables的状态/值。 然而,variables的状态/值可以在使用静态variables的类的每个实例化之间保留,而不是使用单例模式。
你有什么其他的想法?
编辑:我真的不希望这是另一篇关于“如何正确使用单身人士”。 再次,我正在寻找方法来避免它。 为了好玩,好吗? 我想我是以最好的电影预告片的声音问纯粹的学术问题,“在一个没有单身的平行宇宙中,我们能做什么?
Alex Miller在“ Patterns I Hate ”中引用了以下内容:
“当一个单身人士看起来像是答案时,我发现通常更聪明的是:
- 创build一个接口和你的单例的默认实现
- 在系统的“顶部”构build一个默认实现的单个实例。 这可能是在Springconfiguration中,或在代码中,或根据您的系统以各种方式定义。
- 将单个实例传递给每个需要它的组件(dependency injection)
要理解正确的解决办法单身人士,你需要了解什么是单身人士(和一般全球国家)的错误:
单身人士隐藏依赖关系。
为什么这很重要?
因为如果你隐藏依赖关系,你往往会失去耦合量。
你可能会争辩说
void purchaseLaptop(String creditCardNumber, int price){ CreditCardProcessor.getInstance().debit(creditCardNumber, amount); Cart.getInstance().addLaptop(); }
比…更简单
void purchaseLaptop(CreditCardProcessor creditCardProcessor, Cart cart, String creditCardNumber, int price){ creditCardProcessor.debit(creditCardNumber, amount); cart.addLaptop(); }
但是至less第二个API明确地说明了方法的合作者是什么。
因此,解决方法Singletons不是使用静态variables或服务定位器,而是将Singleton类更改为实例,这些实例在有意义的范围中实例化,并注入到需要它们的组件和方法中。 你可以使用一个IoC框架来处理这个问题,或者你也可以手动完成,但重要的是摆脱你的全局状态,并明确依赖和协作。
我遇到的最好的解决scheme是使用工厂模式来构build您的类的实例。 使用该模式,可以确保只有一个类的实例在使用它的对象之间共享。
我虽然pipe理起来会很复杂,但是在阅读了这篇博客“所有的单身人士都去了哪里? ,这似乎很自然。 另外,隔离你的unit testing有很大的帮助。
总之,你需要做什么? 每当一个对象依赖于另一个对象时,它只会通过它的构造函数(在你的类中没有新的关键字)接收它的一个实例。
class NeedyClass { private ExSingletonClass exSingleton; public NeedyClass(ExSingletonClass exSingleton){ this.exSingleton = exSingleton; } // Here goes some code that uses the exSingleton object }
然后,工厂。
class FactoryOfNeedy { private ExSingletonClass exSingleton; public FactoryOfNeedy() { this.exSingleton = new ExSingletonClass(); } public NeedyClass buildNeedy() { return new NeedyClass(this.exSingleton); } }
因为你只会实例化你的工厂一次,所以会有一个exSingleton实例化。 每次调用buildNeedy时,NeedyClass的新实例都将与exSingleton捆绑在一起。
我希望这有帮助。 请指出任何错误。
Spring或者其他IoC容器在这方面做得相当不错。 由于这些类是在应用程序本身之外创build和pipe理的,因此容器可以创build简单的类并将其注入到需要的位置。
你不应该走出自己的方式来避免任何模式。 模式的使用或者是devise决定,或者是自然而然的(它只是落在原地)。 在devise系统时,可以select使用模式或不使用模式。 但是,你不应该为了避免任何最终成为deviseselect的东西而走出困境。
我不避开Singleton模式。 要么它是适当的,我使用它或不合适,我不使用它。 我相信它就这么简单。
单身人士的适当性(或缺乏)取决于情况。 这是必须作出的devise决定,必须理解(和logging)该决定的后果。
存在单例模式是因为有时需要单个对象来提供一组服务 。
即使是这种情况,我仍然考虑通过使用表示实例的全局静态字段/属性来创build单例的方法,这是不恰当的。 这是不合适的,因为它在静态字段和对象之间的代码中创build了依赖关系,而不是对象提供的服务。
因此,我不推荐使用经典的singleton模式,而是使用带有服务容器的服务“like”模式,而不是通过静态字段来使用你的单例,而是通过请求所需服务types的方法获得对它的引用。
*pseudocode* currentContainer.GetServiceByObjectType(singletonType) //Under the covers the object might be a singleton, but this is hidden to the consumer.
而不是单一的全球
*pseudocode* singletonType.Instance
这样,当你想把一个对象的types从singleton改成别的东西时,你就可以轻松地做到这一点。 另外作为一个附加的好处,你不必为每个方法都分配对象实例。
另请参见控制反转(Inversion of Control) ,其思想是通过直接向消费者公开单例,在消费者和对象实例之间创build依赖关系,而不是由对象提供的对象服务。
我的意见是尽可能隐藏单身模式的使用,因为它不总是可以避免它,或者是可取的。
Monostate(在Robert C. Martin的敏捷软件开发中描述)是单身人士的替代scheme。 在这个模式中,类的数据都是静态的,但是getters / setter是非静态的。
例如:
public class MonoStateExample { private static int x; public int getX() { return x; } public void setX(int xVal) { x = xVal; } } public class MonoDriver { public static void main(String args[]) { MonoStateExample m1 = new MonoStateExample(); m1.setX(10); MonoStateExample m2 = new MonoStateExample(); if(m1.getX() == m2.getX()) { //singleton behavior } } }
Monostate与单例有类似的行为,但是这样做的方式是程序员不一定意识到正在使用单例。
如果您使用Singleton来表示单个数据对象,则可以传递一个数据对象作为方法参数。
(但是,我认为这是使用单身人士的错误方法)
如果你的问题是你想保持状态,你需要一个MumbleManager类。 在开始使用系统之前,客户端会创build一个MumbleManager,其中Mumble是系统的名称。 国家是通过这个保留的。 机会是你的MumbleManager将包含一个持有你的状态的财产袋。
这种types的样式感觉非常类似C,而不是很像对象 – 你会发现定义你的系统的对象都会有一个对同一个MumbleManager的引用。
使用普通对象和工厂对象。 工厂负责监视实例和简单对象的细节,仅仅通过configuration信息(例如它包含的)和行为来进行。
实际上,如果你从头开始devise避免Singeltons,你可能不需要通过使用静态variables来避免使用Singletons。 在使用静态variables的时候,你也会创build一个Singleton,唯一的区别就是你创build的是不同的对象实例,但是在内部它们都像使用Singleton一样工作。
你可以给一个详细的例子,你使用一个单身人士或单身人士当前正在使用,你试图避免使用它? 这可以帮助人们find一个更奇特的解决scheme,如何在没有Singleton的情况下处理这种情况。
顺便说一句,我个人没有单身的问题,我不明白其他人有关单身的问题。 我没有看到他们的坏消息。 也就是说,如果你不滥用他们。 每一种有用的技术都可能被滥用,如果被滥用,会导致负面结果。 另一种通常被滥用的技术是inheritance。 仍然没有人会说inheritance是不好的,只是因为有些人可怕地滥用它。
就我个人而言,更实际的方式来实现像单例那样行为的东西是使用完全静态的类(静态成员,静态方法,静态属性)。 大多数时候我以这种方式实现它(从用户的angular度来看,我不能想到任何行为差异)
我认为警察单身人士最好的地方是在课堂devise阶段。 在这个阶段,你应该能够映射出类之间的相互作用,看看是否有绝对的东西,绝对要求在应用程序的任何时候,这个类只有一个实例存在。
如果是这样,那么你有一个单身人士。 如果你在编码过程中为了方便扔单身人士,那么你应该真的重新考虑你的devise,并停止编码所说的单身人士:)
是的,“警察”是我在这里的意思,而不是“避免”。 单例不是要避免的东西(就像goto和全局variables不能被避免一样)。 相反,你应该监视它的使用,并确保它是最好的方法来得到你想要的有效完成。
我主要使用单身作为“方法容器”,根本没有状态。 如果我需要与许多类共享这些方法,并希望避免实例化和初始化的负担,我创build一个上下文/会话并初始化那里的所有类; 所有涉及会议的内容都可以访问由此包含的“单身人士”。
由于没有在强烈的面向对象的环境(例如Java)中进行编程,所以我并不完全了解讨论的复杂性。 但是我已经在PHP 4中实现了一个单例。我做了一个创build“黑盒”数据库处理程序的方法,它自动初始化,并且不必在不完整和有点破碎的框架中上下传递函数。
在阅读了一些单例模式的链接之后,我不完全相信我会以相同的方式再次实现它。 真正需要的是具有共享存储的多个对象(例如实际的数据库句柄),这几乎就是我所说的。
像大多数模式和algorithm一样,使用单例“仅仅因为它很酷”就是错误的事情。 我需要一个真正的“黑匣子”的电话,看起来很像一个单身人士。 国际海事组织就是这样解决这个问题的方法:注意这个模式,但是也要看它的范围更广,实例在什么层次上是独一无二的。
你是什么意思,我的技术是什么来避免呢?
为了“避免”,这意味着我遇到很多情况,单身模式自然是合适的,因此我必须采取一些措施来化解这些情况。
但是没有。 我不必避免单身模式。 它根本不会出现。