单例模式(Design Pattern:Singleton)


单例模式属于创建型模式,目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。考虑这样一种对象,这个对象应该在程序启动时被创建,并且在结束时被删除,如应用程序的基础高层对象。通过这个对象可以得到系统中其他的对象,这些基础对象可能是前面提到的工厂对象(Factories),用来创建其他对象;也可能是管理器对象(managers),负责控管其他对象;或者是全局注册表(Registry)。类似这种类型的对象不该被创造出多份。
4.3.1 单例模式的实现
下面给出单例模式的实现。单例模式参与者如图4.8所示。其中Singleton定义了一个getInstance操作,允许客户访问它的惟一实例。可能负责创建它的惟一实例。

代码4.33,可以看到,经典单例模式的实现非常简单。而正是由于概念和实现上的简单,没有顾及到逻辑概念上、测试性、全局依赖、类装载器、序列化、线程安全以及不同JVM之间等等方面的问题,造成了很多的误用。
代码4.33 Singleton.java

package chapter4.pattern.singleton;
public class Singleton {
private static Singleton instance;
/**
* 不允许通过构造字来实例化
*/
private Singleton() {
}
/**
* 使用synchronized关键字,保障Singleton的线程安全
*/
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}


注意:Singleton模式应当谨慎使用。代码4.33中给出了一种合理的使用方法,通过将Singleton的构造子声明为私有,杜绝了外部或者子类的实例化倾向(当然这也是允许的)。
通过为getInstance()方法签署synchronized锁,保障了Singleton是线程安全的。此处所指的线程不安全是指在多线程环境下,可能产生不同Singleton实例。另外,getInstance()方法实际上是一种静态工厂方法,这是工厂模式的一个变种。考虑上节中工厂模式的例子,尝试结合Factory和Singleton,如代码4.34所示。
代码4.34 UtilServiceSingletonFactory.java
package chapter4.pattern.singleton;
import chapter4.pattern.factory.dipioc.ConcreteUtil;
import chapter4.pattern.factory.dipioc.UtilService;
public class UtilServiceSingletonFactory {
private static UtilServiceSingletonFactory instance;
/**
* 允许子类实例化
*/
protected UtilServiceSingletonFactory() {
}
public synchronized static UtilServiceSingletonFactory getInstance() {
if (instance == null) {
instance = new UtilServiceSingletonFactory();
}
return instance;
}
public UtilService make() {
return new ConcreteUtil();
}
}


客户代码可以是这样:
UtilService utilService = UtilServiceSingletonFactory.getInstance().make();
可以看到,通过单例工厂使得工厂对象实例数不超过1个,同时给出了Factory Method的实现(make方法),通过变换构造子修饰(private->protected),保留了Factory的派生性。如果不考虑派生性,完全有理由使用静态工厂而不是单例工厂,静态工厂使工厂类不需要被实例化,客户代码可以是这样:
UtilService utilService = UtilServiceSingletonFactory.make();

4.3.2 单例注册表
接下来将要介绍Singleton模式的另一种常用实现,即单例注册表(Singleton Registry),如代码4.35~4.39所示。
代码4.35 FactorySingletonRegistryUsage.java

package chapter4.pattern.singleton;
public class FactorySingletonRegistryUsage {
public static void main(String[] args) {
//实例化工厂注册表
FactorySingletonRegistry registry = FactorySingletonRegistry.getInstance();
//<- 通过反射机制,使得注册表可以依据给定的工厂全限定名返回具体工厂实例
//第一次索取
BeanFactory xmlBeanFactory1 = registry.getBeanFactory("chapter4.pattern.singleton.XmlBeanFactory");
BeanFactory listableBeanFactory1 = registry.getBeanFactory("chapter4.pattern.singleton.ListableBeanFactory");
//第二次索取
BeanFactory xmlBeanFactory2 = registry.getBeanFactory("chapter4.pattern.singleton.XmlBeanFactory");
BeanFactory listableBeanFactory2 = registry.getBeanFactory("chapter4.pattern.singleton.ListableBeanFactory");
//->
//比较先后两次索取的工厂实例,希望得到同一实例的工厂引用,结果正确
System.out.println(xmlBeanFactory1.hashCode() == xmlBeanFactory2.hashCode());
System.out.println(listableBeanFactory1.hashCode() == listableBeanFactory2.hashCode());
}
}


代码4.36 FactorySingletonRegistry.java
import java.util.HashMap;
import java.util.Map;
public class FactorySingletonRegistry {
private static FactorySingletonRegistry instance;
private static Map factoryMap = new HashMap();①
private FactorySingletonRegistry() {
}
public synchronized static FactorySingletonRegistry getInstance() {
if (instance == null) {
instance = new FactorySingletonRegistry();
}
return instance;
}
public synchronized BeanFactory getBeanFactory(String factoryClassName) {②
BeanFactory factory = (BeanFactory)factoryMap.get(factoryClassName);
if (factory != null) return factory;
try {
factory = (BeanFactory)Class.forName(factoryClassName).newInstance();③
} catch (ClassNotFoundException e) {
System.out.println("Couldn't find class " + factoryClassName);
} catch (InstantiationException e) {
System.out.println("Couldn't instantiate an object of type " + factoryClassName);
} catch (IllegalAccessException e) {
System.out.println("Couldn't access class " + factoryClassName);
}
factoryMap.put(factoryClassName, factory);
return factory;
}
}
代码4.37 BeanFactory.java
public interface BeanFactory {
}


代码4.38 ListableBeanFactory.java
public class ListableBeanFactory implements BeanFactory {
public ListableBeanFactory() {
System.out.println("ListableBeanFactory Created");
}
}
代码4.39 XmlBeanFactory.java
public class XmlBeanFactory implements BeanFactory {
public XmlBeanFactory() {
System.out.println("XmlBeanFactory Created");
}
}
对单例注册表的简单运作方式,做如下说明:
(1)请看代码4.36,首先这个类是一个单例类,在①处使用了Map对象,这是一个类,持有聚集的讯息。所谓聚集通常就是在Map中以名值对(field-value)的形式存储一系列各类对象的实例引用,通过put(field,value)存储,通过get(field)取得value。在代码4.36的getBeanFactory方法中,通过对factoryMap的存取,可以使该单例注册表持有任意数量的工厂实例,并且通过if (factory != null)的判断,保证了返回的是对同一个工厂实例的引用。
(2)注意代码4.36中的②处,getBeanFactory(String factoryClassName)是一个参数化的工厂方法,和在代码4.26中的类似。
说明:如果工厂方法依赖一个参数标识来决定产品的具体生产行为,那么可以说这就是一个参数化工厂方法。参数化工厂的好处是,创建同一产品族系(拥有同一接口的具体产品)时,不再需要衍生具体的工厂子类来对应。主要缺点是工厂职责过于集中,另外一个缺点仍然是所有传统工厂模式的通病,就是由于硬编码的关系,需要使用if/else语句来决定生产方式,无法摆脱Product = new ConcreteProduct()这种代码带来的强依赖性,同时限制了工厂创建产品的种类数目。这也意味着每当引入新的产品时,就需要重新变更关联代码。
(3)在代码4.36中引入了反射(Reflection)技术,如③所示。在代码4.35中,可以以类的全限定名作为参数传入工厂方法,工厂方法会通过反射技术来实例化那个类,而不需要预先在工厂方法中记载有限的产品集合了。
至此,给出了单例模式的基本用法,单例模式是一种常用的模式,但也很容易被误用。最后还提到了反射和工厂方法模式的一些结合,这是非常有用的技术。

你可能感兴趣的:(单例模式,Pattern,design)