1.什么是工厂方法模式?
工厂方法模式(Factory Method) 又被称为 Virtual Constructor 虚拟构造器 :当客户端不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法。定义一个用于创建对象的接口,让其子类来决定实例化哪一个类,从而使一个类的实例化延迟到其子类。
2.什么时候需要我们用到工厂方法模式?
当一个类遇到以下几个情形的时候:
- 无法预知要构造一个什么样的对象的类
- 希望其子类指定其创建的对象
- 将类的功能委托给多个其他的子类之一时,你需要确定到底哪个子类受到委派
常规情况下,client直接创建具体对象
Product p = new ProductTwo();
在工厂方法模式下:
Product p = new ConcreteTwo().makeObject();
其委派关系如图所示
举一个实例来讲,如对一个调试器的设计
其接口设计如下
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 );
}
分别实现其子类 FileTrace 与 SystemTrace
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 );
}
}
在上面那个例子中 接口Trace就是一个抽象产品, FileTrace 与 SystemTrace便是实现抽象产品的具体的产品
下面我们便套用工厂方法模式对其进行实现:
interface TraceFactory {
public Trace getTrace();
public Trace getTrace(String type);
void otherOperation(){};
}
TraceFactory是一个生产各种Trace产品的抽象工厂,其不仅包含factory method,还可以实现 其他功能
public class Factory1 implements TraceFactory {
public Trace getTrace() {
return new SystemTrace();
}
}
public class Factory2 implements TraceFactory {
public Trace getTrace(String type) {
if(type.equals(“file”)
return new FileTrace();
else if (type.equals(“system”)
return new SystemTrace();
}
对抽象工厂进行实现:根据类型决定创建哪个具体产品, 当有新的具体产品加入时,可以在工厂类里修改或增加新的工厂函数(OCP),不会影响客户端代码
客户端对工厂方法的调用:
//客户端使用“工厂方法”来创建实例,得到实例的类型是抽象接口而非具体类
Trace log1 = new Factory1().getTrace();
log1.setDebug(true);
log1.debug( "entering log" );
Trace log2 = new Factory2().getTrace("system");
log2.setDebug(false);
log2.debug("...");
而为了方便的调用,我们可以设计静态工厂方法,既可以在ADT内部实现也可以构造单独的工厂类。
public class TraceFactory1 {
public static Trace getTrace() {
return new SystemTrace();
}
}
public class TraceFactory2 {
public static Trace getTrace(String type) {
if (type.equals(“file”)
return new FileTrace();
else if (type.equals(“system”)
return new SystemTrace();
}
}
客户端对静态工厂方法的调用:
Trace log1 = TraceFactory1.getTrace();
log1.setDebug(true);
log1.debug( "entering log" );
Trace log2 = TraceFactory2.getTrace(“system”);
log1.setDebug(true);
log2.debug(“...”);
4. 工厂方法模式 Factory Method的优缺点:
优点:
- 无需将各种根据需求选择不同的类的操作绑定到客户端代码。
- 客户端代码仅需要处理工厂生成出来的产品的接口(Trace),因而可以调用用户定义的具体产品(FileTrace,SystemTrace),实现个性化的需求
潜在的缺点:
- 客户端可能需要同时创建一个构造器 Creator的实例,那为什么不直接创建一个特定的具体产品呢?
- 如果客户无论必须将创建者子类化,这是可以接受的,但是如果不是这样,那么客户就不得不处理另一个进化点。