工厂模式(Factory)是经典设计模式之一,运用非常普遍,主要实现对象实例创建和使用的分离。实践中,抽象工厂常用来进一步将工厂的抽象行为和与具体工厂类型相关的创建细节相分离,从而使系统具有更高的灵活性。不过在具体的开发和以往的项目经历中,抽象工厂模式的使用似乎并没有将它的灵活性充分发挥出来。以java设计模式中关于抽象工厂模式的描述为例进行说明。
在创建工厂实例时,通常采用的是以下的方式:
Code 1:public abstract class AbstractFactory { public static AbstractFactory getFactory() {return new ConcretFactory1();} public abstract Product createProduct(String productName);}
Code 2:public abstract class AbstractFactory { private static String factoryClass = "ConcretFactory1"; private static AbstractFactory factory; public static AbstractFactory getFactory() { try { factory = (AbstractFactory)Class.forName(factoryClass).newInstance(); return factory; } catch (Exception e) { e.printStackTrace(); } return null; } … …}
在Code1中,getFactory()方法只能创建ConcretFactory1类型的工厂实例,也许它是项目中仅用的工厂类。当系统需要使用新的工厂类时,就不得不修改代码。因此,这种写法缺乏灵活性。 而在Code2中,使用了动态类装载技术来动态创建工厂类的实例,该工厂类的类名是通过一个类成员 factoryClass 来存放的。 例如:《设计模式》中 Jive论坛的ForumFactory示例就采用的这种方式,参见《设计模式》一书。 然而由于 factoryClass在声明时就初始化为具体的类名 “ConcretFactory1”, 因此它的灵活性也比较低。2 可配置动态抽象工厂模式(Configurable dynamic abstract factory)如果一个系统将会使用不止一种类型的工厂类,即 AbstractFactory 可能有多个不同的子类,系统中factory的实例属于其中的一种。那么这就需要一种更为灵活的方式来创建工厂类的实例。2.1 两个用例 一般考虑两个用例, 第一种, 系统启动时,即可确定要使用的工厂类类型名;系统要工厂类的类型必须停止运行,以新的配置启动。 第二种,系统运行中,需要动态切换工厂类的类型,例如:从oracle DAO工厂切换到DB2 的DAO工厂。 这两种情况都要求系统有高度的灵活性和适应性,即不需要修改代码,就能够切换工厂实例的类型,即具有动态性。 因此,可以考虑在Code2方式的基础上,进一步将工厂类型名参数化,将getFactory方法的声明改为:
Code3: public static AbstractFactory getFactory(String factoryClass); 这样,就能够根据传入的工厂类型动态创建相应的工厂实例。 另外,如果要满足第一个用例,就必须实现初始(或默认)工厂类名的可配置。一般有两种方式,通过java命令的启动参数传入;或者通过配置文件配置。这通常需要一些处理配置文件的技巧。2.2可配置动态抽象工厂模式 它可以定义为,能够通过改变配置或热切换,动态改变唯一工厂实例类型的抽象工厂模式。为此,对经典的AbstractFactory 模式进行适当调整。
调整主要体现在AbstractFactory类成员的调整,见以下声明,
public static AbstractFactory getFactory() public static AbstractFactory getFactory(String factoryClass) public static void switchFactory (String factoryClass) 第一个getFactory方法保留,不过,它的实现需要做实质性调整,要能够加载默认配置并创建工厂实例; 第二个getFactory(String factoryClass) 方法,能够根据类型名动态创建。 第三个switchFactory(String factoryClass), 能够根据类型名动态切换唯一的工厂实例。2.3 参考实现(Referenced implementation) getFactory() 的参考实现: Code 4 public static AbstractFactory getFactory() { if(factory!= null) return factory; factoryClass = getDefaultFromSystem(); //从启动参数中加载 if(factoryClass == null) { factoryClass = getDefaultFromConfiguration(); } if(factoryClass!= null && factoryClass.length() > 0 ) factory = getFactory(factoryClass); else factory = getFactory("ConcretFactory1"); //or throw new IllegalArgumentException("Unconfigured default factory class."); return factory; } //从启动参数中加载 private static String getDefaultFromSystem() { return System.getProperty("defaultFactory"); } private static String getDefaultFromConfiguration() { // TODO:从配置文件中加载,java properties 或 xml } getFactory() 方法执行的逻辑是, 首先检查static factory实例,如果存在,则返回; 否则,从启动参数中获取默认工厂类名,创建并返回实例; 如果启动参数中没有设置,则从配置文件中加载工厂类名的配置,创建并返回实例;配置可能是java properties文件或者是XML配置文件(如weblogic JMS工厂类的配置),在多种J2EE应用服务器配置中, 这种方式采用比较普遍。 如果配置文件中未找到,则使用自设的缺省工厂类 ConcretFactory1。 switchFactory (String factoryClass) 的参考实现。 Code 5 //切换默认工厂实例 public static void switchFactory(String factoryClass) { try { factory = (AbstractFactory)Class.forName(factoryClass).newInstance(); } catch (Exception e) { e.printStackTrace(); } }getFactory(String factoryClass) 的参考实现。
Code 6 //创建工厂实例并切换 public static AbstractFactory getFactory(String factoryClass) { try { switchFactory(factoryClass); return factory; } catch (Exception e) { e.printStackTrace(); } return null; } 动态切换工厂实例在现在的实践中运用并不太多,不过如果能够灵活运用这一特性,系统可以在不重启的情况下,实现功能的热切换,例如: 不重启J2EE应用服务器,从使用oralce数据库热切换到DB2数据库。但热切换还需要注意切换时前后系统的衔接和可能导致的问题。3 结论 可配置动态抽象工厂模式是对抽象工厂模式的一种扩展和改进,通过配置及加载,以及动态切换,使得在不修改代码的情况下,动态创建或改变工厂实例的类型,从而使系统具有更高的灵活性和可伸缩性。参考资料:《Design Pattern - Factory》. 板桥里人 http://www.jdon.com 2002/10/07