什么是替代单身
我们有一个保存应用程序configuration信息的类。 它曾经是一个单身人士。 经过一些build筑审查,我们被告知要删除单身人士。 我们确实看到了在unit testing中不使用单例的一些好处,因为我们可以一次testing不同的configuration。
如果没有单例,我们必须在代码中的任何地方传递实例。 它变得如此混乱,所以我们写了一个单例包装器。 现在我们将相同的代码移植到PHP和.NET,我想知道是否有更好的模式,我们可以使用configuration对象。
Googletesting博客有一系列关于避免Singleton(为了创build可testing的代码)的条目。 也许这可以帮助你:
- 使用dependency injection来避免单例
- 单身人士是病理性的说谎者
- 单身人士的根本原因
- 所有的单身人士都去了哪里?
最后一篇文章详细解释了如何将新对象的创build移动到工厂中,这样就可以避免使用单例。 值得肯定的阅读。
简而言之,我们将所有新的操作员移到一个工厂。 我们将所有类似生命周期的物体分成一个工厂。
最好的方法是使用工厂模式。 当你构造一个新的类的实例(在工厂中)时,你可以将“全局”数据插入到新构造的对象中,或者作为单个实例的引用(存储在工厂类中),或者通过复制相关数据导入新的对象。
然后你的所有对象将包含曾经居住在单身人士的数据。 我不认为总体上有很大差异,但它可以使你的代码更容易阅读。
我可能会在这里说明一些明显的问题,但是为什么不能使用像Spring或Guice这样的dependency injection框架呢? (我相信Spring现在也可以用于.NET)。
这样一来,框架就可以拥有一个configuration对象的副本,而你的bean(服务,DAO等等)不必担心查找它。
这是我通常采取的方法!
如果你使用Spring Framework ,你可以创build一个普通的bean。 默认情况下(或者如果你明确地设置scope="singleton"
),只创buildbean的一个实例,并且每当bean在依赖中被使用或者通过getBean()
获取时,这个实例被返回。
你得到了单一实例的优点,没有Singleton模式的耦合。
另一种方法是传递你需要的东西,而不是要求一个对象的东西。
不要将责任积累到一个configuration对象上,因为它会以一个非常难以理解和脆弱的大对象结束。
例如,如果您需要另一个参数到特定的类,则更改Configuration
对象,然后重新编译所有使用它的类。 这有些问题。
尝试重构您的代码,以避免一个常见的,全局的和大的Configuration
对象。 仅将必需的parameter passing给客户端类:
class Server { int port; Server(Configuration config) { this.port = config.getServerPort(); } }
应该被重构为:
class Server { public Server(int port) { this.port = port; } }
dependency injection框架在这里会有很大的帮助,但并不是非常必要的。
是只包含静态方法和字段的类吗? 我不确定你的情况到底是什么,但是可能值得研究。
取决于正在使用的工具/框架等。 使用dependency injection/ ioc工具,人们仍然可以通过di / ioc容器使用单例行为来获得单例性能/优化(例如IConfigSettings接口),只需创build一个类的实例即可。 这仍然可以取代testing
或者,可以使用工厂来创build类,并在每次请求时返回相同的实例 – 但要进行testing,可以返回一个存根/模拟版本
审查configuration为callback接口的可能性。 所以你的configuration敏感的代码将看起来:
MyReuseCode.Configure(IConfiguration)
系统初始化代码如下:
Library.init(MyIConfigurationImpl)
您可以使用dependency injection框架来缓解传入configuration对象的痛苦。 一个体面的是ninject ,它有使用代码而不是xml的优势。
您可以使用静态方法完成单例的相同行为。 Steve Yegge在这篇文章中解释得非常好。
也许不是很干净,但你也许可以将你想要改变的信息传递给创build单例的方法 – 而不是使用
public static Singleton getInstance() { if(singleton != null) createSingleton(); return singleton; } }
你可以直接在应用程序启动时(以及在unit testing的setUp-Methods中createSingleton(Information info)
调用createSingleton(Information info)
)。
单身不是邪恶的,但devise模式是有缺陷的。 我有一个类,我只想在运行时创build它的单个实例,但要在unit testing期间创build多个孤立的实例,以确保确定性的结果。
使用Spring等DI,是一个非常好的select,但不是唯一的select。