在实际开发中,有些情况下未来避免镀铬实例对象,会用到设计模式的单列模式
通常情况实例对象都是通过public的构造函数实现,这边为了避免多个实例对象,所以用私有的构造函数,防止外部访问,如下代码就是如此实现:
GoF的解决方案
下面让我们看看最流行的解决方案吧,当然也是我们在项目中用到的最广泛的GoF的那本“设计模式圣经”里面所描述的:
1. public class PrintSpooler {
2. private PrintSpooler(){}
3. private static PrintSpooler instance = new PrintSpooler();
4. public static PrintSpooler getInstance() {
5. return instance;
6. }
7. }
神奇吗?把构造函数设为private的,这样的话外部就不能实例化PrintSpooler类,只有自己本身能够实例化自己,然后通过静态方法getInstance()返回这个实例从而提供唯一的访问点。如果你试着运行这行代码:
PrintSpooler p = new PrintSpooler();
^_^,编译器是不会放过你的!那么我们如何使用PrintSpooler呢?很简单,我们敲下这行代码就可以了:
PrintSpooler p = PrintSpooler.getInstance();
这个解决方案还有另外一种变形,就是用到Lazy-initialize:
1. public class PrintSpooler {
2. private PrintSpooler(){}
3. private static PrintSpooler instance = null;
4. public static synchronized PrintSpooler getInstance() {
5. if(instance == null)
6. instance = new PrintSpooler();
7. return instance;
8. }
9. }
这里有两个问题需要解决:为什么需要 Lazy-initialize?它是什么 ?
那里冒出的 synchronized ?
首先,因为有时候单体类在实例化时所需的数据只有在运行时通过计算才能获得,所以我们必须延迟实例化,所以也就有了先前的惰性初始化(Lazy-initialize)--第一次调用时进行初始化。
其次,考虑的线程安全的因素,如果一个线程发现instance==null而去实例化PrintSpooler,且此时PrintSpooler还未实例化完毕。与此同时另一线程也检查instance,结果为instance==null,所以也去实例化PrintSpooler了,现在 PrintSpooler有了两个实例,不再是单体了。为了防止上面的情况发生,可以用synchronized提供的锁机制,所以就冒出了 synchronized!
3.需要注意的问题
有时在某些情况下,使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器和JVM的。所以在EJB中,单体模式就失效了!
4.再罗嗦一些......
在工厂模式中的类装载器(Class Loader)就是一个单体,J2EE设计模式中的ServiceLocator也是一个单体...我们仔细观察会发现我们的项目中在不知不觉用到好多的单体,其实单体模式的实现并没有难点,真正的难点在于在系统中如何识别单体和不滥用单体,这种敏感性只有不断的实践中才能锻炼出来,所以用好 Singleton是非常不容易的事情......