登记式单态类
登记式单态类是GoF为了克服饿汉式单态类及懒汉式式单态类均不可继承的缺点而设计的。 作者把他们的例子翻译为爪哇语言,并将它自己实例化的方式从懒汉式改为饿汉式。只是它的 子类实例化的方式只能是懒汉式的,这是无法改变的。
图4. 登记式单态类的一个例子
图中的关系线表明,此类自已将自己实例化。
package com.javapatterns.singleton.demos;
import java.util.HashMap;
public class RegSingleton {
protected RegSingleton() {}
static public RegSingleton getInstance(String name)
{
if (name == null)
{
name = "com.javapatterns.singleton.demos.RegSingleton";
}
if (m_registry.get(name) == null)
{
try
{
m_registry.put( name, Class.forName(name).newInstance() ) ;
}
catch(Exception e)
{
System.out.println("Error happened.");
}
}
return (RegSingleton) (m_registry.get(name) );
}
static private HashMap m_registry = new HashMap();
static
{
RegSingleton x = new RegSingleton();
m_registry.put( x.getClass().getName() , x);
}
public String about()
{
return "Hello, I am RegSingleton.";
}
}
代码清单4. 登记式单态类。(注意为简单起见,这里没有考虑多线程访问限制的问题,读者可自行加入一个有双重 检查的访问限制)
它的子类
图5. 登记式单态类子类的一个例子。
图中的关系线表明,此类是由父类将自己实例化的。
package com.javapatterns.singleton.demos;
import java.util.HashMap;
public class RegSingletonChild extends RegSingleton
{
public RegSingletonChild() {}
static public RegSingletonChild getInstance()
{
return (RegSingletonChild) RegSingleton.getInstance(
"com.javapatterns.singleton.demos.RegSingletonChild" );
}
public String about()
{
return "Hello, I am RegSingletonChild.";
}
}
代码清单5. 登记式单态类的子类。
在GoF原始的例子中,并没有getInstance()方法,这样得到子类必须调用文类的getInstance(String name) 方法,并传入子类的名字,很不方便。 作者在登记式单态类子类的例子里,加入了getInstance()方法,这样做的好处是RegSingletonChild 可以通过这个方法,返还自已的实例,而这样做的缺点是,由于数据类型不同,无法在RegSingleton提供 这样一个方法。
由于子类必须充许父类以构造子调用产生实例,因此它的构造子必须是公开的。这样一来,就等于允许了 以这样方式产生实例而不在父类的登记中。这是登记式单态类的一个缺点。
GoF曾指出,由于父类的实例必须存在才可能有子类的实例,这在有些情况下是一个浪费。 这是登记式单态类的另一个缺点。
爪哇语言里的垃圾回收
爪哇语言里垃圾回收使得单态类的使用变得有点复杂。原因就在于JDK1.1版里加进去的类的自动清除。 这种类的垃圾回收会清除掉类本身,而不仅仅是对象!事实上JDK1.1甚至可以清除掉一些系统类!
在JDK1.0.x版本里,类的自动清除尚未加入。
在JDK1.2及以后的版本里,升阳公司又收紧了类的垃圾回收规则,它规定,所有通过局部的和系统的 类加载器加载的类,永不被回收。并且,通过其它类加载器加载的类,只有在加载器自己被回收后才可被回收。
在1.1版JDK里使用单态类的读者,如果不了解这一版爪哇语言的特点,很有可能会遇到类消失掉的奇特问题。 为了使你的单态类能在所有版本的爪哇环境里使用,作者特别提供一个"看守"类程序,它能保证你的单态类, 甚至其它任何对象,一旦交给"看守"对象,即不会莫名其妙地被垃圾回收器回收,直到你把它从"看守" 那里把它释放出来。
图6. "看守"类的一个例子
package com.javapatterns.singleton.demos;
import java.util.Vector;
/**
* This class keeps your objects from garbage collected
*/
public class ObjectKeeper extends Thread {
private ObjectKeeper()
{
new Thread(this).start();
}
public void run()
{
try { join(); }
catch (InterruptedException e) {}
}
/**
* Any object passed here will be kept until you call discardObject()
*/
public static void keepObject(Object myObject)
{
System.out.println(" Total number of kept objects: " + m_keptObjects.size());
m_keptObjects.add(myObject);
System.out.println(" Total number of kept objects: " + m_keptObjects.size());
}
/**
* This method will remove the protect of the object you pass in and make it
* available for Garbage Collector to collect.
*/
public static void discardObject(Object myObject)
{
System.out.println(" Total number of kept objects: " + m_keptObjects.size());
m_keptObjects.remove(myObject);
System.out.println(" Total number of kept objects: " + m_keptObjects.size());
}
private static ObjectKeeper m_keeper = new ObjectKeeper();
private static Vector m_keptObjects = new Vector();
}
代码清单6. 看守类的一个实现。
看守类应当自我实例化,而且在每个系统里只需一个实例。这就意味着看守类本身就应当是单态类。当然,类 消失的事情绝不可以发生在它自己身上。作者提供的例子刚好满足所有的要求。
一个实用的例子
这里作者给出一个读取属性(properties)文件的单态类,作为单态类的一个实用的例子。 属性文件如同老式的视窗编程时的.ini文件,属于系统的“资源“,而读取属性文件即为资源管理, 显然应当由一个单态类负责。
图7. 这个例子的UML
显然,在大多数的系统中都会涉及属性文件的读取问题,因而这个例子非常有实用价值。 在这个例子里,作者假定需要读取的属性文件就在当前目录中,且名为singleton.properties。 在这个文件中有如下的一些属性项:
node1.item1=How
node1.item2=are
node2.item1=you
node2.item2=doing
node3.item1=?
代码清单7. 属性文件内容
本例子的源代码如下:
package com.javapatterns.singleton.demos;
import java.util.Properties;
import java.io.FileInputStream;
import java.io.File;
public class ConfigManager
{
/**
* 私有的构造子, 用以保证实例化的唯一性
*/
private ConfigManager()
{
m_file = new File(PFILE);
m_lastModifiedTime = m_file.lastModified();
if(m_lastModifiedTime == 0)
{
System.err.println(PFILE + " file does not exist!");
}
m_props = new Properties();
try
{
m_props.load(new FileInputStream(PFILE));
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
*
* @return 返还ConfigManager类的单一实例
*/
synchronized public static ConfigManager getInstance()
{
return m_instance;
}
/**
* 读取一特定的属性项
*
* @param name 属性项的项名
* @param defaultVal 属性项的缺省值
* @return 属性项的值(如此项存在), 缺省值(如此项不存在)
*/
final public Object getConfigItem(String name, Object defaultVal)
{
long newTime = m_file.lastModified();
// 检查属性文件是否被其它程序(多数情况是程序员手动)修改过。
// 如果是,重新读取此文件。
if(newTime == 0)
{
// 属性文件不存在
if(m_lastModifiedTime == 0)
{
System.err.println(PFILE + " file does not exist!");
}
else
{
System.err.println(PFILE + " file was deleted!!");
}
return defaultVal;
}
else if(newTime > m_lastModifiedTime)
{
m_props.clear