return quotHello, I am RegSingleton.quot 它的子类 RegSingletonChild 需要父类的帮助才能实例化。
下图所示是登记式单例类子类的一个例子。
图中的关系表明,此类是由父类将子类实例化的。
下面是子类的源代码。
代码清单 4:登记式单例类的子类import java.util.HashMappublic class RegSingletonChild extends RegSingletonpublic RegSingletonChild / 静态工厂方法/static public RegSingletonChild getInstancereturn RegSingletonChildRegSingleton.getInstancequotcom.javapatterns.singleton.demos.RegSingletonChildquot / 一个示意性的商业方法/public String aboutreturn quotHello, I am RegSingletonChild.quot 在 GoF 原始的例子中,并没有 getInstance 方法,这样得到子类必须调用的getInstanceString name方法并传入子类的名字,因此很不方便。
本章在登记式单例类子类的例子里,加入了 getInstance 方法,这样做的好处是 RegSingletonChild 可以通过这个方法,返还自已的实例。
而这样做的缺点是,由于数据类型不同,无法在 RegSingleton 提供这样一个方法。
由于子类必须允许父类以构造子调用产生实例,因此,它的构造子必须是公开的。
这样一来,就等于允许了以这样方式产生实例而不在父类的登记中。
这是登记式单例类的一个缺点。
GoF 曾指出,由于父类的实例必须存在才可能有子类的实例,这在有些情况下是一个浪费。
这是登记式单例类的另一个缺点。
在什么情况下使用单例模式 使用单例模式的条件 使用单例模式有一个很重要的必要条件: 在一个系统要求一个类只有一个实例时才应当使用单例模式。
反过来说,如 果一个类可以有几个实例共存,那么就没有必要使用单例类。
但是有经验的读者 可能会看到很多不当地使用单例模式的例子,可见做到上面这一点并不容易,下 面就是一些这样的情况。
例子一 问:我的一个系统需要一些quot全程quot变量。
学习了单例模式后,我发现可以使用一个单例类盛放所有的quot全程quot变量。
请问这样做对吗? 答:这样做是违背单例模式的用意的。
单例模式只应当在有真正的quot单一实例quot的需求时才可使用。
一个设计得当的系统不应当有所谓的quot全程quot变量,这些变量应当放到它们所描述的实体所对应的类中去。
将这些变量从它们所描述的实体类中抽出来, 放到一个不相干的单例类中去,会使得这些变量产生错误的依赖关系和耦合关系。
例子二 问:我的一个系统需要管理与数据库的连接。
学习了单例模式后,我发现可以使用一个单例类包装一个 Connection 对象,并在 finalize方法中关闭这个Connection 对象。
这样的话,在这个单例类的实例没有被人引用时,这个finalize 对象就会被调用,因此,Connection 对象就会被释放。
这多妙啊。
答:这样做是不恰当的。
除非有单一实例的需求,不然不要使用单例模式。
在这里 Connection 对象可以同时有几个实例共存,不需要是单一实例。
单例模式有很多的错误使用案例都与此例子相似,它们都是试图使用单例模式管理共享资源的生命周期,这是不恰当的。
单例类的状态 有状态的单例类 一个单例类可以是有状态的(stateful),一个有状态的单例对象一般也是可变(mutable) 单例对象。
有状态的可变的单例对象常常当做状态库(repositary)使用。
比如一个单例对象可以持有一个 int 类型的属性,用来给一个系统提供一个数值惟一的序列号码,作为某个贩卖系统的账单号码。
当然,一个单例类可以持有一个聚集,从而允许存储多个状态。
没有状态的单例类 , 另一方面,单例类也可以是没有状态的(stateless) 仅用做提供工具性函数的对象。
既然是为了提供工具性函数,也就没有必要创建多个实例,因此使用单例模式很合适。
一个没有状态的单例类也就是不变(Immutable) 单例类;关于不变模式,读者可以参见本书的quot不变(Immutable )模式quot一章。
多个 JVM 系统的分散式系统 EJB 容器有能力将一个 EJB 的实例跨过几个 JVM 调用。
由于单例对象不是 EJB,因此,单例类局限于某一个 JVM 中。
换言之,如果 EJB 在跨过 JVM后仍然需要引用同一个单例类的话,这个单例类就会在数个 JVM 中被实例化,造成多个单例对象的实例出现。
一个 J2EE 应用系统可能分布在数个 JVM 中,这时候不一定需要 EJB 就能造成多个单例类的实例出现在不同 JVM 中的情况。
如果这个单例类是没有状态的,那么就没有问题。
因为没有状态的对象是没 有区别的。
但是如果这个单例类是有状态的, 那么问题就来了。
举例来说,如 果一个单例对象可以持有一个 int 类型的属性,用来给一个系统提供一个数值惟 一的序列号码,作为某个贩卖系统的账单号码的话,用户会看到同一个号码出现 好几次。
在任何使用了 EJB、RMI 和 JINI 技术的分散式系统中,应当避免使用有状态 的单例模式。
多个类加载器 同一个 JVM 中会有多个类加载器,当两个类加载器同时加载同一个类时, 会出现两个实例。
在很多 J2EE 服务器允许同一个服务器内有几个 Servlet 引 擎时,每一个引擎都有独立的类加载器,经有不同的类加载器加载的对象之间是 绝缘的。
一个实用的例子:属性管理器 什么是属性文件 这里给出一个读取属性(properties) 文件的单例类,作为单例模式的一个实用的例子。
属性文件如同老式的视窗编程时的.ini 文件,用于存放系统的配置信息。
配置信息在属性文件中以属性的方式存放,一个属性就是两个字符串组成的对子,其中一个字符串是键(key),另一个字符串是这个键的值(value)。
大多数的系统都有一些配置常量,这些常量如果是存储在程序内部的,那么每一次修改这些常量都需要重新编译程序。
将这些常量放在配置文件中,系统通过访问这个配置文件取得配置常量,就可以通过修改配置文件而无需修改程序而达到更改系统配置的目的。
系统也可以在配置文件中存储一些工作环境信息,这样在系统重启时,这些工作信息可以延续到下一个运行周期中。
假定需要读取的属性文件就在当前目录中,且文件名为 singleton.properties 。
这个文件中有如下的一些属性项。
代码清单 5:属性文件内容node1.item1Hownode1.item2arenode2.item1younode2.item2doingnode3.item1 例如,node1.item1 就是一个键,而 How 就是这个键所对应的值。
Java 属性类 Java 提供了一个工具类,称做属性类,可以用来完成 Java 属性和属性文件的操作。
这个属性类的继承关系可以从下面的类图中看清楚。
属性类提供了读取属性和设置属性的各种方法。
其中读取属性的方法有: .. containsObject value 、containsKeyObject key: 如果给定的参数或属性关键字在属性表中有定义,该方法返回 True ,否则返回 False。
.. getPropertyString key、getPropertyString key String default :根据给定的属性关键字获取关键字值。
.. listPrintStream s 、listPrintWriter w :在输出流中输出属性表内容。
.. size:返回当前属性表中定义的属性关键字个数。
设置属性的方法有: .. putObject key Object value :向属性表中追加属性关键字和关键字的值。
.. removeObject key:从属性表中删除关键字。
从属性文件加载属性的方法为 loadInputStream inStream,可以从一个输入流中读入一个属性列,如果这个流是来自一个文件的话,这个方法就从文件中读入属性。
将属性存入属性文件的方法有几个,重要的一个是 storeOutputStream outString header ,将当前的属性列写入一个输出流,
上一篇:
程序员能力矩阵
下一篇:
14年银行职员入党申请书格式