The Singleton Pattern

The Singleton pattern is thus known because it restricts instantiation of a class to a single object. Classically, the Singleton pattern can be implemented by creating a class with a method that creates a new instance of the class if one doesn't exist. In the event of an instance already existing, it simply returns a reference to that object.

Singletons differ from static classes (or objects) as we can delay their initialization, generally because they require some information that may not be available during initialization time. They don't provide a way for code that is unaware of a previous reference to them to easily retrieve them. This is because it is neither the object or "class" that's returned by a Singleton, it's a structure. Think of how closured variables aren't actually closures - the function scope that provides the closure is the closure.

In JavaScript, Singletons serve as a shared resource namespace which isolate implementation code from the global namespace so as to provide a single point of access for functions.

We can implement a Singleton as follows:

  1. var mySingleton = (function () {
  2.  
  3. // Instance stores a reference to the Singleton
  4. var instance;
  5.  
  6. function init() {
  7.  
  8. // Singleton
  9.  
  10. // Private methods and variables
  11. function privateMethod(){
  12. console.log( "I am private" );
  13. }
  14.  
  15. var privateVariable = "Im also private";
  16.  
  17. var privateRandomNumber = Math.random();
  18.  
  19. return {
  20.  
  21. // Public methods and variables
  22. publicMethod: function () {
  23. console.log( "The public can see me!" );
  24. },
  25.  
  26. publicProperty: "I am also public",
  27.  
  28. getRandomNumber: function() {
  29. return privateRandomNumber;
  30. }
  31.  
  32. };
  33.  
  34. };
  35.  
  36. return {
  37.  
  38. // Get the Singleton instance if one exists
  39. // or create one if it doesn't
  40. getInstance: function () {
  41.  
  42. if ( !instance ) {
  43. instance = init();
  44. }
  45.  
  46. return instance;
  47. }
  48.  
  49. };
  50.  
  51. })();
  52.  
  53. var myBadSingleton = (function () {
  54.  
  55. // Instance stores a reference to the Singleton
  56. var instance;
  57.  
  58. function init() {
  59.  
  60. // Singleton
  61.  
  62. var privateRandomNumber = Math.random();
  63.  
  64. return {
  65.  
  66. getRandomNumber: function() {
  67. return privateRandomNumber;
  68. }
  69.  
  70. };
  71.  
  72. };
  73.  
  74. return {
  75.  
  76. // Always create a new Singleton instance
  77. getInstance: function () {
  78.  
  79. instance = init();
  80.  
  81. return instance;
  82. }
  83.  
  84. };
  85.  
  86. })();
  87.  
  88. // Usage:
  89.  
  90. var singleA = mySingleton.getInstance();
  91. var singleB = mySingleton.getInstance();
  92. console.log( singleA.getRandomNumber() === singleB.getRandomNumber() ); // true
  93.  
  94. var badSingleA = myBadSingleton.getInstance();
  95. var badSingleB = myBadSingleton.getInstance();
  96. console.log( badSingleA.getRandomNumber() !== badSingleB.getRandomNumber() ); // true
  97.  
  98. // Note: as we are working with random numbers, there is a
  99. // mathematical possibility both numbers will be the same,
  100. // however unlikely. The above example should otherwise still
  101. // be valid.

What makes the Singleton is the global access to the instance (generally through MySingleton.getInstance()) as we don't (at least in static languages) call new MySingleton() directly. This is however possible in JavaScript.

In the GoF book, the applicability of the Singleton pattern is described as follows:

  • There must be exactly one instance of a class, and it must be accessible to clients from a well-known access point.
  • When the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code.

The second of these points refers to a case where we might need code such as:

  1. mySingleton.getInstance = function(){
  2. if ( this._instance == null ) {
  3. if ( isFoo() ) {
  4. this._instance = new FooSingleton();
  5. } else {
  6. this._instance = new BasicSingleton();
  7. }
  8. }
  9. return this._instance;
  10. };

Here, getInstance becomes a little like a Factory method and we don't need to update each point in our code accessing it. FooSingleton above would be a subclass of BasicSingleton and implement the same interface.

Why is deferring execution considered important for a Singleton?:

In C++ it serves to isolate from the unpredictability of the order of dynamic initialization, returning control to the programmer.

It is important to note the difference between a static instance of a class (object) and a Singleton: whilst a Singleton can be implemented as a static instance, it can also be constructed lazily, without the need for resources nor memory until this is actually needed.

If we have a static object that can be initialized directly, we need to ensure the code is always executed in the same order (e.g in case objCar needs objWheel during its initialization) and this doesn't scale when you have a large number of source files.

Both Singletons and static objects are useful but they shouldn't be overused - the same way in which we shouldn't overuse other patterns.

In practice, the Singleton pattern is useful when exactly one object is needed to coordinate others across a system. Here is one example with the pattern being used in this context:

  1. var SingletonTester = (function () {
  2.  
  3. // options: an object containing configuration options for the singleton
  4. // e.g var options = { name: "test", pointX: 5};
  5. function Singleton( options ) {
  6.  
  7. // set options to the options supplied
  8. // or an empty object if none are provided
  9. options = options || {};
  10.  
  11. // set some properties for our singleton
  12. this.name = "SingletonTester";
  13.  
  14. this.pointX = options.pointX || 6;
  15.  
  16. this.pointY = options.pointY || 10;
  17.  
  18. }
  19.  
  20. // our instance holder
  21. var instance;
  22.  
  23. // an emulation of static variables and methods
  24. var _static = {
  25.  
  26. name: "SingletonTester",
  27.  
  28. // Method for getting an instance. It returns
  29. // a singleton instance of a singleton object
  30. getInstance: function( options ) {
  31. if( instance === undefined ) {
  32. instance = new Singleton( options );
  33. }
  34.  
  35. return instance;
  36.  
  37. }
  38. };
  39.  
  40. return _static;
  41.  
  42. })();
  43.  
  44. var singletonTest = SingletonTester.getInstance({
  45. pointX: 5
  46. });
  47.  
  48. // Log the output of pointX just to verify it is correct
  49. // Outputs: 5
  50. console.log( singletonTest.pointX );

Whilst the Singleton has valid uses, often when we find ourselves needing it in JavaScript it's a sign that we may need to re-evaluate our design.

They're often an indication that modules in a system are either tightly coupled or that logic is overly spread across multiple parts of a codebase. Singletons can be more difficult to test due to issues ranging from hidden dependencies, the difficulty in creating multiple instances, difficulty in stubbing dependencies and so on.

Miller Medeiros has previously recommended this excellent article on the Singleton and its various issues for further reading as well as the comments to this article, discussing how Singletons can increase tight coupling. I'm happy to second these recommendations as both pieces raise many important points about this pattern that are also worth noting.

Learning JavaScript Design Patterns The Singleton Pattern的更多相关文章

  1. Learning JavaScript Design Patterns The Module Pattern

    The Module Pattern Modules Modules are an integral piece of any robust application's architecture an ...

  2. Learning JavaScript Design Patterns The Observer Pattern

    The Observer Pattern The Observer is a design pattern where an object (known as a subject) maintains ...

  3. Learning JavaScript Design Patterns The Constructor Pattern

    In classical object-oriented programming languages, a constructor is a special method used to initia ...

  4. AMD - Learning JavaScript Design Patterns [Book] - O'Reilly

    AMD - Learning JavaScript Design Patterns [Book] - O'Reilly The overall goal for the Asynchronous Mo ...

  5. use getters and setters Learning PHP Design Patterns

    w Learning PHP Design Patterns Much of what passes as OOP misuses getters and setters, and making ac ...

  6. Learning PHP Design Patterns

    Learning PHP Design Patterns CHAPTER 1 Algorithms handle speed of operations, and design patterns ha ...

  7. [Design Patterns] 3. Software Pattern Overview

    When you're on the way which is unknown and dangerous, just follow your mind and steer the boat. 软件模 ...

  8. [Design Patterns] 4. Creation Pattern

    设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结,使用设计模式的目的是提高代码的可重用性,让代码更容易被他人理解,并保证代码可靠性.它是代码编制真正实现工程化. 四个关键元素 ...

  9. JavaScript Design Patterns: Mediator

    The Mediator Design Pattern The Mediator is a behavioral design pattern in which objects, instead of ...

随机推荐

  1. 解决在构造函数中使用Session,Session为null的问题

    问题描述: public abstract class PageBase : System.Web.UI.Page 在PageBase中如何使用Session??? 我直接用 Session[&quo ...

  2. 由abcd四个字符取5个作允许重复的排列,要求a出现次数不超过2次,但不能不出现;b不超过1个;c不超过3个;d出现的次数为偶数。求满足以上条件的排列数。

    一.我的解法       由于没复习,我在想一般的方法,那就是d取0.2.4,然后分步计算,得到225这个错误答案. 二.指数型母函数       设满足以上条件取个排列的排列数为,的指数型母函数为 ...

  3. csdn搜索技巧

    由于CSDN下载资源高级搜索,只能显示10页分页,所以我们换种方法去搜索csdn的资源. 第10页以后没了. 在百度框里面输入如下: jquery site:download.csdn.net 图:

  4. IOS ITunesConnect 修改开发商名称

    ItunesConnect→ManagerApps→应用→Contact Us

  5. POJ2299+逆序数

    归并排序!!!!!!!!!! /* 归并排序+求逆序数 */ #include<stdio.h> #include<string.h> #include<algorith ...

  6. PHP reset() 函数

    定义和用法 reset()函数把数组的内部指针指向第一个元素,并返回这个元素的值.若失败,则返回 FALSE. reset()函数用来将数组指针设置回数组的开始位置.如果需要在一个脚本中多次查看或处理 ...

  7. USB CDC类

    现代嵌入式系统中,异步串行通信接口往往作为标准外设出现在单片机和嵌入式系统中.但是随着个人计算机通用外围设备越来越少地使用串口,串口正在逐渐从个人计算机特别是便携式电脑上消失.于是嵌入式开发人员常常发 ...

  8. FRM-40401 No changes to save error

    FAQ: I have updated a table from the form and written a update statement in the when-button-pressed ...

  9. java集合类——Stack类

    查看java的API文档,Stack继承Vector类. 栈的特点是后进先出. API中Stack自身的方法不多,基本跟栈的特点有关. import java.util.Stack; public c ...

  10. WPF——传实体类及绑定实体类属性

    public class User: private string _User; public string User1 { get { return _User; } set { _User = v ...