《研磨設計模式》之工廠方法模式
一、應用場景
實現一個能導出數據到各種文件格式(txt,DBFile,Excel,Xml)的應用框架.
1.不管用戶導出神馬樣的文件格式,都要進行一個導出的操作.
2.系統不知道用戶會導出哪種格式的文件
書中描述,框架是一個半成品,開發者可以在此基礎上完成軟件功能。因此具體是導出神馬樣的格式,是由具體的開發者(本例是Client類)決定的.
根據1.都要進行一個導出操作,因此要定義具有導出功能的接口
1: /**2: * 定義數據導出的功能的接口3: * @author YoungCold4: *5: */6: public interface ExportFileApi {7: /**8: * 將數據導出成文件(具體文件格式有實現類決定)9: * @param data10: * @return11: */12: public boolean export(String data);13: }
對於導出數據的業務功能對象,也就是用ExportFileApi定義的導出數據的方法export()完成導出數據這一功能的類的實例.它需要根據用戶的需要(比如,用戶選擇導出數據到Excel文件)創建相應的ExportFileApi對象(實現ExportFileApi定義的export():將數據導出到Excel),也就是說特定的ExportFileApi實現由實際情況決定。問題是,業務功能對象不知道如何創建ExportFileApi的對象(實際上,也不需要知道)
二、工廠模式
工廠方法的定義:定義一個用於創建對象的接口,讓子類決定實例化哪一個類①,Factory Method使一個類②的實例化延遲到子類。
補充:就對應用場景而言,個人感覺,①和②指的是同一個類——實現ExportFileApi的類。
前面提過,實現導出功能的業務類跟本不知道實際會選擇哪種導出數據的格式,因此該對象就不應該和具體的導出文件對象(實現ExportFileApi的類)耦合在一起,只需要知道ExportFileApi接口即可。這樣一來,業務類要求不能和ExportFileApi具體實現類耦合,但業務類同時要實現導出數據的功能,這就必然要用到ExportFileApi具體實現類的功能。
解決方法:既然需要接口對象,那就定義一個方法創建接口對象。但事實上,業務類並不知道要創建哪種接口對象,因此創建接口對象的任務就由業務類的子類完成,比方說是導出txt文件的業務類(業務類的子類)完成,這樣導出txt文件的業務類負責創建出接口對象,該接口對象完成數據導出到txt的功能。所以,業務類的創建接口對象的方法就定義成抽象方法。
1.實現導出功能的業務類是個抽象類2.具體的導出操作有業務類的子類完成數據導出功能業務類1: /**2: * 實現導出數據的業務功能對象3: * 即使用數據導出的工具類,實現實際的數據導出4: * @author YoungCold5: *6: */7: public abstract class ExportOperate {8: public boolean export(String data){9: ExportFileApi exporter = createExportFileApi();
10: return exporter.export(data);11: }
12:
13: public abstract ExportFileApi createExportFileApi();14: }
數據導出功能業務類的子類,實現具體的導出功能(創建何種接口對象)
接口對象(實現數據導出到txt文件)1: /**2: * 導出數據的工具類,實現數據導出到文本文件3: * @author YoungCold4: *5: */6: public class ExportTxtFile implements ExportFileApi {7:
8: @Override
9: public boolean export(String data) {10: System.out.println("導出數據"+data+"到文本文件");11: return true;12: }
13:
14: }
接口對象(實現數據導出到數據庫文件)1: /**2: * 導出數據的工具類,實現數據導出到數據庫文件3: * @author YoungCold4: *5: */6: public class ExportDBFile implements ExportFileApi {7:
8: @Override
9: public boolean export(String data) {10: System.out.println("導出數據"+data+"到數據庫文件");11: return true;12: }
13:
14: }
數據導出業務類(負責創建 數據導出到txt文件的接口對象)1: public class ExportTxtFileOperate extends ExportOperate {2:
3: @Override
4: public ExportFileApi createExportFileApi() {5: // 創建數據導出到txt文件的接口對象6: return new ExportTxtFile();7: }
8:
9: }
數據導出業務類(負責創建 數據導出到數據庫文件的接口對象)1: public class ExportDBFileOperate extends ExportOperate {2:
3: @Override
4: public ExportFileApi createExportFileApi() {5: // 創建數據導出到數據庫文件的接口對象6: return new ExportDBFile();7: }
8:
9: }
這樣,當用戶選擇數據導出到具體格式的文件時,我們只需要創建這種文件格式的業務類的對象即可。
比如,用戶選擇數據導出到數據庫文件
1: String data = "2013-1-13";//要導出的數據2: ExportOperate exporter1 = new ExportDBFileOperate();//要導出到數據庫文件,就創建導出到數據庫的業務類對象3: exporter1.export(data);//執行導出操作
用戶選擇數據導出到txt文件
1: String data = "2013-1-13";2: ExportOperate exporter2 = new ExportTxtFileOperate();//要導出到txt文件,就創建導出到txt的業務類對象3: exporter2.export(data);
再來看數據導出業務抽象類,所實現的數據導出方法1: public boolean export(String data){2: ExportFileApi exporter = createExportFileApi();
3: return exporter.export(data);4: }
ExportFileApi exporter = createExportFileApi();業務類的導出功能代碼中,沒有和具體的接口對象耦合,根本不知道創建的哪一個接口對象,它只關心得到一個接口對象,再調用接口對象的導出方法,完成數據導出功能。