题
编辑:从另一个问题,我提供一个答案,已经链接到的很多问题答复关于单: 更多信息单:
所以我阅读线 单:好的设计或一个拐杖?
和参数仍然肆虐。
我看到单身作为一个设计图案(好的和坏的).
该问题单是不是模式,而是使用者(对不起大家都).每个人和他们的父亲认为他们可以实现一个正常(和从许多访谈我已经做了,大多数人无法).还因为每个人都认为他们可以实现一个正确的单身,他们滥用模式和使用情况是不适当的(替换全球变量单身!).
因此,主要的问题需要回答是:
- 当你应该使用单独
- 你怎么实现一个单独正确
我希望这篇文章是我们可以收集一起在一个地点(而不是谷歌搜索多个站点)一个权威来源的时(以及如何)使用一个单独正确。还适当将是一个列表的反惯例和普通坏实现解释为什么他们无法工作和良好的实现他们的弱点。
因此,获得的球滚动:
我会握住我的手,并说这是我的使用,但可能有问题。
我喜欢"斯科特*迈尔斯"处理的问题在他的书籍"有效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;
}
};
"确定"。让我们得到一些批评和其它实现中在一起。
:-)
解决方案
你们都是错误的。读这个问题。回答:
使用一个单一实例,如果:
- 你需要有一个和唯一一个目的的一种类型在系统
不要使用一个单一实例,如果:
- 你想要保存的记忆
- 你想尝试新的东西
- 你想显示你知道多少
- 因为别人都是这样做(见 货物的邪教组织的程序员 在维基百科)
- 在用户界面部件
- 它应该是一个缓存
- 在弦
- 在会议
- 我可以去所有的一天长
如何创造最佳的单:
- 较小的、更好。我是一个简约
- 确保它是安全线
- 确保它是从来没有空
- 确保它是创建了唯一一次
- 懒惰的或系统的初始化?达到你的要求
- 有时的操作系统或JVM创建单给你的(例如在Java每一类定义是一个单一实例)
- 提供析构或某种方式找出如何处置资源
- 使用的存储器
其他提示
单身给你的能力相结合两个不良的特征中的一类。这是错误的,在几乎每一个方式。
一个单独给你:
- 全球访问对象,
- 一个保证没有多于一个象这种类型的 以往任何时候都可以创建的
号码一个是直接的。Globals一般是坏的。我们永远不应该让对象的全球访问,除非我们 真的 需要它。
两个数可能听起来很有意义,但是让我们想一想。当你最后一次**不小心*创建一个新的对象,而不引用现有的一个吗?由于这是加标记的C++,让我们使用的一个例子,语言。你经常写意外
std::ostream os;
os << "hello world\n";
当你打算写信
std::cout << "hello world\n";
当然不是。我们不需要保护反对这个错误,因为这种错误就不会发生。如果是这样,正确的反应是要回家睡觉12-20小时,希望你感觉更好。
如果只有一个目的是必要的,只要创建一个实例。如果一个物体应在全球范围内的访问,使其成为一个全球性的。但是,这并不意味着它应该是不可能建立的其他实例。
"唯一的一个实例是可能的"约束并没有真正保护我们对可能的错误。但它 做 使我们很难"重构"和维护。因为很多时候我们找出 后 我们需要更多的一个实例。我们 做 有超过一个数据库,我们的 做 有多于一个配对象,我们想要几个记录仪。我们单元的测试,可能希望能够创建和重建,这些对象,每一个测试,采取一个常见的例子。
所以一个单一实例应用于如果并且只有如果我们需要 既 的特征,它提供:如果我们 需要 全球访问(这是罕见的,因为globals一般都是劝阻) 和 我们 需要 为防止任何人 曾经 创建多的一个实例一类(这听起来对我像一个设计问题)。唯一的理由我可以看到这是如果创建的两个实例,会破坏我们的应用程序的国家可能是因为该类包含一些静态的成员,或类似的愚蠢。在这种情况下答案很明显是为了解决这个问题的类。它不应该取决于是唯一的实例。
如果你需要的全球访问对象,使它成为一个全球性的,喜欢 std::cout
.但是,不限制数量的实例,可以创建。
如果你绝对,肯定需限制数量的实例类只有一个,而且没有方式创建的第二个实例可以被安全地处理,然后实施的。但是不要让它在全球访问。
如果你需要两个特征,然后1)使其单独和2),让我知道你需要什么,因为我有一个很难想象这样的情况。
该问题单是不是他们的执行情况。这是,他们将两个不同的概念,无论这显然是可取的。
1)的单身人士提供一个全球性的访问机构的一个对象。虽然他们可能会稍微更多线程安全或略微更可靠的语言,而不一定义的初始化了,这种使用率仍是道德上的相等的一个全球性的变量。这是一个全球变穿着一些尴尬的语法(foo::get_instance()而不是g_foo,说),但它提供确切的相同的目的(单对象访问的整个程序)和具有完全相同的缺陷。
2)单防止多实例中的一类。这是罕见的,法,即这种特征应该被烤成一类。它通常有更多的背景的事情;很多东西,被视为一个和唯一的真的只是情况发生-可只有一个。海事组织更适当的解决办法是仅仅创建一个实例--直到你知道你需要超过一个实例。
一点与模式: 不要一概而论.他们有他们所有的情况下,当他们是有用的,并且当他们失败。
单独可以被讨厌的时你必须 测试 代码。你通常坚持的一个实例类,并且可以选择之间,开了一门的构造或某些方法重置的国家。
其他的问题是,单的事实是没有什么比一个 全球变 在伪装。当你有太多的全球共享的状态过你的节目,事情往往回去,我们都知道这一点。
它可以让 依赖跟踪 难。当一切都取决于你的单身,它很难加以改变,分为两个,等等。你是一般地坚持它。这也妨碍了灵活性。调查一些 依赖注射 框架,以尽量减轻这一问题。
单身基本上让你有复杂的全球国家在语言,否则很难或不可能有复杂的全球性变量。
Java特别是使用单作为一个替代的全球变量,因为一切都必须包含在一类。最近涉及到全球变量公共静态的变量,这可以使用,如果他们是全球与 import static
C++的确具有全球变量,但为了在其构造全球类变量是调用的是不确定的。因此,一个单一实例可以让你推迟建立一个全球性的可变直到的第一时间变量是必要的。
语言,例如Python和红宝石使用单一实例很少,因为可以使用全球变量内的一个模块,而不是。
因此,当它是好坏,使用单独?差不多什么时候它会好坏为使用全球变量。
现代C++的设计 通过Alexandrescu有一线的安全,可继承的通用单例。
我2p-值得,我认为这是重要的定义的一生对于你的单身人士(当这是绝对必要时使用他们)。我通常不让静 get()
功能化的任何东西,并留下设置和销毁到一些专门的部分主要应用程序。这有助于突出之间的依赖关系单-但是,正如强调以上,这是最好的,只是避免他们如果可能的。
- 你怎么实现一个单独正确
有一个问题我从没见过所提到的,我的东西跑进在以前的工作。我们有C++单们之间共享Dll,并且通常机制,确保一个单一的类的实例只是不工作。问题是每个DLL得到其自己的静态变量,随着EXE。如果你get_instance功能是行内部或一部分的一个静态的图书馆,每个DLL将风与自己的副本"单独".
该方案是确保单独码只限定在一个DLL或可执行软件,或者创建一个单独管理这些财产包裹出的实例。
第一个例子是不是线安全的-如果两个线呼叫getInstance在同一时间,静态将是一个皮塔。某些形式的互斥会有所帮助。
如其他人已经指出的,主要缺点包括单身无法延长,并失去的权力的实例超过一个的实例,例如为测试目的。
一些有益方面的单:
- 懒惰的或预付的实例
- 方便的对象,需要建立和/或状态
但是,你不需要使用单独获得这些好处。你可以写一个正常的对象是没有工作,然后有人访问它通过一个工厂(一个单独的对象)。工厂可以担心的唯一实例之一,并重新使用等, 如果需要的话。此外,如果你计划一个接口,而不是一个具体的类,将工厂可以采用的战略,即你可以开关和各种实现方式的接口。
最后,一个工厂本身的依赖关系注入技术,如弹等。
单是方便的时候你已经得到了很多代码正在运行,当你初始化和对象。例如,在你的路由相关配置,当你设置一个持久的对象它已经读取所有的配置、分析地图,确保其所有正确的,等等。之前得到你的代码。
如果你这样做的每一个时间、性能将大大降低。使用它在一个单独的,你把这打一次,然后后续的所有电话没有做到这一点。
实际垮台单一实例是,他们打破产继承权。你不能得出一个新的类给你的扩展功能的除非你已经访问代码在哪里的单一被引用。因此,除了事实上的单独会让你代码紧耦合(由可以解决的一个战略图案...aka依赖注射),它还将阻止你们从关闭的章节的代码从修订(共用库)。
因此,即使实例的记录器或线的游泳池是无效的,应予以更换由战略。
大多数人使用的单身,当他们试图让自己感觉良好的关于使用全球变量。有合法用途,但大多数时候,当人民利用他们,事实上,只能有一个实例是只是一个简单的事实相比,事实上,它是全球的访问。
因为一个单一实例只允许一个实例是创建有效控制的实例复制。例如你不需要多个实例,查找一个莫尔斯查找地图,例如,因此包装在一个单独的类是贴切。只是因为你有一个单一的类实例并不意味着你也是有限的数量引用的实例。你可以排队的呼吁(以避免穿问题)的实例和影响的变化所必需的。是的,在一般形式的一个单一实例是一个全球公众的一个,你当然可以修改设计以创建更多的访问受限制的单例。我还没有厌倦了这个但我确定我知道这是可能的。和所有这些人评论说,单一模式完全是邪恶,你应该知道这个:是的,它是邪恶的,如果你不正确地使用它,或在其范围的有效功能性和可预测的行为:不要一概而论。
但当我需要的东西就像一个单独的,我往往最终使用 施瓦茨计数 实例。
我用单作面试时测试。
当我问一个开发商的名称一些设计的模式,如果他的名字是单身,他们不是雇用。
下面是更好的方法实施一个线程安全的单独模式与释放的存在析构本身。但我认为析构应该是一个可选的,因为单一实例将自动被摧毁的时程序终止:
#include<iostream>
#include<mutex>
using namespace std;
std::mutex mtx;
class MySingleton{
private:
static MySingleton * singletonInstance;
MySingleton();
~MySingleton();
public:
static MySingleton* GetInstance();
MySingleton(const MySingleton&) = delete;
const MySingleton& operator=(const MySingleton&) = delete;
MySingleton(MySingleton&& other) noexcept = delete;
MySingleton& operator=(MySingleton&& other) noexcept = delete;
};
MySingleton* MySingleton::singletonInstance = nullptr;
MySingleton::MySingleton(){ };
MySingleton::~MySingleton(){
delete singletonInstance;
};
MySingleton* MySingleton::GetInstance(){
if (singletonInstance == NULL){
std::lock_guard<std::mutex> lock(mtx);
if (singletonInstance == NULL)
singletonInstance = new MySingleton();
}
return singletonInstance;
}
有关的情况下,我们需要使用单独的类可以- 如果我们想要维持该状态的实例在整个执行程序 如果我们参与编写入执行日志的应用程序,其中只有一个实例的文件的需要。等等。这将是明显的,如果有人可以建议优化在我上面的代码。
反使用情况:
一个主要问题与过度的单独使用该模式可以防止容易扩展和交换交替的方式实现的。这类名字是很难编码无论是单独使用。
我觉得这是 最强大的版本 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();
}
}
}
}
这里是的 .净化版本:
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.
的梅尔singleton案工作以及有足够多的时间,以及在该场合,它不它不一定支付寻找更好的东西。只要构造将永远不会扔和没有依赖性之间的单身.
一个单一实例是实现一个 全球访问的对象 (GAO从现在起)虽然不是所有GAOs是单身.
记录本身不应该是单身的,但这意味着登录最好应在全球访问,以便在该日志的消息是正在产生在哪里或如何得到记录。
懒惰的装载/懒惰的评价是一个不同的概念和单身通常为实现这一点。它涉及与很多其自己的问题,特别是在线安全和问题,如果它未能与例外的情况下,似乎像一个好想法的时候是没有那么大。(有点喜欢牛执行在strings)。
考虑到这一点,GOAs可以被初始化这样的:
namespace {
T1 * pt1 = NULL;
T2 * pt2 = NULL;
T3 * pt3 = NULL;
T4 * pt4 = NULL;
}
int main( int argc, char* argv[])
{
T1 t1(args1);
T2 t2(args2);
T3 t3(args3);
T4 t4(args4);
pt1 = &t1;
pt2 = &t2;
pt3 = &t3;
pt4 = &t4;
dostuff();
}
T1& getT1()
{
return *pt1;
}
T2& getT2()
{
return *pt2;
}
T3& getT3()
{
return *pt3;
}
T4& getT4()
{
return *pt4;
}
它不需要做的工作,作为粗暴地,并清楚地载入图书馆,其中包含的对象,你可能想要一些其他机制来管理他们的一生。(把他们在一个对象你当你装载的图书馆)。
因为当我使用单身?我用他们2个的事情 -一个单独的表格,指明库中已载有dlopen里 -一个信息处理程序记录仪可以订阅这,你可以发送消息。需要具体地说用于信号处理程序。
我还是不明白为什么单独要全球性的。
我是要产生一个单独的在那里我躲在一个数据库内部的类作为一个私定的静态变量和使级的职能利用该数据库没有暴露数据库的用户。
我看不出为什么这一功能将是糟糕的。
我找到他们有时我有一个类封装了大量的存储器。例如在最近的游戏我已经工作我有一种影响类地图,其中包含一个收集非常大的阵的连续记忆。我想所有分配的启动时,所有的释放,在关闭我也绝对想要的只有一个副本。我还要访问它从许多地方。我找到单一的模式是非常有用,在这种情况。
我敢肯定还有其他的解决方案,但我找到这一非常有用和易于实现。
如果你是一个人创造了单独和谁在使用它,不把它作为单(没有意义,因为你可以控制的奇点对象,而不让它单),但是有意义的,当你一个开发商的一个图书馆和你想要来源只有一个对象你的用户(在这种情况下你是谁创造了单独的,但是你不用户)。
单身是目的使用它们作为对象,许多人访问为单身直接通过调用的方法返回,但这是有害的,因为你正在你的代码知道,对象是单身,我更喜欢使用单作为对象,我通过他们通过构造和我使用它们作为普通的对象,通过这种方式,你的代码不知道如果这些对象是单身或不和,这使得依赖关系更加清楚,它有助于一个小小的重构...
在桌面应用程序(我知道,我们只恐龙写这些了!) 他们必须得到相对不变的全球应用程序设置的-的用户语言、路径来帮助的文件,用户的偏好等等,否则将必须propogate到的每一类和每个对话。
编辑-当然,这些应该是只读!
另一个执行情况
class Singleton
{
public:
static Singleton& Instance()
{
// lazy initialize
if (instance_ == NULL) instance_ = new Singleton();
return *instance_;
}
private:
Singleton() {};
static Singleton *instance_;
};