编辑:
从另一个问题,我提供了一个答案,有很多关于单例的问题/答案的链接:
所以我读了单身人士的帖子:好的设计还是拐杖?
争论仍在激烈进行。
我认为单例是一种设计模式(有好有坏)。
单例的问题不在于模式,而在于用户(对不起大家)。每个人和他们的父亲都认为他们可以正确地实施一个(从我所做的许多采访来看,大多数人都不能)。另外,因为每个人都认为他们可以实现正确的单例,所以他们滥用模式并在不合适的情况下使用它(用单例替换全局变量!)
所以需要回答的主要问题是:
什么时候应该使用单例
如何正确地实现单例
我对本文的希望是,我们可以在一个地方(而不是谷歌和搜索多个站点)收集何时(以及如何)正确使用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#最健壮的版本:
using System;
using System.Collections;
using System.Threading;
namespace DoFactory.GangOfFour.Singleton.RealWorld
{
// MainApp test application
class MainApp
{
static void Main()
{
LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
LoadBalancer b4 = LoadBalancer.GetLoadBalancer();
// Same instance?
if (b1 == b2 && b2 == b3 && b3 == b4)
{
Console.WriteLine("Same instance\n");
}
// All are the same instance -- use b1 arbitrarily
// Load balance 15 server requests
for (int i = 0; i < 15; i++)
{
Console.WriteLine(b1.Server);
}
// Wait for user
Console.Read();
}
}
// "Singleton"
class LoadBalancer
{
private static LoadBalancer instance;
private ArrayList servers = new ArrayList();
private Random random = new Random();
// Lock synchronization object
private static object syncLock = new object();
// Constructor (protected)
protected LoadBalancer()
{
// List of available servers
servers.Add("ServerI");
servers.Add("ServerII");
servers.Add("ServerIII");
servers.Add("ServerIV");
servers.Add("ServerV");
}
public static LoadBalancer GetLoadBalancer()
{
// Support multithreaded applications through
// 'Double checked locking' pattern which (once
// the instance exists) avoids locking each
// time the method is invoked
if (instance == null)
{
lock (syncLock)
{
if (instance == null)
{
instance = new LoadBalancer();
}
}
}
return instance;
}
// Simple, but effective random load balancer
public string Server
{
get
{
int r = random.Next(servers.Count);
return servers[r].ToString();
}
}
}
}
下面是. net优化版:
using System;
using System.Collections;
namespace DoFactory.GangOfFour.Singleton.NETOptimized
{
// MainApp test application
class MainApp
{
static void Main()
{
LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
LoadBalancer b4 = LoadBalancer.GetLoadBalancer();
// Confirm these are the same instance
if (b1 == b2 && b2 == b3 && b3 == b4)
{
Console.WriteLine("Same instance\n");
}
// All are the same instance -- use b1 arbitrarily
// Load balance 15 requests for a server
for (int i = 0; i < 15; i++)
{
Console.WriteLine(b1.Server);
}
// Wait for user
Console.Read();
}
}
// Singleton
sealed class LoadBalancer
{
// Static members are lazily initialized.
// .NET guarantees thread safety for static initialization
private static readonly LoadBalancer instance =
new LoadBalancer();
private ArrayList servers = new ArrayList();
private Random random = new Random();
// Note: constructor is private.
private LoadBalancer()
{
// List of available servers
servers.Add("ServerI");
servers.Add("ServerII");
servers.Add("ServerIII");
servers.Add("ServerIV");
servers.Add("ServerV");
}
public static LoadBalancer GetLoadBalancer()
{
return instance;
}
// Simple, but effective load balancer
public string Server
{
get
{
int r = random.Next(servers.Count);
return servers[r].ToString();
}
}
}
}
你可以在dotfactory.com上找到这个模式。
如果你是创建单例对象并使用它的人,不要将它设置为单例对象(这没有意义,因为你可以控制对象的奇点而不将其设置为单例对象),但如果你是一个库的开发人员,并且你只想向你的用户提供一个对象,这是有意义的(在这种情况下,你是创建单例对象的人,但你不是用户)。
单例对象所以使用它们作为对象,很多人访问单例直接通过调用方法返回它,但这是有害的因为你使你的代码知道对象是单身,我喜欢用单例对象,我通过构造函数传递它们,使用它们作为普通对象,通过这种方式,你的代码不知道是否这些对象是单例对象,这让依赖关系更加明确,它有助于重构……