单例模式下的通用工厂类

8
我读了Jim Hyslop 和 Herb Sutter的文章 抽象工厂,模板风格。该工厂被实现为Singleton。他们提供了RegisterInFactory助手类来自动注册类的简单方法。
现在我已经多次阅读到应该避免使用Singletons,一些人甚至认为它们是反模式,只有很少的情况下它们是有用的。这是否是其中之一?还是有其他替代方法可以提供这样一种自动注册类的简单方式?

1
@hansmaad:在我看来,这似乎是全局变量有用的情况之一。我看不出将其作为单例模式也有什么好处。 - jalf
1
@YeenFei:例如http://jalf.dk/blog/2010/03/singletons-solving-problems-you-didnt-know-you-never-had-since-1995/。当然,如果你谷歌“singletonitis”、“singleton antipattern”或“singletons considered harmful”,你会找到更多的例子。 - jalf
@jalf 晚了一些,但是...上周我的一个同事使用了全局变量作为工厂,并陷入了“静态初始化顺序灾难”的陷阱。本地静态(单例)实例的好处在于第一次注册时进行初始化。 - hansmaad
@hansmaad:但你只是在处理症状,而不是原因。问题在于你的整个程序都发生在main函数之外。为什么你有一些全局变量的初始化依赖于其他全局变量?当然,如果你坚持让你的程序的一半成为全局变量,那么你需要使用大型(过于复杂和容易出错)工具,但这并不意味着单例是一个好工具;这只是意味着你需要减少应用中的全局状态量。 - jalf
此外,根据您的描述,您所需要的仅仅是单例提供给您的部分功能。您需要一个延迟初始化的全局变量。单例确实可以做到这一点,但它也会将您限制在其中,并防止您永远创建该类型的另一个实例。当您只需要一个延迟初始化的全局变量时,在您的代码中施加这种有害且不必要的约束是不明智的。 - jalf
显示剩余3条评论
1个回答

2
针对这种类型的问题,没有适用于所有问题的答案。有些人认为,在作为访问服务的单例时应避免使用它们。这种用法类似于使用全局变量。这样你就掩盖了你在实现中使用服务X的事实。
// in header
class MyUsefulClass
{
    public:
    void doSomethingUseful();
};

// in .cpp
MyUsefulClass::doSomethingUseful()
{
    // ...
    MyWonderfulService::instance().doService1();
    // ...
    MyWonderfulService::instance().doService2();
}

您与MyWonderfulService创建了一种耦合方式,而您的类的用户可能猜不到这一点。此外,您无法轻松地使用模拟服务测试您的有用类...
这就是为什么我通常更喜欢依赖反转
// in header
class MyUsefulClass
{
    public:
    void setServiceToUse(MyService&);
    void doSomethingUseful();

    // [...]
};

// in .cpp
MyUsefulClass::doSomethingUseful()
{
    // ...
    _myService->doService1();
    // ...
    _myService->doService2();
}

这种方式通常被认为更好,因为类之间的耦合较轻。然而,对于一些服务来说,它们在框架中被广泛使用,使用单例模式会更简单。例如,对于一个单一的服务,它可以让你访问框架中的所有其他服务。它经常用于像日志记录这样的技术服务。
我的个人看法。
编辑:我读了这篇文章,由于重点是抽象工厂,所以使用单例模式是一种随意的选择,而不是设计决策。在一篇文章中,你不想写一些与主题无关的东西是可以理解的。

因此,为了实现文章中所示的自动注册,我需要全局访问权限,但我不需要它成为唯一实例。因此,一种替代方法是像 std::cout 一样提供它作为全局变量,这样我就可以使用它,但不会阻止我使用第二个实例(如果需要的话)。或者有没有第三种方式,更优越的方式来实现这种自动注册? - P3trus
@p3trus: 当我有时间时,我会在我的回答中添加一个具体的例子 ... 现在先尝试全局。我个人每次都使用依赖反转 ... - neuro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接