# 工廠三兄弟之工廠方法模式(三)
3 完整解決方案
Sunny公司開發人員決定使用工廠方法模式來設計日志記錄器,其基本結構如圖3所示:

圖3 日志記錄器結構圖
在圖3中,Logger接口充當抽象產品,其子類FileLogger和DatabaseLogger充當具體產品,LoggerFactory接口充當抽象工廠,其子類FileLoggerFactory和DatabaseLoggerFactory充當具體工廠。完整代碼如下所示:
```
//日志記錄器接口:抽象產品
interface Logger {
public void writeLog();
}
//數據庫日志記錄器:具體產品
class DatabaseLogger implements Logger {
public void writeLog() {
System.out.println("數據庫日志記錄。");
}
}
//文件日志記錄器:具體產品
class FileLogger implements Logger {
public void writeLog() {
System.out.println("文件日志記錄。");
}
}
//日志記錄器工廠接口:抽象工廠
interface LoggerFactory {
public Logger createLogger();
}
//數據庫日志記錄器工廠類:具體工廠
class DatabaseLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//連接數據庫,代碼省略
//創建數據庫日志記錄器對象
Logger logger = new DatabaseLogger();
//初始化數據庫日志記錄器,代碼省略
return logger;
}
}
//文件日志記錄器工廠類:具體工廠
class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//創建文件日志記錄器對象
Logger logger = new FileLogger();
//創建文件,代碼省略
return logger;
}
}
```
編寫如下客戶端測試代碼:
```
class Client {
public static void main(String args[]) {
LoggerFactory factory;
Logger logger;
factory = new FileLoggerFactory(); //可引入配置文件實現
logger = factory.createLogger();
logger.writeLog();
}
}
```
編譯并運行程序,輸出結果如下:
文件日志記錄。
4 反射與配置文件
為了讓系統具有更好的靈活性和可擴展性,Sunny公司開發人員決定對日志記錄器客戶端代碼進行重構,使得可以在不修改任何客戶端代碼的基礎上更換或增加新的日志記錄方式。
在客戶端代碼中將不再使用new關鍵字來創建工廠對象,而是將具體工廠類的類名存儲在配置文件(如XML文件)中,通過讀取配置文件獲取類名字符串,再使用Java的反射機制,根據類名字符串生成對象。在整個實現過程中需要用到兩個技術:Java反射機制與配置文件讀取。軟件系統的配置文件通常為XML文件,我們可以使用DOM (Document Object Model)、SAX (Simple API for XML)、StAX (Streaming API for XML)等技術來處理XML文件。關于DOM、SAX、StAX等技術的詳細學習大家可以參考其他相關資料,在此不予擴展。
擴展
關于Java與XML的相關資料,大家可以閱讀Tom Myers和Alexander Nakhimovsky所著的《Java XML編程指南》一書或訪問developer Works 中國中的“Java XML 技術專題”,參考鏈接:
http://www.ibm.com/developerworks/cn/xml/theme/x-java.html
Java反射(Java Reflection)是指在程序運行時獲取已知名稱的類或已有對象的相關信息的一種機制,包括類的方法、屬性、父類等信息,還包括實例的創建和實例類型的判斷等。在反射中使用最多的類是Class,Class類的實例表示正在運行的Java應用程序中的類和接口,其forName(String className)方法可以返回與帶有給定字符串名的類或接口相關聯的 Class對象,再通過Class對象的newInstance()方法創建此對象所表示的類的一個新實例,即通過一個類名字符串得到類的實例。如創建一個字符串類型的對象,其代碼如下:
```
//通過類名生成實例對象并將其返回
Class c=Class.forName("String");
Object obj=c.newInstance();
return obj;
```
此外,在JDK中還提供了java.lang.reflect包,封裝了其他與反射相關的類,此處只用到上述簡單的反射代碼,在此不予擴展。
Sunny公司開發人員創建了如下XML格式的配置文件config.xml用于存儲具體日志記錄器工廠類類名:
```
<!— config.xml -->
<?xml version="1.0"?>
<config>
<className>FileLoggerFactory</className>
</config>
```
為了讀取該配置文件并通過存儲在其中的類名字符串反射生成對象,Sunny公司開發人員開發了一個名為XMLUtil的工具類,其詳細代碼如下所示:
```
//工具類XMLUtil.java
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
public class XMLUtil {
//該方法用于從XML配置文件中提取具體類類名,并返回一個實例對象
public static Object getBean() {
try {
//創建DOM文檔對象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("config.xml"));
//獲取包含類名的文本節點
NodeList nl = doc.getElementsByTagName("className");
Node classNode=nl.item(0).getFirstChild();
String cName=classNode.getNodeValue();
//通過類名生成實例對象并將其返回
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
```
有了XMLUtil類后,可以對日志記錄器的客戶端代碼進行修改,不再直接使用new關鍵字來創建具體的工廠類,而是將具體工廠類的類名存儲在XML文件中,再通過XMLUtil類的靜態工廠方法getBean()方法進行對象的實例化,代碼修改如下:
```
class Client {
public static void main(String args[]) {
LoggerFactory factory;
Logger logger;
factory = (LoggerFactory)XMLUtil.getBean(); //getBean()的返回類型為Object,需要進行強制類型轉換
logger = factory.createLogger();
logger.writeLog();
}
}
```
引入XMLUtil類和XML配置文件后,如果要增加新的日志記錄方式,只需要執行如下幾個步驟:
(1) 新的日志記錄器需要繼承抽象日志記錄器Logger;
(2) 對應增加一個新的具體日志記錄器工廠,繼承抽象日志記錄器工廠LoggerFactory,并實現其中的工廠方法createLogger(),設置好初始化參數和環境變量,返回具體日志記錄器對象;
(3) 修改配置文件config.xml,將新增的具體日志記錄器工廠類的類名字符串替換原有工廠類類名字符串;
(4) 編譯新增的具體日志記錄器類和具體日志記錄器工廠類,運行客戶端測試類即可使用新的日志記錄方式,而原有類庫代碼無須做任何修改,完全符合“開閉原則”。
通過上述重構可以使得系統更加靈活,由于很多設計模式都關注系統的可擴展性和靈活性,因此都定義了抽象層,在抽象層中聲明業務方法,而將業務方法的實現放在實現層中。
疑問
思考
有人說:可以在客戶端代碼中直接通過反射機制來生成產品對象,在定義產品對象時使用抽象類型,同樣可以確保系統的靈活性和可擴展性,增加新的具體產品類無須修改源代碼,只需要將其作為抽象產品類的子類再修改配置文件即可,根本不需要抽象工廠類和具體工廠類。
試思考這種做法的可行性?如果可行,這種做法是否存在問題?為什么?
- Introduction
- 基礎知識
- 設計模式概述
- 從招式與內功談起——設計模式概述(一)
- 從招式與內功談起——設計模式概述(二)
- 從招式與內功談起——設計模式概述(三)
- 面向對象設計原則
- 面向對象設計原則之單一職責原則
- 面向對象設計原則之開閉原則
- 面向對象設計原則之里氏代換原則
- 面向對象設計原則之依賴倒轉原則
- 面向對象設計原則之接口隔離原則
- 面向對象設計原則之合成復用原則
- 面向對象設計原則之迪米特法則
- 六個創建型模式
- 簡單工廠模式-Simple Factory Pattern
- 工廠三兄弟之簡單工廠模式(一)
- 工廠三兄弟之簡單工廠模式(二)
- 工廠三兄弟之簡單工廠模式(三)
- 工廠三兄弟之簡單工廠模式(四)
- 工廠方法模式-Factory Method Pattern
- 工廠三兄弟之工廠方法模式(一)
- 工廠三兄弟之工廠方法模式(二)
- 工廠三兄弟之工廠方法模式(三)
- 工廠三兄弟之工廠方法模式(四)
- 抽象工廠模式-Abstract Factory Pattern
- 工廠三兄弟之抽象工廠模式(一)
- 工廠三兄弟之抽象工廠模式(二)
- 工廠三兄弟之抽象工廠模式(三)
- 工廠三兄弟之抽象工廠模式(四)
- 工廠三兄弟之抽象工廠模式(五)
- 單例模式-Singleton Pattern
- 確保對象的唯一性——單例模式 (一)
- 確保對象的唯一性——單例模式 (二)
- 確保對象的唯一性——單例模式 (三)
- 確保對象的唯一性——單例模式 (四)
- 確保對象的唯一性——單例模式 (五)
- 原型模式-Prototype Pattern
- 對象的克隆——原型模式(一)
- 對象的克隆——原型模式(二)
- 對象的克隆——原型模式(三)
- 對象的克隆——原型模式(四)
- 建造者模式-Builder Pattern
- 復雜對象的組裝與創建——建造者模式(一)
- 復雜對象的組裝與創建——建造者模式(二)
- 復雜對象的組裝與創建——建造者模式(三)
- 七個結構型模式
- 適配器模式-Adapter Pattern
- 不兼容結構的協調——適配器模式(一)
- 不兼容結構的協調——適配器模式(二)
- 不兼容結構的協調——適配器模式(三)
- 不兼容結構的協調——適配器模式(四)
- 橋接模式-Bridge Pattern
- 處理多維度變化——橋接模式(一)
- 處理多維度變化——橋接模式(二)
- 處理多維度變化——橋接模式(三)
- 處理多維度變化——橋接模式(四)
- 組合模式-Composite Pattern
- 樹形結構的處理——組合模式(一)
- 樹形結構的處理——組合模式(二)
- 樹形結構的處理——組合模式(三)
- 樹形結構的處理——組合模式(四)
- 樹形結構的處理——組合模式(五)
- 裝飾模式-Decorator Pattern
- 擴展系統功能——裝飾模式(一)
- 擴展系統功能——裝飾模式(二)
- 擴展系統功能——裝飾模式(三)
- 擴展系統功能——裝飾模式(四)
- 外觀模式-Facade Pattern
- 深入淺出外觀模式(一)
- 深入淺出外觀模式(二)
- 深入淺出外觀模式(三)
- 享元模式-Flyweight Pattern
- 實現對象的復用——享元模式(一)
- 實現對象的復用——享元模式(二)
- 實現對象的復用——享元模式(三)
- 實現對象的復用——享元模式(四)
- 實現對象的復用——享元模式(五)
- 代理模式-Proxy Pattern
- 設計模式之代理模式(一)
- 設計模式之代理模式(二)
- 設計模式之代理模式(三)
- 設計模式之代理模式(四)
- 十一個行為型模式
- 職責鏈模式-Chain of Responsibility Pattern
- 請求的鏈式處理——職責鏈模式(一)
- 請求的鏈式處理——職責鏈模式(二)
- 請求的鏈式處理——職責鏈模式(三)
- 請求的鏈式處理——職責鏈模式(四)
- 命令模式-Command Pattern
- 請求發送者與接收者解耦——命令模式(一)
- 請求發送者與接收者解耦——命令模式(二)
- 請求發送者與接收者解耦——命令模式(三)
- 請求發送者與接收者解耦——命令模式(四)
- 請求發送者與接收者解耦——命令模式(五)
- 請求發送者與接收者解耦——命令模式(六)
- 解釋器模式-Interpreter Pattern
- 自定義語言的實現——解釋器模式(一)
- 自定義語言的實現——解釋器模式(二)
- 自定義語言的實現——解釋器模式(三)
- 自定義語言的實現——解釋器模式(四)
- 自定義語言的實現——解釋器模式(五)
- 自定義語言的實現——解釋器模式(六)
- 迭代器模式-Iterator Pattern
- 遍歷聚合對象中的元素——迭代器模式(一)
- 遍歷聚合對象中的元素——迭代器模式(二)
- 遍歷聚合對象中的元素——迭代器模式(三)
- 遍歷聚合對象中的元素——迭代器模式(四)
- 遍歷聚合對象中的元素——迭代器模式(五)
- 遍歷聚合對象中的元素——迭代器模式(六)
- 中介者模式-Mediator Pattern
- 協調多個對象之間的交互——中介者模式(一)
- 協調多個對象之間的交互——中介者模式(二)
- 協調多個對象之間的交互——中介者模式(三)
- 協調多個對象之間的交互——中介者模式(四)
- 協調多個對象之間的交互——中介者模式(五)
- 備忘錄模式-Memento Pattern
- 撤銷功能的實現——備忘錄模式(一)
- 撤銷功能的實現——備忘錄模式(二)
- 撤銷功能的實現——備忘錄模式(三)
- 撤銷功能的實現——備忘錄模式(四)
- 撤銷功能的實現——備忘錄模式(五)
- 觀察者模式-Observer Pattern
- 對象間的聯動——觀察者模式(一)
- 對象間的聯動——觀察者模式(二)
- 對象間的聯動——觀察者模式(三)
- 對象間的聯動——觀察者模式(四)
- 對象間的聯動——觀察者模式(五)
- 對象間的聯動——觀察者模式(六)
- 狀態模式-State Pattern
- 處理對象的多種狀態及其相互轉換——狀態模式(一)
- 處理對象的多種狀態及其相互轉換——狀態模式(二)
- 處理對象的多種狀態及其相互轉換——狀態模式(三)
- 處理對象的多種狀態及其相互轉換——狀態模式(四)
- 處理對象的多種狀態及其相互轉換——狀態模式(五)
- 處理對象的多種狀態及其相互轉換——狀態模式(六)
- 策略模式-Strategy Pattern
- 算法的封裝與切換——策略模式(一)
- 算法的封裝與切換——策略模式(二)
- 算法的封裝與切換——策略模式(三)
- 算法的封裝與切換——策略模式(四)
- 模板方法模式-Template Method Pattern
- 模板方法模式深度解析(一)
- 模板方法模式深度解析(二)
- 模板方法模式深度解析(三)
- 訪問者模式-Visitor Pattern
- 操作復雜對象結構——訪問者模式(一)
- 操作復雜對象結構——訪問者模式(二)
- 操作復雜對象結構——訪問者模式(三)
- 操作復雜對象結構——訪問者模式(四)
- 設計模式趣味學習(復習)
- 設計模式與足球(一)
- 設計模式與足球(二)
- 設計模式與足球(三)
- 設計模式與足球(四)
- 設計模式綜合應用實例
- 多人聯機射擊游戲
- 多人聯機射擊游戲中的設計模式應用(一)
- 多人聯機射擊游戲中的設計模式應用(二)
- 數據庫同步系統
- 設計模式綜合實例分析之數據庫同步系統(一)
- 設計模式綜合實例分析之數據庫同步系統(二)
- 設計模式綜合實例分析之數據庫同步系統(三)