单例模式

177 阅读4分钟

文档地址: refactoringguru.cn/design-patt… 代码来源: www.cnblogs.com/TomXu/archi…

单例模式: 是一种创建型设计模式,让你能够保证一个类只有一个实例,并提供一个访问该实例的全局节点

1. 保证一个类只有一个实例: 这样做最常见的原因是,控制某些共享资源(例如数据库或文件)的访问权限。

它的运作方式是这样的:如果你创建了一个对象,同时一会儿后你决定再创建一个新对象,此时你会获得之前已创建的对象,而不是一个新对象2. 为该实例提供一个全局访问节点: 那些存储重要对象的全局变量吗?它们在使用上十分方便,但同时也非常不安全,因为任何代码都有可能覆盖掉那些变量的内容,从而引发程序崩溃。

和全局变量一样,单例模式也允许在程序的任何地方访问特定对象。但是它可以保护该实例不被其他代码覆盖。

还有一点:你不会希望解决同一个问题的代码分散在程序各处的。因此更好的方式是将其放在同一个类中,特别是当其他代码已经依赖这个类时更应该如此。

单例模式的实现

  • 将默认构造函数设为私有,防止其他对象使用单例类的new运算符。
  • 新建一个静态构建方法作为构造函数。该函数会“偷偷”调用私有构造函数创建一个对象,并将其保存在一个静态成员变量中。此后所有对于该函数的调用都将返回这一缓存对象。

伪代码实现

// 数据库类会对`getInstance`(获取实例)方法进行定义以让客户端在程序各处// 都能访问相同的数据库连接实例。class Database is
    // 保存单例实例的成员变量必须被声明为静态类型。
    private static field instance: Database

    // 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构
    // 造方法。
    private constructor Database() is
        // 部分初始化代码(例如到数据库服务器的实际连接)。
        // ...

    // 用于控制对单例实例的访问权限的静态方法。
    public static method getInstance() is
        // 核心代码
        if (Database.instance == null) then
            acquireThreadLock() and then
                // 确保在该线程等待解锁时,其他线程没有初始化该实例。
                if (Database.instance == null) then
                    Database.instance = new Database()
        return Database.instance

    // 最后,任何单例都必须定义一些可在其实例上执行的业务逻辑。
    public method query(sql) is
        // 比如应用的所有数据库查询请求都需要通过该方法进行。因此,你可以
        // 在这里添加限流或缓冲逻辑。
        // ...

伪代码调用

class Application is
    method main() is
        Database foo = Database.getInstance()
        foo.query("SELECT ...")
        // ...
        Database bar = Database.getInstance()
        bar.query("SELECT ...")
        // 变量 `bar` 和 `foo` 中将包含同一个对象。

javascript实现

简单实现:

var mySingleton = function() {
	/* 这里声明私有变量和方法 */
	var privateVariable = 'something private';

	function showPrivate() {
		console.log(privateVariable);
	} 
	
	/* 公有变量和方法(可以访问私有变量和方法) */
	return {
		publicMethod: function() {
			showPrivate();
		},
		publicVar: 'the public can see this!'
	};
};
var single = mySingleton();
single.publicMethod(); // 输出 'something private'
console.log(single.publicVar); // 输出 'the public can see this!'

如果我们想做到只有在使用的时候才初始化,为了节约资源的目的,我们可以另外一个构造函数里来初始化这些代码。

var Singleton = (function () {
	var instantiated;
	function init() {
		/*这里定义单例代码*/
		return {
			publicMethod: function () {
				console.log('hello world');
			},
			publicProperty: 'test'
		};
	}

	return {
		getInstance: function () {
			if (!instantiated) {
				instantiated = init();
			}
			return instantiated;
		}
	};
})();

/*调用公有的方法来获取实例:*/
// 这样只有 Singleton.getInstance() 时才进行初始化,进而调用实例上的方法
Singleton.getInstance().publicMethod();

单例一般是用在系统间各种模式的通信协调上,下面的代码是一个单例的最佳实践:

var SingletonTester = (function() {

	//参数:传递给单例的一个参数集合
	function Singleton(args) {

		//设置args变量为接收的参数或者为空(如果没有提供的话)
		var args = args || {};
		//设置name参数
		this.name = 'SingletonTester';
		debugger
		//设置pointX的值
		this.pointX = args.pointX || 6; //从接收的参数里获取,或者设置为默认值
		//设置pointY的值
		this.pointY = args.pointY || 10;

	}

	//实例容器
	var instance;

	var _static = {
		name: 'SingletonTester',

		//获取实例的方法
		//返回Singleton的实例
		getInstance: function(args) {
			if (instance === undefined) {
				instance = new Singleton(args);
			}
			return instance;
		}
	};
	return _static;
})();

var singletonTest = SingletonTester.getInstance({
	pointX: 5
});
console.log(singletonTest.pointX); // 输出 5

单例模式适用性

  • 如果程序中的某个类对于所有客户端只有一个可用的实例,可以使用单例模式。
  • 如果你需要更加严格地控制全局变量,可以使用单例模式 例如:vuex