单例的定义:对于单例,全局有且仅有一个实例,并提供一个全局访问点。
单例的运用情况很广,比如window系统任务管理器,访问数据库时用到的连接池,Spring MVC框架下的 DispatcherServlet 等。总之,单例运用广泛,值得学习。下文就JavaScript 与Java实现单例。
自从那后羿把金乌十射其九,才得现在温和的环境。想象下,隔壁三体文明的滋味可不好受。让我们想个办法,让天上的太阳有且只有一个。
JavaScript版本
var getSun = (function () {
var sun = null
var Sun = function () {
}
return function () {
return sun || (sun = new Sun ())
}
})();
a = getSun ()
b = getSun ()
a == b // True
上面的代码我们使用一个自执行的匿名函数返回一个函数,通过调用此函数,就可以得到一个单例对象。我们通过闭包,在我们第一次构造Sun实例时,将其用变量sun进行保存,在第二次调用时,实际上仅返回了对最初对象的引用。这样,就可以保证我们最多只有一个太阳了。
上述方法虽然能完成对单例的实现,但违反了单一职责原则,上面的函数,既要保证单例功能,又要完成。对每个单例对象,我们都需要编写相同逻辑的代码,这明显是费时费力的,考虑将其拆分为两个类,一个普通类,一个用于管理单例。
ProxySingle = function (cls) {
var obj = null
return function () {
return obj || (obj = new cls());
}
}
function Sun () {}
function Moon() {}
getsun = ProxySingle(Sun)
getmoon = ProxySingle(Moon)
a = getsun ()
b = getsun ()
c = getmoon ()
d = getmoon ()
console.log(a == b ); //True
console.log(c == d ); //True
在改进的代码中,我们给了一个ProxySingle 函数用于实现单例功能。使用时,我们只需要把一个普通类传入,最后会返回一个函数,调用它就可得到最终对象,且无论我们调用多少次,得到的都是相同对象。
根据创建时间不同,又把单例分为惰性单例与非惰性单例。惰性单例是指只有在需要用到时才进行创建。对于非惰性单例,在程序一开始就进行创建,在需要时去取而不是创建。上文中所有单例都是惰性的。
给出一个java 版本
第一个类文件
public class SunFactory {
public static class Sun {
private Sun() {}
}
private static Sun sun = null;
public static Sun getSun () {
if (sun == null) {
sun = new Sun();
}
return sun;
}
}
程序入口
public class Main {
public static void main(String[] args) {
SunFactory.Sun a1 = SunFactory.getSun();
SunFactory.Sun a2 = SunFactory.getSun();
System.out.println(a1 == a2); // true
}
}
在上述java版本中,我们在工厂类SunFactory 中,定义了一个内部静态类Sun,但将其构造函数私有化,在SunFactory中,暴露一个getSun接口,用于获得Sun对象。原理是,private对于类外是私有的,但是类内是可以访问到的。
当然,我们更希望Java版本也能像JavaScript提供的版本一样,遵循单一职责原则,也就是把管理单例的类与构造实例化对象的类分离。但目前我还没想到好的方法,欢迎大家一起交流。