工厂方法模式 (Factory Method)
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。引入多态创建对象。
一个层次中的类都有相似实现一个方法(AnOperation), 只是这个方法中对象创建的步骤不同。工厂方法通常在模板方法中被调用。
下面的例子通过学习Log4j APIs,运用重构技术实现模板方法和工厂方法模式:
Log4j 有ConsoleAppender and FileAppender 两个类,用于将log 输出到控制台和制定文件中,基于JUnit4写两个测试类学习这两种Appender如何工作.
Step 1: 写两个测试类,分别测试将debug logs输出到控制台和指定的文件中。
public class ConsoleAppenderTest{
private Logger logger = Logger.getLogger(ConsoleAppenderTest.class);
@Test
public void testConsoleAppender() {
<font color="red">ConsoleAppender appender = new ConsoleAppender(new SimpleLayout());</font>
logger.addAppender(appender);
logger.setLevel((Level) Level.DEBUG);
logger.debug("Debug logs");
}
}
public class FileAppenderTest{
private Logger logger = Logger.getLogger(FileAppenderTest.class);
@Test
public void testFileAppender(){
FileAppender appender = null;
try {
appender = new FileAppender(new SimpleLayout(), "d:/log.txt", false);
} catch (Exception e) {
fail();
//do something
}
logger.addAppender(appender);
logger.setLevel((Level) Level.DEBUG);
logger.debug("Debug logs");
}
}
FileAppender appender = null;
try {
appender = new FileAppender(new SimpleLayout(), "d:/log.txt", false);
} catch (Exception e) {
fail();
//do something
}
logger.addAppender(appender);
logger.setLevel((Level) Level.DEBUG);
logger.debug("Debug logs");
}
}
Step 2: 上面两个测试类测试的方法类似,只是创建的对象不同 (ConsoleAppender, FileAppender), 这两个对象类型属于同一个类层次(继承WriterAppender),符合应用工厂方法(Factory Method)模式。首先要提取方法(Extract Method) 对这两个类进行重构:
public class ConsoleAppenderTest{
private Logger logger = Logger.getLogger(ConsoleAppenderTest.class);
@Test
public void testConsoleAppender() {
WriterAppender appender = createAppender();
logger.addAppender(appender);
logger.setLevel((Level) Level.DEBUG);
logger.debug("Debug logs");
}
//Extract method
WriterAppender createAppender() {
return new ConsoleAppender(new SimpleLayout());
}
}
public class FileAppenderTest{
private Logger logger = Logger.getLogger(FileAppenderTest.class);
@Test
public void testFileAppender() {
WriterAppender appender = createAppender();
logger.addAppender(appender);
logger.setLevel((Level) Level.DEBUG);
logger.debug("Debug logs");
}
//Extract method
WriterAppender createAppender() {
FileAppender appender = null;
try {
appender = new FileAppender(new SimpleLayout(), "d:/log.txt", false);
} catch (Exception e) {
fail();
//do something
}
return appender;
}
}
Step 3: 两个测试类中方法 testConsoleAppender() 和 testFileAppender()内容相同,可以重命名(Rename Method)使其方法名相同; 两个测试类中都有相同签名的createAppender方法,提炼父类(Extract Superclass), 然后方法上移(Pull up Method), 形成模板方法模式(Template Method).
public abstract class AppenderTest { //Extract superclass
private Logger logger = Logger.getLogger(AppenderTest.class);
@Test
public void testAppender() {//Rename method and Pull up method (template method)
WriterAppender appender = createAppender();
logger.addAppender(appender);
logger.setLevel((Level) Level.DEBUG);
logger.debug("Debug logs");
}
abstract WriterAppender createAppender();
}
public class ConsoleAppenderTest extends AppenderTest {
WriterAppender createAppender() {
return new ConsoleAppender(new SimpleLayout());
}
}
public class FileAppenderTest extends AppenderTest {
WriterAppender createAppender() {
FileAppender appender = null;
try {
appender = new FileAppender(new SimpleLayout(), "d:/log.txt", false);
} catch (Exception e) {
fail();
//do something
}
return appender;
}
}
不是一开始编程的时候就运用模式设计,而是随着需求的变化,引起代码的改变,通过重构代码改进设计以实现模式,从而减少代码重复和可复用代码。