工厂方法模式
定义
工厂方法也叫作虚拟构造器。工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成。
当客户端不知道创建哪个类的具体实例,或者不想在客户端代码中指定具体创建的实例时,使用工厂方法。
一个例子
例如,我们有一个接口Trace:
public interface Trace {
// turn on and off debugging
public void setDebug( boolean debug );
// write out a debug message
public void debug( String message );
// write out an error message
public void error( String message );
}
将接口实现为如下两个类:
public class FileTrace implements Trace {
private PrintWriter pw;
private boolean debug;
public FileTrace() throws IOException {
pw = new PrintWriter( new FileWriter( "t.log" ) );
}
public void setDebug( boolean debug ) {
this.debug = debug;
}
public void debug( String message ) {
if( debug ) {
pw.println( "DEBUG: " + message );
pw.flush();
}
}
public void error( String message ) {
pw.println( "ERROR: " + message );
pw.flush();
}
}
public class SystemTrace implements Trace {
private boolean debug;
public void setDebug( boolean debug ) {
this.debug = debug;
}
public void debug( String message ) {
if( debug )
System.out.println( "DEBUG: " + message );
}
public void error( String message ) {
System.out.println( "ERROR: " + message );
}
}
这两个类分别实现了在控制台输出错误信息以及将错误信息输出到日志。
//... some code ...
Trace log1 = new SystemTrace();
log1.debug("entering log");
Trace log2 = new FileTrace();
log2.debug("... ");
在调用时我们看到客户端代码是与调用的类紧密结合的。
而使用工厂方法模式,我们可以将定义改为:
interface TraceFactory {
Trace getTrace();
void otherOperation();//不仅包含工厂方法,也能实现其他功能
}
public class SystemTraceFactory implements TraceFactory {
public Trace getTrace() {
//other operations
return new SystemTrace();
}
}
public class FileTraceFactory implements TraceFactory {
public Trace getTrace() {
return new FileTrace();
}
}
在调用时,我们进行如下操作:
Trace log1 = new SystemTraceFactory().getTrace();
log1.debug("entering log");
Trace log2 = new FileTraceFactory().getTrace();
log2.debug("...");
这样做使得一个类不知道它所需要的对象的类名:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端只需要知道创建具体产品的工厂类。
此外,一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,从而使得系统更容易扩展。
也可以实现为:
interface TraceFactory {
Trace getTrace(String type);
void otherOperation();
}
public class Factory implements TraceFactory {
public getTrace(String type) {
if(type.equals("file" )
return new FileTrace();
else if (type.equals("system")
return new SystemTrace();
}
}
Trace log = new Factory().getTrace("system");
log.setDebug(false);
log.debug("...");
静态工厂方法
public class SystemTraceFactory{
public static Trace getTrace() {
return new SystemTrace();
}
}
public class TraceFactory {
public static Trace getTrace(String type) {
if(type.equals("file")
return new FileTrace();
else if (type.equals("system")
return new SystemTrace();
}
}
客户端代码为:
Trace log1 = SystemTraceFactory.getTrace();
log1.setDebug(true);
log1.debug( "entering log" );
Trace log2 = TraceFactory.getTrace("system");
log1.setDebug(true);
log2.debug("... ");
静态工厂方法的优点(相比构造器):
- 静态工厂方法可以指定更有意义的名称。
- 不必在每次调用时创建新的工厂对象。
- 可以返回原类型的任意子类型。
工厂方法的优点
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
- 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合OCP。
工厂方法的缺点
- 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。