我一遍又一遍地阅读,出于各种原因应该避免使用单例。我想知道如何正确处理类表示唯一系统资源的情况。例如,使用 SDL 的音频输出类。由于SDL_OpenAudio一次只能打开一次,因此拥有多个这种类型的对象是没有意义的,在我看来,防止意外制作多个对象实际上会很好。
只是想知道有经验的程序员对此有何看法,我是否错过了另一种选择?
Singleton 的一个问题是,它的使用经常会破坏控制反转原则(或 SOLID 方面的依赖反转)。
Singleton 不仅会阻止创建多个对象,还会公开此对象,以便从代码中的任何位置访问此对象。这很糟糕,因为如果您决定更改创建/访问对象的方式,则必须更改代码中的所有位置,例如,期望它们应该通过静态SingletonClass.GetInstance()
访问它。
此外,当您实现单元测试时,通常需要使用模拟而不是真实对象。在您的情况下,如果您希望对某个模块进行单元测试,并且如果该模块通过SingletonClass.GetInstance()
访问真实的音频输出,则很难用存根替换真实的音频输出。
另一方面,如果您的模块通过依赖注入获取音频输出对象(例如,作为传递给构造函数的参数),那么在测试时,您可以注入一个实现相同接口的存根而不是真正的音频输出对象。
当然,在注入此类对象的级别上,可以使用单例来确保一次只有一个实例。重点是,底层代码不应该关心可以有多少对象或如何获取它们 - 它只适用于注入的内容。
因此,作为底线,如果您确实认为需要 Singleton,则可以使用它,但不允许将其作为全局状态访问。
明智地使用设计模式是一个棘手的问题,需要大量的练习。
我见过很多人使用Singleton,只是因为它"有点"适合他们需要做的事情。这通常会导致灾难,例如在多线程环境中,或者只是为了隐藏设计缺陷,并且以后需要完全重新设计系统。
我想说的是,在考虑单例时,您需要考虑几件事:
- 我真的只需要我的对象的一个实例吗?我不是想隐藏一些设计缺陷吗?
- 可以全局访问我的对象是否有意义?
我想说的是,不要花太多时间在上面:它可能并不总是最好的解决方案,但在某些情况下,这仍然是一个可接受的解决方案,除非你处于多线程环境中,否则你可能很少会遇到问题。这主要是一种设计选择。
如果你想获得更多关于设计模式的知识,我建议这本书,它几乎是关于这个主题的参考。
只要:
- 您不在多线程环境中(多个线程访问单一实例)
- 您处于多线程环境中,并且在单例中提供了适当的保护
- 单一实例对象未在多个动态加载模块中定义