@XingdingCAO
2018-01-24T10:01:54.000000Z
字数 3017
阅读 2075
design-pattern
OOD
在读到 Effective Java 中的静态工厂方法Static Factory Method
相关部分时,作者提到了该方法不同于Gof的Factory Method
,自此我就埋下了疑问。而后,我接触到了 Design Pattern 这本著作和由刘伟老师总结的《设计模式Java版》,从中了解到了23种设计模式中的工厂模式Factory Pattern
和抽象工厂模式Abstract Factory Pattern
,以及未列入23种模式的简单工厂模式Simple Factory Pattern
。
后来,我才完全搞清楚这几个“工厂”模式(方法)。
这些模式或者方法,均和工厂有关,难免让人困扰。为了消除困扰,就必须先搞清楚,什么是工厂以及为何要引入工厂。工厂,字如其名,就是生产产品的地方,在软件中引入工厂类或工厂方法就是为了让它来生产产品——类的实例。
下面,我就各个“工厂”模式(方法)的用途来逐个说明:
Static Factory Method
出处:Effective Java(2nd Edition)中的item 1
实例化控制
若一个类的实例个数是受到限制的,那么就不应该向客户端暴露该类构造方法,同时仔细考量继承对其的影响。构造方法已经被封锁,剩下的途径也只有客户端可访问的public
、static
方法(静态工厂方法)了。例如,java.lang.Boolean
类就可以通过只暴露多个静态方法valueOf
重载来实现不管任何时刻仅存在两个类的实例——TRUE
和FALSE
(实际上并没有)。
public final class Boolean implements java.io.Serializable,
Comparable<Boolean>{
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
public static Boolean valueOf(String s) {
return parseBoolean(s) ? TRUE : FALSE;
}
}
实例化受限的类大多数情况下是一种不怎么优秀的设计,因为这种从静态方法获取实例的方法容易造成全局耦合,从而使得软件的依赖关系变得错综复杂,难以维护、测试。仅有少数被证明是无害的,如:不变类的实例化限制(消除全局耦合)、日志记录(不影响软件功能逻辑)等。实际采用实例化控制这种设计时,请再三思量。
在抽象类中提供实现
在设计某项功能,我抽象了该功能的操作,得到了一个抽象类,我希望该类能提供默认的实现,但又不使其失去被继承的能力。因此,我使用了静态工厂方法来为客户端提供默认的实现,甚至用来提供其他的实现。例如,JAXP(Java API for XML Processing)为了实现即装即用这一特性,在发行时会集成默认解析器实现,但客户端有权选用其他解析器实现。严格来说,JAXP是一种SPI(Service Provider Interface),而不是API(Application Programming Interface)。
public abstract class DocumentBuilderFactory {
public static DocumentBuilderFactory newInstance() {
return FactoryFinder.find(
/* The default property name according to the JAXP spec */
DocumentBuilderFactory.class, // "javax.xml.parsers.DocumentBuilderFactory"
/* The fallback implementation class name */
"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
}
public static DocumentBuilderFactory newInstance(String factoryClassName,ClassLoader classLoader){
//do not fallback if given classloader can't find the class, throw exception
return FactoryFinder.newInstance(DocumentBuilderFactory.class,
factoryClassName, classLoader, false);
}
//...
}
可以从代码中看到,DocumentBuilderFactory
这一抽象类通过静态工厂方法提供了默认的实现,以及加载其他实现的能力。
Simple Factory Pattern
不同于静态工厂方法,接下来的工厂模式都有着一个单独的、中心的工厂类来负责实例的产生。
简单工厂模式的做法是:将同属于一个抽象类/接口的多个实现类的实例化集中在一起
设计工厂类的初衷源于类的创建职责不应与功能逻辑耦合在一起,创建实例、互相合作这件事应该由额外的类来管理,从而减小类之间的依赖关系,方便软件的变更。
至于具体的实现,我就不赘述了(具体方法可移步刘伟老师的《设计模式Java版》),这里就仅贴一张UML类图来帮助理解吧。
简单工厂模式这种耦合所有实现类的实例化的做法非常直接,却有效。但是,这种模式面对产品的实现类新增时,服务端(代码提供者)必须修改工厂类来容纳更多实现类,这显然违背了开闭原则。而对于客户端(代码使用者),该模式隐藏了实例化特定实现的细节,消除了对特定实现的依赖。
Factory Pattern
出处:由Gof总结自Design Pattern一书
工厂模式则改进了简单工厂模式这种将多个实现耦合进一个工厂类的方法,为针对某一抽象类/接口的所有工厂类抽象了一个工厂接口,具体的产品由具体的工厂类来创建。
在此举《设计模式Java版》中的一个例子:
需要注意的是,工厂接口由来实现?工厂模式将实例化过程推迟到工厂接口的实现类中,所以工厂接口应由客户端来实现。服务端提供抽象层的设计以及对于每个抽象有限的实现,由客户端来选择合适的实现类来实现特定的抽象操作。
AbstractFactory
工厂模式中,每产生一个具体的实现类,都会伴随着新的工厂实现类的产生,因此类的个数成倍地增加。而在实际中,客户端通常需要一组相互关联的类来协同工作,所以单独地创建多个抽象的实现是琐碎的。而抽象工厂模式中工厂类创建一组相关的抽象的实现,从而有效减少类的个数,减低复杂度。
《设计模式Java版》中的一个例子:
这些与“工厂”相关的设计方法都是为了减少客户端对具体的实现的依赖,或者说是直接依赖,从来更方便地变更服务端代码而不影响客户端。这些模式可能在某一情形下都满足要求,但使用者也应该恰当地选择。例如:实例化受限的类也可以采用工厂模式,但是相较于类中的静态方法,单独的另一个工厂类无疑扩大了实例化的可访问性;虽然客户端无法直接实例化,但还是给服务端的编码者留下了犯错的机会。