我在使用picocontainer时看到了这一点。他们说你必须避免单身。 因为Singleton模式使得类(以及可能依赖于它的所有其他类)几乎不可能是可测试的。子类化或为Singleton类创建模拟对象非常困难。
但是如果你绝对需要它,那么测试和子类化问题是否有解决方法?
答案 0 :(得分:5)
难以测试单例的是执行单例的代码(意味着public static MySingleton getInstance() {...}
样板)。使用像Picocontainer或Guice或Spring这样的控件反转容器可以从对象中删除这个问题,所以现在:
它可以被实例化,并且可以在测试中将协作者插入其中而不会出现问题。
调用单例的代码不必知道它正在查找哪个类(如果必须调用静态方法,则需要知道它。)
我将picocontainer网站上的建议解释为与此类似。他们告诉您的是,让我们的容器为您管理组件的范围,不要将范围执行代码硬连接到它们中。
答案 1 :(得分:1)
如果你必须有单身人士:
示例:
interface IBankApi
{
public void MakeDeposity(int accountNumber, int dollarAmount);
// ...
}
public class RealBankApi : IBankApi { ... }
// startup code
serviceLocator.Register<IBankApi>(new RealBankApi());
// code using the API
serviceLocator.Resolve<IBankApi>().MakeDeposit(...);
// test code setup
class FakeBankApi : IBankApi { ... }
serviceLocator.Register<IBankApi>(new FakeBankApi());
答案 2 :(得分:1)
使用IOC(控制反转)而不是在第一次使用时启动的单例也有其他原因。
Singleton-initialisation可能会遭遇(着名的)多线程问题,其中两个线程同时第一次尝试访问它。后续访问很可能是正确同步的,但第一个很难做到。
我发现使用IOC的另一个巨大优势是初始化时可能发生错误。你不希望这种情况发生在“第一次使用”,你想在早期就知道这个失败,当然这样更容易处理错误。
最后,关于测试,IOC提供了完美的模型来隔离组件,根据需要替换它们,并以更灵活的方式将不同的组合结合在一起,从而为单元测试和集成测试提供完美的线束,作为一个很好的回滚机制,根本不需要恢复任何代码。
单身经常被使用的一般原因不是为了一个人而是为了全球性。如果您的项目得到了正确管理,那么您将拥有一个全局对象,所有其他对象都会“注册”(因此您的IOC模型会将其挂起),并且在全局可用的同时仍可配置。