创建模式中的单例模式

来源:互联网 发布:现金流量适合比率算法 编辑:程序博客网 时间:2024/04/30 11:55

创建模式中的单例模式单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式的要点有三个: 一个是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须向整个系统提供这个实例。饿汉式单例类:饿汉式单例类是在Java语言里实现起来最为简便的单例类,下面的代码描述了一个这样的单例类。 package com.test; public class ObjectPoolFactory { /** * 饿汉式单例类 */ private static final ObjectPoolFactory factory = new ObjectPoolFactory(); /** * 私有默认的构造子 */ private ObjectPoolFactory() { } /** * 静态工厂方法 * @return 池工厂 */ public static ObjectPoolFactory getInstance() { return factory; } /** * * @param paraObj 对象池参数对象 * @param clsType 所创建对象类型 * @return 对象池 */ public ObjectPool createPool(ParameterObject paraObj, Class clsType) { return new ObjectPool(paraObj, clsType); } } 读者可以看出,在这个类被加装时,静态变量factory会被初始化,此时私有的构造子会被调用,这时候,单例类的惟一实例就被创建了。Java语言中单例类的一个最重要的特点是类的构造子时私有的,从而避免外界利用构造子直接创建任意多的实例。值得注意的是,由于构造子时私有的,因此此类不能被继承。懒汉式单例类与饿汉式单例类相同之处,类的构造子时私有的。与饿汉式不同的是,懒汉式单例类在第一次被引用时将自己实例化。如果加载器是静态的,那么在懒汉式单例类被加载时不会将自己实例化。 package com.test; public class PoolableObjectFactory { private static PoolableObjectFactory factory; /** * 私有的默认构造子,保证外界无法直接实例化 */ private PoolableObjectFactory() { } /** * 静态工厂方法,返回此类的惟一实例 * @return */ public static synchronized PoolableObjectFactory getInstance() { if (factory == null) { factory = new PoolableObjectFactory(); } return factory; } public Object createObject(Class clsType) { Object obj = null; try { obj = clsType.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return obj; } } 上面给出的懒汉式单例类实现里对静态工厂方法使用了同步化,以处理多线程环境。有些设计师在这里建议使用所谓的“双重检查成例”,必须指出的是,“双重检查成例”不可以在Java语言中使用。同样,由于构造子私有的,因此,此类不能被继承。饿汉式单例类可以在Java语言内实现,但不易在C++内实现,因为静态初始化在C++里没有固定的顺序,因而静态变量的初始化与类的加载顺序没有保证,可能会出现大问题。登记式单例类登记式单例类是GoF为了克服饿汉式单例类与懒汉式单例类均不能继承的缺点而设计的。 package com.test.singleton; import java.util.HashMap; public class RegSingleton { private static HashMap m_registry = new HashMap(); static { RegSingleton x = new RegSingleton(); m_registry.put(x.getClass().getName(), x); } /** * 保护的默认构造子 */ protected RegSingleton() {} public static RegSingleton getInstance(String name) { if (name == null) { name = "com.test.singleton.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)); } public String about() { return "Hello, I am RegSingleton"; } package com.test.singleton; public class RegSingletonChild extends RegSingleton { public RegSingletonChild() {}; /** * 静态工厂方法 * @return */ public static RegSingleton getInstance() { return (RegSingleton)RegSingleton. getInstance("com.test.singleton.RegSingletonChild"); } /** * 一个示意性的商业方法 */ public String about() { return "Hello, I am RegSingletonChild."; } } 在GoF原始代码例子中并没有getInstance()方法,这样得到子类必须调用的getInstance(String name)方法并传入子类的名字,因此很不方便。而在这个单例类子类的例子里,加入了getInstance()方法,这样做的好处是RegSingleChild可以通过这个方法返回自己的实例。而这样做的缺点是,由于数据类型不同,无法在RegSingletong提供这样一个方法。由于子类必须允许父类以构造子调用产生实例,因此,它的构造子必须是公开的。这样一来,就等于允许了以这样方式产生实例而不在父类的登记中。这是登记类的一个缺点。 GoF曾指出,由于父类的实例必须存在才可能有子类的实例,这在有些情况下是一个浪费。使用单例模式的条件:使用单例模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反过来说,如果一个类可以有几个实例共存,那么就没有必要使用单例类。下面是一些错误使用单例的例子:例子一问:我有一个系统需要一些”全程变量”。学习了单例模式后,我发现可以使用一个单例类盛放所有的“全程”变量。答:这样做是违背单例模式的用意的。单例模式只应当在有真正的”单一实例”的需要时才可以使用。一个设计得当的系统不应当有所谓的”全程变量”,这些变量应当放到它们所描述的实体所对应的类中区,将这些变量从它们所描述的实体类中抽出来。放到一个不相干的单例类中去,使得这些变量产生错误的依赖关系和耦合关系。例子二问:我的一个系统需要管理与数据库的链接。学习了单例模式后,我发现可以使用一个单例类包装一个Connection对象,并在finalize()方法中关闭这个Connection对象。这样的话,在这个单例类的实例没有被人引用时,这个finalize()对象就会被调用,因此Connection对象就会被释放。答:这样做是不恰当的。除非有单一实例的需求,否则不要使用单例模式。在这里Connection对象可以同时有几个实例共存,不必是单一实例。单例模式有很多错误使用案例都与此例子相似,它们都是试图使用单例模式管理共享资源的生命周期,这是不恰当的。单例的状态有状态的单例类 一个单例类可以是有状态的(stateful),一个有状态的单例对象一般也是可变(mutable)单例状态。有状态的的可变单例对像常常当做状态库(repositary)使用。比如一个单例对象可以持有一个int类型的属性,用来给一个系统提供一个数值惟一的序列号码,作为某个贩卖系统的账单号码。当然,一个单例类可以持有一个聚集,从而允许存储多个状态。没有状态的单例类另一方面,单例类也可以是没有状态的(stateless),仅用做提供工具性函数的对象。既然是为了提供工具性函数,也就没有必要创建多个实例,因此使用单例模式很适合,一个没有状态的单例类也就是不变(Immutable)模式。在任何使用了EJB,RMI和JNDI技术的分散式系统中,应当避免使用有状态的单例模式。 Java语言中的单例模式: Java的Runtime对像 在Java语言内部,java.lang.Runtime对象就是一个使用单例模式的例子。在每一个Java应用程序里。都有一个惟一的Runtime对象。通过这个Runtime对象,应用程序可以与其运行环境发生相互作用。 Runtime类提供一个静态工厂方法getRuntime(); public static Runtime getRuntime();通过调用此方法,可以获得Runtime类惟一的一个实例 Runtime rt = Runtime.getRuntime() Runtime对象通常的用途包括:执行外部命令:返回现有内存即全部内存 运行垃圾收集器;加载动态库等。不完全单例类 package com.test.singleton; public class LazySingleton { private static LazySingleton s = null; /** * 公共的构造子,外界可以直接实例化 */ public LazySingleton() {} /** * 静态工厂方法 * @return 返回LazySingleton的唯一实例 */ public static synchronized LazySingleton getInstance() { if (s == null) { s = new LazySingleton(); } return s; } } 双重检查成例的研究:成例是一种代码层次上的模式,是在比设计模式的层次更具体的层次上的代码技巧。成例往往与编程语言密切相关。双重检查成例(Double Check Idiom)是从C语言移植过来的一种代码模式。在C语言里,双重检查成例常常用在多线程环境中类的晚实例化(Late Instantiation)里。多例模式:所谓的多例模式(Multiton),实际上就是单例模式的自然推广。作为对象的创建模式,多例模式或多例类有以下特点: ◆ 多例类可有多个实例 ◆ 多例类必须自己创建、管理自己的实例,并向外界提供自己的实例。 单例类一般情况下最多只可以有一个实例,但是单例模式的精神是允许有限个实例,并不是仅允许一个实例。这种允许有有限个实例并向整个JVM提供自己实例的类叫做多例类(Multiton),这种模式叫做多例模式(Multiton Pattern).一个实例数目有上限的多例类已经把实例的上限当作逻辑的一部分,并建造到了多实例的内部,这种多例模式叫做有上限多例模式。由于有上限的多例类对实例的数目有上限,因此有上限的多例类在这个上限等于1时,多例类就回到了单例类。因此,多例类是单例类的推广,单例类是多例类的特殊情况。一个有上限的多例类可以使用静态变量存储所有的实例,特别是在实例数目不多的时候,可以使用一个个的静态变量存储一个个的实例。在数目较多的时候,就需要使用静态聚集存储这些实例。多例类的实例数目并不需要上限,实例数目没有上限的多例模式就叫做无上限多例模式。由于没有上限的多例类对实例的数目没有限制的。因此,虽然这种多例模式就是单例模式的推广,但这种多例模式并不一定能够回到单例类。

原创粉丝点击