ImageVerifierCode 换一换
格式:DOC , 页数:23 ,大小:267KB ,
资源ID:963026      下载积分:10 积分
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 微信支付   
验证码:   换一换

加入VIP,免费下载资源
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【http://www.wodocx.com/d-963026.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Java模式设计之单例模式.doc)为本站会员(精***)主动上传,沃文网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知沃文网(发送邮件至2622162128@qq.com或直接QQ联系客服),我们立即给予删除!

Java模式设计之单例模式.doc

1、Java模式设计之单例模式(一):单例模式的要点单例单例显然单例模式的要点有三个;一是某各类只能有一个实例;二是它必须自行创建这个事例;三是它必须自行向整个系统提供这个实例。在下面的对象图中,有一个单例对象,而客户甲、客户乙 和客户丙是单例对象的三个客户对象。可以看到,所有的客户对象共享一个单例对象。而且从单例对象到自身的连接线可以看出,单例对象持有对自己的引用。资源管理一些资源管理器常常设计成单例模式。在计算机系统中,需要管理的资源包括软件外部资源,譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler, 以避免两个打印作业同时输出到打印机中。每台计算机可以有若干传真卡

2、,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。需要管理的资源包括软件内部资源,譬如,大多数的软件都有一个(甚至多个)属性(properties)文件存放系统配置。这样的系统应当由一个对象来管理一个属性文件。 需要管理的软件内部资源也包括譬如负责记录网站来访人数的部件,记录软件系统内部事件、出错信息的部件,或是对系统的表现进行检查的部件等。这些部件都必须集中管理,不可政出多头。这些资源管理器构件必须只有一个实例,这是其一;它们必须自行初始化,这是其二;允许整

3、个系统访问自己这是其三。因此,它们都满足单例模式的条件,是单例模式的应用。一个例子:Windows 回收站Windows 9x 以后的视窗系统中都有一个回收站,下图就显示了Windows 2000 的回收站。在整个视窗系统中,回收站只能有一个实例,整个系统都使用这个惟一的实例,而且回收站自行提供自己的实例。因此,回收站是单例模式的应用。双重检查成例在本章最后的附录里研究了双重检查成例。双重检查成例与单例模式并无直接的关系,但是由于很多C 语言设计师在单例模式里面使用双重检查成例,所以这一做法也被很多Java 设计师所模仿。因此,本书在附录里提醒读者,双重检查成例在Java 语言里并不能成立,详

4、情请见本章的附录。单例模式的结构单例模式有以下的特点:. 单例类只可有一个实例。. 单例类必须自己创建自己这惟一的实例。. 单例类必须给所有其他对象提供这一实例。虽然单例模式中的单例类被限定只能有一个实例,但是单例模式和单例类可以很容易被推广到任意且有限多个实例的情况,这时候称它为多例模式(Multiton Pattern) 和多例类(Multiton Class),请见专题:多例(Multiton )模式与多语言支持一章。单例类的简略类图如下所示。由于Java 语言的特点,使得单例模式在Java 语言的实现上有自己的特点。这些特点主要表现在单例类如何将自己实例化上。饿汉式单例类饿汉式单例类是

5、在Java 语言里实现得最为简便的单例类,下面所示的类图描述了一个饿汉式单例类的典型实现。从图中可以看出,此类已经自已将自己实例化。代码清单1:饿汉式单例类public class EagerSingleton private static final EagerSingleton m_instance = new EagerSingleton(); /* * 私有的默认构造子 */ private EagerSingleton() /* * 静态工厂方法 */ public static EagerSingleton getInstance() 224Java 与模式 return m_in

6、stance; 读者可以看出,在这个类被加载时,静态变量m_instance 会被初始化,此时类的私有构造子会被调用。这时候,单例类的惟一实例就被创建出来了。Java 语言中单例类的一个最重要的特点是类的构造子是私有的,从而避免外界利用构造子直接创建出任意多的实例。值得指出的是,由于构造子是私有的,因此,此类不能被继承。懒汉式单例类与饿汉式单例类相同之处是,类的构造子是私有的。与饿汉式单例类不同的是,懒汉式单例类在第一次被引用时将自己实例化。如果加载器是静态的,那么在懒汉式单例类被加载时不会将自己实例化。如下图所示,类图中给出了一个典型的饿汉式单例类实现。代码清单2:懒汉式单例类package

7、 com.javapatterns.singleton.demos; public class LazySingleton private static LazySingleton m_instance = null; /* * 私有的默认构造子,保证外界无法直接实例化 */ private LazySingleton() /* * 静态工厂方法,返还此类的惟一实例 */ synchronized public static LazySingleton getInstance() if (m_instance = null) m_instance = new LazySingleton();

8、return m_instance; 读者可能会注意到,在上面给出懒汉式单例类实现里对静态工厂方法使用了同步化,以处理多线程环境。有些设计师在这里建议使用所谓的双重检查成例。必须指出的是,双重检查成例不可以在Java 语言中使用。不十分熟悉的读者,可以看看后面给出的小节。同样,由于构造子是私有的,因此,此类不能被继承。饿汉式单例类在自己被加载时就将自己实例化。即便加载器是静态的,在饿汉式单例类被加载时仍会将自己实例化。单从资源利用效率角度来讲,这个比懒汉式单例类稍差些。从速度和反应时间角度来讲,则比懒汉式单例类稍好些。然而,懒汉式单例类在实例化时, 必须处理好在多个线程同时首次引用此类时的访问

9、限制问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费时间。这意味着出现多线程同时首次引用此类的机率变得较大。饿汉式单例类可以在Java 语言内实现, 但不易在C+ 内实现,因为静态初始化在C+ 里没有固定的顺序,因而静态的m_instance 变量的初始化与类的加载顺序没有保证,可能会出问题。这就是为什么GoF 在提出单例类的概念时,举的例子是懒汉式的。他们的书影响之大,以致Java 语言中单例类的例子也大多是懒汉式的。实际上,本书认为饿汉式单例类更符合Java 语言本身的特点。 登记式单例类登记式单例类是GoF 为了克服饿汉式单例类及懒汉式单例类均不

10、可继承的缺点而设计的。本书把他们的例子翻译为Java 语言,并将它自己实例化的方式从懒汉式改为饿汉式。只是它的子类实例化的方式只能是懒汉式的, 这是无法改变的。如下图所示是登记式单例类的一个例子,图中的关系线表明,此类已将自己实例化。代码清单3:登记式单例类 import java.util.HashMap; public class RegSingleton static private HashMap m_registry = new HashMap(); static RegSingleton x = new RegSingleton(); m_registry.put( x.getCl

11、ass().getName() , x); /* * 保护的默认构造子 */ 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).newInstan

12、ce() ) ; catch(Exception e) System.out.println(Error happened.); return (RegSingleton) (m_registry.get(name) ); /* * 一个示意性的商业方法 */ public String about() return Hello, I am RegSingleton.; 它的子类RegSingletonChild 需要父类的帮助才能实例化。下图所示是登记式单例类子类的一个例子。图中的关系表明,此类是由父类将子类实例化的。下面是子类的源代码。代码清单4:登记式单例类的子类import java.

13、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(

14、) return Hello, I am RegSingletonChild.; 在GoF 原始的例子中,并没有getInstance() 方法,这样得到子类必须调用的getInstance(String name)方法并传入子类的名字,因此很不方便。本章在登记式单例类子类的例子里,加入了getInstance() 方法,这样做的好处是RegSingletonChild 可以通过这个方法,返还自已的实例。而这样做的缺点是,由于数据类型不同,无法在RegSingleton 提供这样一个方法。由于子类必须允许父类以构造子调用产生实例,因此,它的构造子必须是公开的。这样一来,就等于允许了以这样方式产

15、生实例而不在父类的登记中。这是登记式单例类的一个缺点。GoF 曾指出,由于父类的实例必须存在才可能有子类的实例,这在有些情况下是一个浪费。这是登记式单例类的另一个缺点。Java模式设计之单例模式(二)使用单例模式的条件使用单例模式有一个很重要的必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反过来说,如果一个类可以有几个实例共存,那么就没有必要使用单例类。但是有经验的读者可能会看到很多不当地使用单例模式的例子,可见做到上面这一点并不容易,下面就是一些这样的情况。例子一问:我的一个系统需要一些全程变量。学习了单例模式后,我发现可以使用一个单例类盛放所有的全程变量。请问这样做对吗?

16、答:这样做是违背单例模式的用意的。单例模式只应当在有真正的单一实例的需求时才可使用。一个设计得当的系统不应当有所谓的全程变量,这些变量应当放到它们所描述的实体所对应的类中去。将这些变量从它们所描述的实体类中抽出来, 放到一个不相干的单例类中去,会使得这些变量产生错误的依赖关系和耦合关系。例子二问:我的一个系统需要管理与数据库的连接。学习了单例模式后,我发现可以使用一个单例类包装一个Connection 对象,并在finalize()方法中关闭这个Connection 对象。这样的话,在这个单例类的实例没有被人引用时,这个finalize() 对象就会被调用,因此,Connection 对象就会

17、被释放。这多妙啊。答:这样做是不恰当的。除非有单一实例的需求,不然不要使用单例模式。在这里Connection 对象可以同时有几个实例共存,不需要是单一实例。单例模式有很多的错误使用案例都与此例子相似,它们都是试图使用单例模式管理共享资源的生命周期,这是不恰当的。单例类的状态有状态的单例类一个单例类可以是有状态的(stateful),一个有状态的单例对象一般也是可变(mutable) 单例对象。有状态的可变的单例对象常常当做状态库(repositary)使用。比如一个单例对象可以持有一个int 类型的属性,用来给一个系统提供一个数值惟一的序列号码,作为某个贩卖系统的账单号码。当然,一个单例类可

18、以持有一个聚集,从而允许存储多个状态没有状态的单例类另一方面,单例类也可以是没有状态的(stateless), 仅用做提供工具性函数的对象。既然是为了提供工具性函数,也就没有必要创建多个实例,因此使用单例模式很合适。一个没有状态的单例类也就是不变(Immutable) 单例类; 关于不变模式,读者可以参见本书的不变(Immutable )模式一章。多个JVM 系统的分散式系统EJB 容器有能力将一个EJB 的实例跨过几个JVM 调用。由于单例对象不是EJB,因此,单例类局限于某一个JVM 中。换言之,如果EJB 在跨过JVM 后仍然需要引用同一个单例类的话,这个单例类就会在数个JVM 中被实例

19、化,造成多个单例对象的实例出现。一个J2EE应用系统可能分布在数个JVM 中,这时候不一定需要EJB 就能造成多个单例类的实例出现在不同JVM 中的情况。如果这个单例类是没有状态的,那么就没有问题。因为没有状态的对象是没有区别的。但是如果这个单例类是有状态的, 那么问题就来了。举例来说,如果一个单例对象可以持有一个int 类型的属性,用来给一个系统提供一个数值惟一的序列号码,作为某个贩卖系统的账单号码的话,用户会看到同一个号码出现好几次。在任何使用了EJB、RMI 和JINI 技术的分散式系统中,应当避免使用有状态的单例模式。多个类加载器同一个JVM 中会有多个类加载器,当两个类加载器同时加载

20、同一个类时,会出现两个实例。在很多J2EE 服务器允许同一个服务器内有几个Servlet 引擎时,每一个引擎都有独立的类加载器,经有不同的类加载器加载的对象之间是绝缘的。比如一个J2EE 系统所在的J2EE 服务器中有两个Servlet 引擎:一个作为内网给公司的网站管理人员使用;另一个给公司的外部客户使用。两者共享同一个数据库,两个系统都需要调用同一个单例类。如果这个单例类是有状态的单例类的话,那么内网和外网用户看到的单例对象的状态就会不同。除非系统有协调机制,不然在这种情况下应当尽量避免使用有状态的单例类。Java模式设计之单例模式(三):一个实用的例子:属性管理器什么是属性文件这里给出一

21、个读取属性(properties) 文件的单例类,作为单例模式的一个实用的例子。属性文件如同老式的视窗编程时的.ini 文件,用于存放系统的配置信息。配置信息在属性文件中以属性的方式存放,一个属性就是两个字符串组成的对子,其中一个字符串是键(key),另一个字符串是这个键的值(value)。大多数的系统都有一些配置常量,这些常量如果是存储在程序内部的,那么每一次修改这些常量都需要重新编译程序。将这些常量放在配置文件中,系统通过访问这个配置文件取得配置常量,就可以通过修改配置文件而无需修改程序而达到更改系统配置的目的。系统也可以在配置文件中存储一些工作环境信息,这样在系统重启时,这些工作信息可以

22、延续到下一个运行周期中。假定需要读取的属性文件就在当前目录中,且文件名为singleton.properties 。这个文件中有如下的一些属性项。代码清单5:属性文件内容node1.item1=How node1.item2=are node2.item1=you node2.item2=doing node3.item1=?例如,node1.item1 就是一个键,而How 就是这个键所对应的值。Java 属性类Java 提供了一个工具类,称做属性类,可以用来完成Java 属性和属性文件的操作。这个属性类的继承关系可以从下面的类图中看清楚。属性类提供了读取属性和设置属性的各种方法。其中读取属

23、性的方法有:. 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, Obje

24、ct value) :向属性表中追加属性关键字和关键字的值。. remove(Object key):从属性表中删除关键字。从属性文件加载属性的方法为load(InputStream inStream),可以从一个输入流中读入一个属性列,如果这个流是来自一个文件的话,这个方法就从文件中读入属性。将属性存入属性文件的方法有几个,重要的一个是store(OutputStream out, String header) ,将当前的属性列写入一个输出流,如果这个输出流是导向一个文件的,那么这个方法就将属性流存入文件。为什么需要使用单例模式属性是系统的一种资源,应当避免有多余一个的对象读取特别是存储属性

25、。此外,属性的读取可能会在很多地方发生,创建属性对象的地方应当在哪里不是很清楚。换言之,属性管理器应当自己创建自己的实例,并且自己向系统全程提供这一事例。因此,属性文件管理器应当是一个单例模式负责。系统设计系统的核心是一个属性管理器,也就是一个叫做ConfigManager 的类,这个类应当是一个单例类。因此,这个类应当有一个静态工厂方法,不妨叫做getInstance(), 用于提供自己的实例。为简单起见,本文在这里采取饿汉方式实现ConfigManager 。例子的类图如下所示。本例子的源代码如下所示。代码清单6:ConfigManager 的源代码import java.util.Pro

26、perties; 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_lastM

27、odifiedTime = 0; /* * 属性文件所对应的属性对象变量 */ private Properties m_props = null; /* * 本类可能存在的惟一的一个实例 */ private static ConfigManager m_instance = 234Java 与模式 new ConfigManager(); /* * 私有的构造子,用以保证外界无法直接实例化 */ private ConfigManager() m_file = new File(PFILE); m_lastModifiedTime = m_file.lastModified(); if(m

28、_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_ins

29、tance; /* * 读取一特定的属性项 * * param name 属性项的项名 * param defaultVal 属性项的默认值 * return 属性项的值(如此项存在), 默认值(如此项不存在) */ final public Object getConfigItem( String name, Object defaultVal) long newTime = m_file.lastModified(); / 检查属性文件是否被其他程序 / (多数情况是程序员手动)修改过 / 如果是,重新读取此文件if(newTime = 0) / 属性文件不存在 if(m_lastModi

30、fiedTime = 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.printStackTrac

31、e(); m_lastModifiedTime = newTime; Object val = m_props.getProperty(name); if( val = null ) return defaultVal; else return val; 在上面直接使用了一个局域的常量储存储属性文件的路径。在实际的系统中,读者可以采取更灵活的方式将属性文件的路径传入。读者可以看到,这个管理器类有一个很有意思的功能,即在每一次调用时,检查属性文件是否已经被更新过。如果确实已经被更新过的话,管理器会自动重新加载属性文件, 从而保证管理器的内容与属性文件的内容总是一致的。怎样调用属性管理器下面的源代

32、码演示了怎样调用ConfigManager 来读取属性文件。代码清单7:怎样调用ConfigManager 类以读取属性文件 BufferedReader reader = new BufferedReader( new InputStreamReader(System.in); System.out.println(Type quit to quit); do System.out.print(Property item to read: ); String line = reader.readLine(); if(line.equals(quit) break; System.out.pr

33、intln(ConfigManager.getInstance() .getConfigItem(line, Not found.); while(true); 上面代码运行时的情况如下图所示。感兴趣的读者可以参考阅读本书的专题:XMLProperties 与适配器模式一章,那里对使用Java 属性类和XML 文件格式做了有用的讨论。Java 语言中的单例模式Java 语言中就有很多的单例模式的应用实例,这里讨论比较有名的几个。Java 的Runtime 对象在Java 语言内部,java.lang.Runtime 对象就是一个使用单例模式的例子。在每一个Java 应用程序里面,都有惟一的一个

34、Runtime 对象。通过这个Runtime 对象,应用程序可以与其运行环境发生相互作用。Runtime 类提供一个静态工厂方法getRuntime():: public static Runtime getRuntime();通过调用此方法,可以获得Runtime 类惟一的一个实例: Runtime rt = Runtime getRuntime(); Runtime 对象通常的用途包括:执行外部命令;返回现有内存即全部内存;运行垃圾收集器;加载动态库等。下面的例子演示了怎样使用Runtime 对象运行一个外部程序。代码清单8:怎样使用Runtime 对象运行一个外部命令import jav

35、a.io.*; public class CmdTest public static void main(String args) throws IOException Process proc = Runtime.getRuntime().exec(notepad.exe); 上面的程序在运行时会打开notepad 程序。应当指出的是,在Windows 2000 的环境中,如果需要打开一个Word 文件,而又不想指明Word 软件安装的位置时,可以使用下面的做法: Process proc = Runtime.getRuntime().exec( cmd /E:ON /c start MyD

36、ocument.doc); 在上面,被执行的命令是start MyDocument.doc ,开关E:ON 指定DOS 命令处理器允许命令扩展,而开关/C 指明后面跟随的字符串是命令,并在执行命令后关闭DOS 窗口,start 命令会开启一个单独的窗口执行所提供的命令。Introspector 类一般的应用程序可能永远也不会直接用到Introspector 类,但读者应该知道Introspector 是做什么的。Sun 提供了一个叫做BeanBox 的系统,允许动态地加载JavaBean ,并动态地修改其性质。BeanBox 在运行时的情况如下图所示。在上面的图中显示了BeanBox 最重要的

37、两个视窗,一个叫做BeanBox 视窗,另一个叫做性质视窗。在上面的BeanBox 视窗中显示了一个Juggler Bean 被放置到视窗中的情况。相应的,在性质视窗中显示了Juggler Bean 的所有性质。所有的Java 集成环境都提供这种功能,这样的系统就叫做BeanBox 系统。BeanBox 系统使用一种自省(Introspection )过程来确定一个Bean 所输出的性质、事件和方法。这个自省机制是通过自省者类,也即java.util.Introspector 类实现的;这个机制是建立在Java 反射(Reflection) 机制和命名规范的基础之上的。比如,Introspec

38、tor 类可以确定Juggler Bean 所支持的所有的性质,这是因为Introspector 类可以得到所有的方法,然后将其中的取值和赋值方法以及它们的特征加以比较,从而得出结果。显然,在整个BeanBox 系统中只需要一个Introspector 对象,下面所示就是这个类的结构图。可以看出,Introspector 类的构造子是私有的, 一个静态工厂方法instantiate() 提供了Instrospector 类的惟一实例。换言之,这个类是单例模式的应用。java.awt.Toolkit 类Toolkit 类是一个非常有趣的单例模式的例子。Toolkit 使用单例模式创建所谓的Too

39、lkit 的默认对象,并且确保这个默认实例在整个系统中是惟一的。Toolkit 类提供了一个静态的方法getDefaultToolkit() 来提供这个惟一的实例,这个方法相当于懒汉式的单例方法,因此整个方法都是同步化的。代码清单9:getDefaultToolkit() 方法 public static synchronized Toolkit getDefaultToolkit() . Toolkit 类的类图如下所示。其中性质defaultToolkit 实际上就是静态的getDefaultToolkit 类。有趣的是,由于Toolkit 是一个抽象类,因此其子类如果提供一个私有的构造子

40、,那么其子类便是一个正常的单例类;而如果其子类作为具体实现提供一个公开的构造子, 这时候这个具体子类便是 不完全的单例类。关于不完全的单例类的讨论请见本章后面的专题:不完全的单例类一节。模版方法模式同时,熟悉模版方法模式的读者可以看出,getDefaultToolkit() 方法实际上是一个模版方法。私有构造子是推迟到子类实现的剩余逻辑,根据子类对这个剩余逻辑的不同实现, 子类就可以提供完全不同的行为。对Toolkit 的子类而言,私有构造子依赖于操作系统,不同的子类可以根据不同的操作系统而给出不同的逻辑,从而使Toolkit 的子类对不同的操作系统给出不同的行为。javax.swing.Ti

41、merQueue 类这是一个不完全的单例类,由于这个类是在Swing 的定时器类中使用的,因此我们将在后面介绍。Java模式设计之单例模式(四):不完全的单例类什么是不完全的单例类估计有些读者见过下面这样的“不完全”的单例类。代码清单10:“不完全”单例类package com.javapatterns.singleton.demos; public class LazySingleton private static LazySingleton m_instance = null; /* * 公开的构造子,外界可以直接实例化 */ public LazySingleton() /* * 静态

42、工厂方法 * return 返还LazySingleton 类的惟一实例 */ synchronized public static LazySingleton getInstance() if (m_instance = null) m_instance = new LazySingleton(); return m_instance; 上面的代码乍看起来是一个“懒汉”式单例类,仔细一看,发现有一个公开的构造子。由于外界可以使用构造子创建出任意多个此类的实例,这违背了单例类只能有一个(或有限个)实例的特性,因此这个类不是完全的单例类。这种情况有时会出现,比如javax.swing.Timer

43、Queue 便是一例,关于这个类,请参见Java与模式一书中的“观察者模式与Swing 定时器” 一章。造成这种情况出现的原因有以下几种可能:(1) 初学者的错误。许多初学者没有认识到单例类的构造子不能是公开的,因此犯下这个错误。有些初学Java 语言的学员甚至不知道一个Java 类的构造子可以不是公开的。在 这种情况下,设计师可能会通过自我约束,也就是说不去调用构造子的办法,将这个不完全的单例类在使用中作为一个单例类使用。在这种情况下,一个简单的矫正办法,就是将公开的构造子改为私有的构造子。(2) 当初出于考虑不周,将一个类设计成为单例类,后来发现此类应当有多于一个的实例。为了弥补错误, 干

44、脆将构造子改为公开的,以便在需要多于一个的实例时, 可以随时调用构造子创建新的实例。要纠正这种情况较为困难,必须根据具体情况做出改进的决定。如果一个类在最初被设计成为单例类,但后来发现实际上此类应当有有限多个实例,这时候应当考虑是否将单例类改为多例类(Multiton)。(3)设计师的Java 知识很好,而且也知道单例模式的正确使用方法,但是还是有意使用这种不完全的单例模式,因为他意在使用一种“改良”的单例模式。这时候, 除去共有的构造子不符合单例模式的要求之外,这个类必须是很好的单例模式。默认实例模式有些设计师将这种不完全的单例模式叫做“默认实例模式”(Default Instance Pa

45、ttern)。在所谓的“ 默认实例模式”里面, 一个类提供静态的方法,如同单例模式一样, 同时又提供一个公开的构造子,如同普通的类一样。这样做的惟一好处是,这种模式允许客户端选择如何将类实例化:创建新的自己独有的实例,或者使用共享的实例。这样一来,由于没有任何的强制性措施,客户端的选择不一定是合理的选择。其结果是设计师往往不会花费时间在如何提供最好的选择上,而是不恰当地将这种选择交给客户端的程序员,这样必然会导致不理想的设计和欠考虑的实现。相关模式有一些模式可以使用单例模式,如抽象工厂模式可以使用单例模式,将具体工厂类设计成单例类;建造模式可以使用单例模式,将具体建造类设计成单例类。多例(Multiton)模式正如同本章所说的,单例模式的精神可以推广到多于一个实例的情况。这时候这种类叫做多例类,这种模式叫做多例模式。单例类(左)和多例类(右)的类

版权声明:以上文章中所选用的图片及文字来源于网络以及用户投稿,由于未联系到知识产权人或未发现有关知识产权的登记,如有知识产权人并不愿意我们使用,如有侵权请立即联系:2622162128@qq.com ,我们立即下架或删除。

Copyright© 2022-2024 www.wodocx.com ,All Rights Reserved |陕ICP备19002583号-1 

陕公网安备 61072602000132号     违法和不良信息举报:0916-4228922