Design Pattern - Creational Patterns - Singleton Pattern

85 篇文章 0 订阅
50 篇文章 0 订阅

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";
  };
}());









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
“设计模式:可重用的面向对象软件的基本元素(Design Patterns: Elements of Reusable Object-Oriented Software)”是一本由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人所著的经典书籍。该书于1994年首次出版,被誉为“软件设计模式圣经”。 该书讲述了一些经典的设计模式,包括面向对象编程中涉及到的对象、类、接口和继承等基本概念,并详细介绍了在实际应用中如何使用这些模式来解决常见的设计问题。 该书的主要内容包括:介绍了23种设计模式,其中分为创建型模式(Creational Patterns)、结构型模式(Structural Patterns)和行为型模式(Behavioral Patterns)三种类型;每个设计模式包括模式的定义、优点、结构以及应用场景等方面,以及一些常见的解决方案;此外,该书还特别强调了设计模式的重要性,并简要介绍了如何利用设计模式提高代码的可维护性和可重用性等方面。 该书的读者主要包括软件开发人员、软件架构师、系统设计师等技术人员,也适合经验较浅的Java、C++和C#等编程语言的初学者,读过该书后可以深刻理解并熟练运用设计模式来提高软件开发的效率和质量。 在CSDN(中文技术社区)上,设计模式是一门热门的技术,有很多人通过阅读“设计模式:可重用的面向对象软件的基本元素”这本书籍,来掌握设计模式的基础知识,并学习如何在实际项目中应用这些模式。该书也经常被提及在CSDN社区中的技术讨论中,并被认为是学习设计模式最权威的教材之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值