2007 |
Section 5, Chapter 2 |
Singleton Pattern
Concept
The Singleton pattern is a way to provide global access to a class instance without making the constructor available outside the class. The singleton class instantiates itself and maintains that instance across multiple process threads. Only singular class instance is allowed to exist, not multiple ones.
Use
We have a class that we wish to have globally accessible in our application. We want to allow only one instance of that class to exist, and want the class itself to control its scope. For purposes of state management, we need a class instance to be provided for each method on the class instead of making the methods static (shared between threads). This pattern is handy for creating classes for holding code to perform database select queries.
Design
The singleton class acts as a global repository for an instance of itself, whose constructor is private. So no instance outside the class can be created, and only one instance resides inside the singleton.
Illustration
With Lock
To control multi-threaded access we use the lock statement outside the instance creation. This allows us to lock the instance creation to a single thread (the first one). This is important to keep multiple threads from creating their own instance before the first thread gets through the code.
All the following process threads after the first one will see a non-null instance of our MDIWindow class and not try to create it again.
class MDIWindow
{
private MDIWindow __instance;
private MDIWindow()
{
//... no-op for a singleton
}
//Lazy creation of singleton internal instance
public static MDIWindow Instance
{
get
{
lock(typeof(MDIWindow))
{
if(__instance == null)
__instance = new MDIWindow();
}
return __instance;
}
}
}
With mutex or semaphore
Another way to do this is to use a mutex or a semaphore. A mutex is a thread synchronization device that allows access to only one thread.
The difference between a mutex and a semaphore is that a mutex enforces thread identity, and the semaphore does not.
Comment: the rest of the sample code is messy. |
December 2007 |
Section 3, Chapter 5 |
2016 |
Chapter 3 |
GoF Definition: Ensure a class only has one instance, and provide a global point of access to it.
In a software system sometimes we may decide to use only one file system. Usually we may use it for the centralized management of resources.
package singleton.pattern.demo;
class MakeACaptain
{
private static MakeACaptain _captain;
//We make the constructor private to prevent the use of "new"
private MakeACaptain() { }
public static MakeACaptain getCaptain()
{
// Lazy initialization
if (_captain == null)
{ _captain = new MakeACaptain();
System.out.println("New Captain selected for our team");
}
else
{
System.out.print("You already have a Captain for your team.");
System.out.println("Send him for the toss.");
}
return _captain;
}
}
class SingletonPatternEx
{
public static void main(String[] args)
{
System.out.println("***Singleton Pattern Demo***\n");
System.out.println("Trying to make a captain for our team");
MakeACaptain c1 = MakeACaptain.getCaptain();
System.out.println("Trying to make another captain for our team");
MakeACaptain c2 = MakeACaptain.getCaptain();
if (c1 == c2)
{
System.out.println("c1 and c2 are same instance");
}
}
}
Concern of thread safety
Solution with synchronized
public static synchronized MakeACaptain getCaptain()
{
//our code
}
With this solution we need to pay for the performance cost associated with this synchronization method.
Eager initialization solution
class MakeACaptain
{
//Early initialization
private static MakeACaptain _captain = new MakeACaptain();
//We make the constructor private to prevent the use of "new"
private MakeACaptain() { }
// Global point of access //MakeACaptain.getCaptain() is a public static //method
public static MakeACaptain getCaptain()
{
return _captain;
}
}
Bill Pugh solution
class MakeACaptain
{
private static MakeACaptain _captain;
private MakeACaptain() { }
//Bill Pugh solution
private static class SingletonHelper{
//Nested class is referenced after getCaptain() is called
private static final MakeACaptain _captain = new MakeACaptain();
}
public static MakeACaptain getCaptain()
{
return SingletonHelper._captain;
}
}
March 25, 2002 |
Chapter 8 |
package com.oozinoz.machine;
public class Factory_2 {
private static Factory_2 factory;
private static final Object classLock = Factory_2.class;
private long wipMoves;
private Factory_2()
{
wipMoves = 0;
}
public static Factory_2 getFactory()
{
synchronized (classLock)
{
if (factory == null)
{
factory = new Factory_2();
}
return factory;
}
}
public void recordWipMove()
{
// challenge!
}
// ...
}
2012 |
Section 3, Chapter 9 |
在JavaScript的世界里这个模式有些不同,它的用处主要是提供一个实现被隔离的命名空间的入口。通过它得到的不是简简单单的一个对象,而是一个结构。
最简单的形态为:
var mySingleton = {
property1: "something",
property2: "something else",
method1: function () {
console.log('hello world');
}
};
上面的做法其实就是声明一个对象。进一步的扩展是利用闭包将私有成员与公共成员分开:
var mySingleton = function () {
// here are our private methods and variables
var privateVariable = 'something private';
function showPrivate() {
console.log(privateVariable);
}
// public variables and methods (which can access
// private variables and methods )
return {
publicMethod: function () {
showPrivate();
},
publicVar: 'the public can see this!'
};
};
var single = mySingleton();
single.publicMethod(); // logs 'something private'
console.log(single.publicVar); // logs 'the public can see this!'
上面两段示例其实没有限制实例的数量为单独一个。更高级的做法是将实例化类的操作放在另一个构造函数里:
var Singleton = (function () {
var instantiated;
function init() {
// singleton here
return {
publicMethod: function () {
console.log('hello world');
},
publicProperty: 'test'
};
}
return {
getInstance: function () {
if (!instantiated) {
instantiated = init();
}
return instantiated;
}
};
})();
// calling public methods is then as easy as:
Singleton.getInstance().publicMethod();
这个做法才符合严谨的单例模式的定义。那么常见的用法是:
var SingletonTester = (function () {
// options: an object containing configuration options for the singleton
// e.g var options = { name: 'test', pointX: 5};
function Singleton(options) {
// set options to the options supplied or an empty object if none provided.
options = options || {};
//set the name parameter
this.name = 'SingletonTester';
//set the value of pointX
this.pointX = args.pointX || 6;
//set the value of pointY
this.pointY = args.pointY || 10;
}
// this is our instance holder
var instance;
// this is an emulation of static variables and methods
var _static = {
name: 'SingletonTester',
// This is a method for getting an instance
// It returns a singleton instance of a singleton object
getInstance: function (options) {
if (instance === undefined) {
instance = new Singleton(options);
}
return instance;
}
};
return _static;
})();
var singletonTest = SingletonTester.getInstance({
pointX: 5
});
console.log(singletonTest.pointX); // outputs 5
2010 |
Section 1, Chapter 7 |
上一本书里,作者Addy介绍说最极端简单的单例模式就是单纯创建一个对象,关于原因他没有解释得很清楚,这本书里,作者Stoyan则解释了这个问题,因为JS里,一切都是对象,一切始于对象,所以不需要其他的特殊语法或者操作就已经符合单例的定义了,因为一个对象是唯一的,即使你创建另一个成员和成员值一模一样的对象,它们也并不是同一个对象。
上面的解释偏理论,回到更实际些的问题上来,JS还是要用new关键字来完成这个模式需要的一些特性,那就是要保证不论用new呼叫一个构造函数几次,它都应该返回指向同一个对象的引用,所以就需要一个地方保存第一次创建的那个对象,作者接着罗列了可以保存这个对象的三个方案。
- 全局变量
- 构造函数的属性
- 一个新闭包
第一个略,第二个的做法是:
function Universe() {
// do we have an existing instance?
if (typeof Universe.instance === "object") {
return Universe.instance;
}
// proceed as normal
this.start_time = 0;
this.bang = "Big";
// cache
Universe.instance = this;
// implicit return:
// return this;
}
// testing
var uni = new Universe();
var uni2 = new Universe();
uni === uni2; // true
它的缺点是,谁都可以访问并修改。
第三个,这个做法超乎我的预料,原来竟然是靠重新定义函数自身,并且又保留了闭包:
function Universe() {
// the cached instance
var instance = this;
// proceed as normal
this.start_time = 0;
this.bang = "Big";
// rewrite the constructor
Universe = function () {
return instance;
};
}
// testing
var uni = new Universe();
var uni2 = new Universe();
uni === uni2; // true
但是它也有缺点,如下代码:
// adding to the prototype
Universe.prototype.nothing = true;
var uni = new Universe();
// again adding to the prototype
// after the initial object is created
Universe.prototype.everything = true;
var uni2 = new Universe();
// only the original prototype was
// linked to the objects
uni.nothing; // true
uni2.nothing; // true
uni.everything; // undefined
uni2.everything; // undefined
// that sounds right:
uni.constructor.name; // "Universe"
// but that's odd:
uni.constructor === Universe; // false
解决办法是:
function Universe() {
// the cached instance
var instance;
// rewrite the constructor
Universe = function Universe() {
return instance;
};
// carry over the prototype properties
Universe.prototype = this;
// the instance
instance = new Universe();
// reset the constructor pointer
instance.constructor = Universe;
// all the functionality
instance.start_time = 0;
instance.bang = "Big";
return instance;
}
最后,还有一个更好的解决方案:
var Universe;
(function () {
var instance;
Universe = function Universe() {
if (instance) {
return instance;
}
instance = this;
// all the functionality
this.start_time = 0;
this.bang = "Big";
};
}());