单例模式的应用场景:不适宜出现多于一个实例的场景。当我们需要一个公共区域以便于其他各部分进行交流时,我们需要使用单利模式。单例模式就相当于大房间里有很多小房间,但是只有一个卫生间和厨房,众多小房间共用一个卫生间和厨房。我还看到过一个很生动的描述单利模式机制的例子。
总结:面向对象设计时我们讲求的是封装,是低耦合高内聚。但是世界上的东西不管怎么封装,都是需要相互关联的。所以我们不仅要认识事物,更要清楚的认识事物之间的关联,处理好各个对象间的关系。
问题:下面这个案例中,私有构造函数什么时候能被调用,公共变量Country什么时候初始化?
public class Country { List<string> oCountries = new List<string>(); public Country() { oCountries.Add("India"); oCountries.Add("Nepal"); oCountries.Add("USA"); oCountries.Add("UK"); } public IEnumerable<string> getCounties() { return (IEnumerable<string>) oCountries; } } public sealed class GlobalSingleton { // object which needs to be shared globally public Country Countries = new Country(); // use static variable to create a single instance of the object static readonly GlobalSingleton INSTANCE = new GlobalSingleton(); /// This is a private constructor, meaning no outsides have access. private GlobalSingleton() { } public static GlobalSingleton Instance { get { return INSTANCE; } } }
Lazy Loading
Memory barrier issue
Double-Check-Locking
Ensure a class only has one instance, and provide a global point of access to it.-[GOF]
From http://csharpindepth.com/Articles/General/Singleton.aspx
- A single constructor, which is private and parameterless. This prevents other classes from instantiating it (which would be a violation of the pattern). Note that it also prevents subclassing - if a singleton can be subclassed once, it can be subclassed twice, and if each of those subclasses can create an instance, the pattern is violated. The factory pattern can be used if you need a single instance of a base type, but the exact type isn't known until runtime.
- The class is sealed. This is unnecessary, strictly speaking, due to the above point, but may help the JIT to optimise things more.
- A static variable which holds a reference to the single created instance, if any.
- A public static means of getting the reference to the single created instance, creating one if necessary.
Third version - attempted thread-safety using double-check locking
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}
This implementation attempts to be thread-safe without the necessity of taking out a lock every time. Unfortunately, there are four downsides to the pattern:
- It doesn't work in Java. This may seem an odd thing to comment on, but it's worth knowing if you ever need the singleton pattern in Java, and C# programmers may well also be Java programmers. The Java memory model doesn't ensure that the constructor completes before the reference to the new object is assigned to instance. The Java memory model underwent a reworking for version 1.5, but double-check locking is still broken after this without a volatile variable (as in C#).
- Without any memory barriers, it's broken in the ECMA CLI specification too. It's possible that under the .NET 2.0 memory model (which is stronger than the ECMA spec) it's safe, but I'd rather not rely on those stronger semantics, especially if there's any doubt as to the safety. Making the
instance
variable volatile can make it work, as would explicit memory barrier calls, although in the latter case even experts can't agree exactly which barriers are required. I tend to try to avoid situations where experts don't agree what's right and what's wrong! - It's easy to get wrong. The pattern needs to be pretty much exactly as above - any significant changes are likely to impact either performance or correctness.
- It still doesn't perform as well as the later implementations.