单例模式(Singleton)

4.3 单例模式(Design Pattern:Singleton)

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

如图4.8和代码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中,可以以类的全限定名作为参数传入工厂方法,工厂方法会通过反射技术来实例化那个类,而不需要预先在工厂方法中记载有限的产品集合了。
至此,给出了单例模式的基本用法,单例模式是一种常用的模式,但也很容易被误用。最后还提到了反射和工厂方法模式的一些结合,这是非常有用的技术。

转自:http://book.csdn.net/bookfiles/92/100922653.shtml

posted @ 2009-01-05 23:06  kelin1314  阅读(669)  评论(0编辑  收藏  举报