设计模式实战应用之四:简单工厂模式

        简单工厂模式的定义
        简单工厂模式是一个很基本的设计模式。Java API 里的 java.text.DateFormat 获得具体子类实例化的 getDateInstance 就是一个简单工厂的应用;SAX1 库里的 javax.xml.parsers.DocumentBuilderFactory、javax.xml.parsers.SAXParserFactory 也都是简单工厂的应用例子,newInstance 是它们各自的简单工厂方法;SAX2 库里的 org.xml.sax.helpers.XMLReaderFactory 创建产品类 XMLReader 实例的 createXMLReader 也是简单工厂模式的具体工厂方法;Apache POI 库里的 WorkbookFactory 就是一个非常标准的简单工厂模式,完全可以用来作为我们研究学习简单工厂模式的范例。
        简单工厂模式,又称静态工厂方法模式。Gof 在《 设计模式:可复用面向对象软件的基础》中没有将其列入二十三种设计模式的名单中。很多文献中,它只算作工厂方法模式的一种特殊形式,因此它跟工厂方法模式一样,属于创建型模式。自由职业人 Tom McFarlin 在 tutsplus 上对简单工厂模式进行了这样的定义:"The Simple Factory pattern returns a specific set of data or a specific class based on its input.",翻译过来就是:"简单工厂根据传入的参量决定返回一组特定的数据或者一个特定的类的实例。"; CSCI 3132 课程中对简单工厂模式进行了这样的定义:" Pull the code that builds the instances out and put it into a separate class.",翻译过来就是:" 将负责创建对象的代码移植到一个单独的类中。";阎宏博士在《 Java 与模式》中对简单工厂模式的定义如下:"简单工厂模式就是由一个工厂类根据传入的参量决定创建出哪一种产品类的实例。"
        笔者比较倾向于 CSCI 3132 关于简单工厂的定义。
        why 简单工厂模式?
        简单工厂有助于我们的代码抽象化,使得我们的项目易于维护、易于阅读,并能使我们保持代码的整洁。这可能会导致我们的项目引入更多文件,但每个文件将具有更少的代码。
        这意味着每个文件的用途更清晰,不仅对我们自己,而且对以后的开发人员。
        简单工厂模式的使用场合
  • 多个客户端需要同一种类型的对象。
  • 保持对象初始化的一致性。
        《系统错误信息报告》需求
        每个系统都需要一种报告错误信息的途径。程序员小白的 Java Web 应用也不例外:项目部署以后,小白就无法使用断点的方式来对自己的系统进行错误跟踪了,系统需要一种错误报告机制来让自己对系统的健康状况做到心中有数;此外,真实压力下系统的并发量、重要接口的执行时间,这些编码期间无法准确获知的数据也是小白同学所关心的。小白同学自己设计了一个系统错误信息报告机制。
        小白同学设计的跟踪接口 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 );
}

        小白根据自己的需要写了两个 Trace 的实现。一个是 FileTrace,将跟踪信息写入一个文件中:
public class FileTrace implements Trace {
          
      private java.io.PrintWriter pw;
      private boolean debug;
      public FileTrace() throws java.io.IOException {
            // a real FileTrace would need to obtain the filename somewhere
            // for the example I'll hardcode it
            pw = new java.io.PrintWriter( new java.io.FileWriter( "c:\trace.log" ) );
      }
      public void setDebug( boolean debug ) {
            this.debug = debug;
      }
      public void debug( String message ) {
            if( debug ) {  // only print if debug is true
                  pw.println( "DEBUG: " + message );
                  pw.flush();
            }
      }
      public void error( String message ) {
            // always print out errors
            pw.println( "ERROR: " + message );
            pw.flush();
      }
}

        另一个是 SystemTrace,将跟踪信息写到命令行中:
public class SystemTrace implements Trace {
      private boolean debug;
      public void setDebug( boolean debug ) {
            this.debug = debug;
      }
      public void debug( String message ) {
            if( debug ) {  // only print if debug is true
                  System.out.println( "DEBUG: " + message );
            }
      }
      public void error( String message ) {
            // always print out errors
            System.out.println( "ERROR: " + message );
      }
}

        在需要跟踪的代码中,小白使用 Trace 的代码如下:
//... some code ...
Trace log = new SystemTrace();
//... code ...
log.debug( "entering loog" );
// ... etc ...

        《系统错误信息报告》分析
        现在如果小白同学想要应用中的 Trace 的实现,必须修改每一个对 Trace 进行实例化的类。这个工作量取决于使用了 Trace 的类的数量,如果系统稍具规模,将会费很大一番功夫。这给后续的系统维护带来很多不便。此外,有的类用了 SystemTrace,有些则用了 FileTrace,系统的 Trace 使用混乱,不一致。
        我们引入了简单工厂模式,以帮小白优化系统错误信息报告机制。这里使用简单工厂的优势有:
  • 简单工厂提供了一个优雅的方式来抽象化你的代码,大大减少了代码视觉上的混乱。
  • 简单工厂允许你引入更加专业、专注的类,专门去负责一个单一目的。
  • 简单工厂增强了代码的可维护性,因为各种实现类的实例化集中在了一个单一的地方。

        《系统错误信息报告》类设计
设计模式实战应用之四:简单工厂模式_第1张图片

        《系统错误信息报告》时序图
设计模式实战应用之四:简单工厂模式_第2张图片

        《系统错误信息报告》源码实现
        简单工厂 TraceFactory 类源代码如下:
public class TraceFactory {
      public static Trace getTrace() {
            try {
                  return new FileTrace();
            } catch ( java.io.IOException ex ) {
                  Trace t = new SystemTrace();
                  t.error( "could not instantiate FileTrace: " + ex.getMessage() );
                  return t;
            }
      }
}

        客户端调用示例:
//... some code ...
Trace log = new TraceFactory.getTrace();
//... code ...
log.debug( "entering loog" );
// ... etc ...

        可以看出,在不确定具体使用哪个 Trace 实现进行实例化时,简单工厂是很有用的。你只需将这些细节抛给简单工厂。
        在上面的例子中,系统并不知道是要创建 FileTrace 还是 SystemTrace 实例。你的对象只需要轻松使用从工厂拿来的 Trace 对象,这个对象的具体实现是哪一个?怎么进行实例化的?这些细节就交给简单工厂去完成吧。

你可能感兴趣的:(设计模式)