在全局变量和单例是“好”还是“坏”方面,C# 和 C++ 之间确实没有区别。
您概述的解决方案在 C# 和 C++ 中同样糟糕(或良好)。
你似乎发现的只是不同的人有不同的看法。一些 C# 开发人员喜欢使用单例来完成类似的事情。一些 C++ 程序员也有同样的感觉。
一些 C++ 程序员认为单例是一个糟糕的主意,而且……一些 C# 程序员也有同样的感觉。:)
微软给出了许多关于如何编码的坏例子。永远不要仅仅因为它在盒子上写着微软就接受他们的示例代码作为“良好实践”。重要的是代码,而不是其背后的名称。
现在,我对单身人士的主要不满并不是他们的全球方面。
像大多数人一样,我通常不喜欢和不信任全局变量,但我不会说永远不应该使用它们。在某些情况下,使某些东西可以全局访问会更方便。它们并不常见(我认为大多数人仍然过度使用全局变量),但它们存在。
但是单例的真正问题是它们对您的代码施加了不必要且通常有害的约束:它们阻止您创建对象的多个实例,就好像您在编写类时知道如何更好地使用它实际用户会。
当您编写一个类时,例如,PluginService
正如您在评论中提到的那样,您当然对您计划如何使用它有所了解。你可能认为“它的一个实例应该是全局可访问的(这是有争议的,因为许多类不应该访问插件服务,但我们假设我们现在确实希望它是全局的)。你可能认为“我不能想象一下为什么我想要两个实例”。
但问题是当你采取这个假设并积极阻止创建两个实例时。
如果从现在开始的两个月后,您发现需要创建两个 PluginServices 怎么办?如果您在编写类时采用了简单的方法,并且没有在其中构建不必要的约束,那么您现在也可以采用简单的方法,只需创建两个实例即可。
但是,如果您采取了编写额外代码以防止创建多个实例的困难路径,那么您现在必须再次采取困难路径:现在您必须返回并更改您的类。
除非你有理由,否则不要在你的代码中设置限制:如果它使你的工作更容易,那就继续做吧。如果它可以防止对课程的有害滥用,那就去做吧。
但在单例的情况下,这两者都没有:您为自己创造额外的工作,以防止可能完全合法的使用。
您可能有兴趣阅读我为回答单身问题而写的这篇博文。
但要回答如何处理您的具体情况的具体问题,我会推荐以下两种方法之一:
- “纯粹”的方法是创建一个
ServiceLocator
非全球性的。将其传递给需要查找服务的人。根据我的经验,您可能会发现这比听起来要容易得多。您往往会发现在许多不同的地方实际上并不需要它,因为您认为它会是。它给你一个动力去解耦代码,最小化依赖,确保只有那些真正需要ServiceLocator 的人才能访问它。这很健康。
- 或者有一种务实的方法:创建
ServiceLocator
. 任何需要它的人都可以使用它,而且毫无疑问如何找到它——毕竟它是全球性的。但不要让它成为单例。让创建其他实例成为可能。如果您永远不需要创建另一个实例,那么就不要这样做。但这让大门敞开,因此如果您最终需要另一个实例,您可以创建它。
在许多情况下,您最终需要一个您认为只需要一个实例的类的多个实例。围绕某些硬件的配置/设置对象、记录器或包装器都是人们经常所说的“这显然应该是一个单例,拥有多个实例是没有意义的”,并且在每种情况下,它们都是错误的. 在很多情况下,您需要此类类的多个实例。
但最普遍适用的场景很简单:测试。
您要确保您的 ServiceLocator正常工作。所以你想测试它。
如果是单例,那真的很难做到。一个好的测试应该在原始、隔离的环境中运行,不受之前测试的影响。但是一个单例在应用程序的持续时间内存在,所以如果你有多个 ServiceLocator 测试,它们都会在同一个“脏”实例上运行,所以每个测试都可能影响下一个测试看到的状态。
相反,每个测试都应该创建一个新的、干净的 ServiceLocator,这样它们就可以准确地控制它所处的状态。为此,您需要能够创建该类的实例。
所以不要让它成为单例。:)