一个实用的例子:属性治理器
什么是属性文件
这里给出一个读取属性(PRoperties) 文件的单例类,作为单例模式的一个实用的例子。属性文件如同老式的视窗编程时的.ini 文件,用于存放系统的配置信息。配置信息在属性文件中以属性的方式存放,一个属性就是两个字符串组成的对子,其中一个字符串是键(key),另一个字符串是这个键的值(value)。
大多数的系统都有一些配置常量,这些常量假如是存储在程序内部的,那么每一次修改这些常量都需要重新编译程序。将这些常量放在配置文件中,系统通过访问这个配置文件取得配置常量,就可以通过修改配置文件而无需修改程序而达到更改系统配置的目的。系统也可以在配置文件中存储一些工作环境信息,这样在系统重启时,这些工作信息可以延续到下一个运行周期中。
假定需要读取的属性文件就在当前目录中,且文件名为singleton.properties 。这个文件中有如下的一些属性项。
代码清单5:属性文件内容
node1.item1=How
node1.item2=are
node2.item1=you
node2.item2=doing
node3.item1=?
例如,node1.item1 就是一个键,而How 就是这个键所对应的值。
java 属性类
Java 提供了一个工具类,称做属性类,可以用来完成Java 属性和属性文件的操作。这个属性类的继续关系可以从下面的类图中看清楚。
属性类提供了读取属性和设置属性的各种方法。其中读取属性的方法有:
.. contains(Object value) 、containsKey(Object key): 假如给定的参数或属性要害字在属性表中有定义,该方法返回True ,否则返回False。
.. getProperty(String key)、getProperty(String key, String default) :根据给定的属性要害字获取要害字值。
.. list(PrintStream s) 、list(PrintWriter w) :在输出流中输出属性表内容。
.. size():返回当前属性表中定义的属性要害字个数。
设置属性的方法有:
.. put(Object key, Object value) :向属性表中追加属性要害字和要害字的值。
.. remove(Object key):从属性表中删除要害字。
从属性文件加载属性的方法为load(InputStream inStream),可以从一个输入流中读入一个属性列,假如这个流是来自一个文件的话,这个方法就从文件中读入属性。
将属性存入属性文件的方法有几个,重要的一个是store(OutputStream out, String header) ,将当前的属性列写入一个输出流,假如这个输出流是导向一个文件的,那么这个方法就将属性流存入文件。进入讨论组讨论。
为什么需要使用单例模式
属性是系统的一种"资源",应当避免有多余一个的对象读取非凡是存储属性。此外,属性的读取可能会在很多地方发生,创建属性对象的地方应当在哪里不是很清楚。换言之,属性治理器应当自己创建自己的实例,并且自己向系统全程提供这一事例。因此,属性文件治理器应当是一个单例模式负责。
系统设计
系统的核心是一个属性治理器,也就是一个叫做ConfigManager 的类,这个类应当是一个单例类。因此,这个类应当有一个静态工厂方法,不妨叫做getInstance(), 用于提供自己的实例。
为简单起见,本文在这里采取"饿汉"方式实现ConfigManager 。例子的类图如下所示。
本例子的源代码如下所示。
代码清单6:ConfigManager 的源代码
import java.util.Properties;
import java.io.FileInputStream;
import java.io.File;
public class ConfigManager
{
/**
* 属性文件全名
*/
private static final String PFILE =
System.getProperty("user.dir")
+ File.Separator + "Singleton.properties";
/**
* 对应于属性文件的文件对象变量
*/
private File m_file = null;
/**
* 属性文件的最后修改日期
*/
private long m_lastModifiedTime = 0;
/**
* 属性文件所对应的属性对象变量
*/
private Properties m_props = null;
/**
* 本类可能存在的惟一的一个实例
*/
private static ConfigManager m_instance =
·234·Java 与模式
new 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)
{
// Get rid of the old properties
m_props.clear();
try
{
m_props.load(new FileInputStream(PFILE));
}
catch(Exception e)
{
e.printStackTrace();
}
}
m_lastModifiedTime = newTime;
Object val = m_props.getProperty(name);
if( val == null )
{
return defaultVal;
}
else
{
return val;
}
}
}
在上面直接使用了一个局域的常量储存储属性文件的路径。在实际的系统中,读者可以采取更灵活的方式将属性文件的路径传入。
读者可以看到,这个治理器类有一个很有意思的功能,即在每一次调用时,检查属性文件是否已经被更新过。假如确实已经被更新过的话,治理器会自动重新加载属性文件, 从而保证治理器的内容与属性文件的内容总是一致的。
怎样调用属性治理器
下面的源代码演示了怎样调用ConfigManager 来读取属性文件。
代码清单7:怎样调用ConfigManager 类以读取属性文件