编辑: 从另一个问题,我提供了一个答案,有很多关于单例的问题/答案的链接:

所以我读了单身人士的帖子:好的设计还是拐杖? 争论仍在激烈进行。

我认为单例是一种设计模式(有好有坏)。 单例的问题不在于模式,而在于用户(对不起大家)。每个人和他们的父亲都认为他们可以正确地实施一个(从我所做的许多采访来看,大多数人都不能)。另外,因为每个人都认为他们可以实现正确的单例,所以他们滥用模式并在不合适的情况下使用它(用单例替换全局变量!)

所以需要回答的主要问题是:

什么时候应该使用单例 如何正确地实现单例

我对本文的希望是,我们可以在一个地方(而不是谷歌和搜索多个站点)收集何时(以及如何)正确使用Singleton的权威来源。同样合适的是列出反用法和常见的坏实现,解释为什么它们不能工作,以及对于好的实现来说它们的缺点。

所以开始行动吧: 我会举起我的手,说这是我用的,但可能有问题。 我喜欢“Scott Myers”在他的书《Effective c++》中对这个主题的处理。

使用单例的好情况(不多): 日志框架 线程回收池

/*
 * C++ Singleton
 * Limitation: Single Threaded Design
 * See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
 *      For problems associated with locking in multi threaded applications
 *
 * Limitation:
 * If you use this Singleton (A) within a destructor of another Singleton (B)
 * This Singleton (A) must be fully constructed before the constructor of (B)
 * is called.
 */
class MySingleton
{
    private:
        // Private Constructor
        MySingleton();
        // Stop the compiler generating methods of copy the object
        MySingleton(MySingleton const& copy);            // Not Implemented
        MySingleton& operator=(MySingleton const& copy); // Not Implemented

    public:
        static MySingleton& getInstance()
        {
            // The only instance
            // Guaranteed to be lazy initialized
            // Guaranteed that it will be destroyed correctly
            static MySingleton instance;
            return instance;
        }
};

好的。让我们把一些批评和其他实现放在一起。 : -)


当前回答

单身让你有能力在一个职业中结合两种坏特性。这在任何方面都是错误的。

单例可以给你:

对对象的全局访问,以及 这种类型只能创建一个对象的保证

第一点很简单。全局变量通常不好。除非真的需要,否则永远不要让对象具有全局可访问性。

第二点可能听起来很有道理,但让我们想想。上次你**不小心*创建了一个新对象而不是引用一个现有对象是什么时候?因为它被标记为c++,所以让我们使用该语言中的一个示例。你经常不小心写东西吗

std::ostream os;
os << "hello world\n";

当你打算写信的时候

std::cout << "hello world\n";

当然不是。我们不需要防范这种错误,因为这种错误根本不会发生。如果确实如此,正确的反应是回家睡12-20个小时,希望你能感觉好点。

如果只需要一个对象,只需创建一个实例。如果一个对象应该是全局可访问的,则将其设置为全局。但这并不意味着不可能创建它的其他实例。

“只有一个实例是可能的”约束并不能真正保护我们免受可能的错误。但这确实使我们的代码很难重构和维护。因为我们经常会发现我们需要不止一个实例。我们确实有不止一个数据库,不止一个配置对象,我们确实需要几个记录器。举一个常见的例子,我们的单元测试可能希望能够在每次测试中创建和重新创建这些对象。

So a singleton should be used if and only if, we need both the traits it offers: If we need global access (which is rare, because globals are generally discouraged) and we need to prevent anyone from ever creating more than one instance of a class (which sounds to me like a design issue). The only reason I can see for this is if creating two instances would corrupt our application state - probably because the class contains a number of static members or similar silliness. In which case the obvious answer is to fix that class. It shouldn't depend on being the only instance.

如果你需要全局访问一个对象,把它设置为全局的,比如std::cout。但是不要限制可以创建的实例的数量。

如果您确实需要将一个类的实例数量限制为一个,并且无法安全地处理创建第二个实例,那么就强制执行。但不要让它在全球范围内都可以使用。

如果你确实需要这两个特性,那么1)让它成为单例,2)让我知道你需要它做什么,因为我很难想象这样的情况。

其他回答

单例基本上让你在语言中拥有复杂的全局状态,否则就很难或不可能拥有复杂的全局变量。

Java特别使用单例变量作为全局变量的替代品,因为所有内容都必须包含在类中。最接近全局变量的是公共静态变量,它们可以像导入静态的全局变量一样使用

c++确实有全局变量,但是调用全局类变量的构造函数的顺序未定义。因此,单例可以让您推迟全局变量的创建,直到第一次需要该变量。

Python和Ruby等语言很少使用单例,因为您可以在模块中使用全局变量。

那么什么时候使用单例对象是好的/坏的呢?差不多就是什么时候使用全局变量是好是坏。

关于模式有一点:不要一概而论。当他们有用的时候,当他们失败的时候,他们有所有的情况。

当您必须测试代码时,单例可能会令人讨厌。您通常只能使用类的一个实例,并且可以选择在构造函数中打开一扇门或使用一些方法来重置状态等等。

另一个问题是,Singleton实际上只不过是一个伪装的全局变量。当你的程序有太多的全局共享状态时,事情往往会倒退,我们都知道这一点。

这可能会使依赖追踪更加困难。当所有事情都依赖于你的单例时,就很难改变它,拆分为两个等等。你通常会被它困住。这也妨碍了灵活性。研究一些依赖注入框架来缓解这个问题。

如果你是创建单例对象并使用它的人,不要将它设置为单例对象(这没有意义,因为你可以控制对象的奇点而不将其设置为单例对象),但如果你是一个库的开发人员,并且你只想向你的用户提供一个对象,这是有意义的(在这种情况下,你是创建单例对象的人,但你不是用户)。

单例对象所以使用它们作为对象,很多人访问单例直接通过调用方法返回它,但这是有害的因为你使你的代码知道对象是单身,我喜欢用单例对象,我通过构造函数传递它们,使用它们作为普通对象,通过这种方式,你的代码不知道是否这些对象是单例对象,这让依赖关系更加明确,它有助于重构……

在桌面应用程序中(我知道,只有我们这些恐龙才会写这些!),它们对于获得相对不变的全局应用程序设置是必不可少的——用户语言,帮助文件的路径,用户首选项等等,否则就必须传播到每个类和每个对话框中。

编辑-当然这些应该是只读的!

独生子女的真正缺点是他们打破了继承。你不能派生一个新的类来提供扩展的功能,除非你能访问引用单例的代码。因此,除了Singleton将使您的代码紧密耦合之外(可通过策略模式修复…)又名依赖注入),它也会阻止你关闭部分代码的修订(共享库)。

因此,即使日志记录器或线程池的示例也是无效的,应该由策略替换。