簡單工廠模式雖然簡單,但存在一個很嚴重的問題當系統中須要引入新產品時,因爲靜態數據庫
工廠方法經過所傳入參數的不一樣來建立不一樣的產品,這一定要修改工廠類的源代碼,將違
背「開閉原則」編程
在簡單工廠模式中,全部的產品都由同一個工廠建立,工廠類職
責較重,業務邏輯較爲複雜,具體產品與工廠類之間的耦合度高,嚴重影響了系統的靈活性
和擴展性,而工廠方法模式則能夠很好地解決這一問題。ide
在工廠方法中函數
不在提供一個統一的工廠類來建立全部的產品對象測試
針對不一樣的產品提供不一樣的工廠spa
系統提供一個與產品等級結構對應的工廠等級結構設計
定義:3d
定義一個用於建立對象的接口,讓子類決定將哪個
類實例化。工廠方法模式讓一個類的實例化延遲到其子類。日誌
工廠方法模式又簡稱爲工廠模式又可稱做虛擬構造器模式或多態工廠模式
工廠方法模式是一種類建立型模式。code
圖示:
角色:
Product(抽象產品):它是定義產品的接口,是工廠方法模式所建立對象的超類型,
是全部產品的公共父類
ConcreteProduct(具體產品):它實現了抽象產品接口,某種類型的具體產品由專門的具體
工廠建立,具體工廠和具體產品之間一一對應。
Factory(抽象工廠):在抽象工廠類中,聲明瞭工廠方法(Factory Method),用於返回一個
產品。抽象工廠是工廠方法模式的核心,全部建立對象的工廠類都必須實現該接口。
interface Factory { public Product factoryMethod(); }
聲明瞭工廠方法但並未實現工廠方法,具體產品對象的建立由其子類負責,客
戶端針對抽象工廠編程,可在運行時再指定具體工廠類
ConcreteFactory(具體工廠):它是抽象工廠類的子類,實現了抽象工廠中定義的工廠方
法,並可由客戶端調用,返回一個具體產品類的實例。
class ConcreteFactory implements Factory { public Product factoryMethod() { return new ConcreteProduct(); } }
事例:
日誌記錄器的設計
記錄器能夠經過多種途徑保存系
統的運行日誌,如經過文件記錄或數據庫記錄,用戶能夠經過修改配置文件靈活地更換日誌
記錄方式
開發人員發現須要對日誌記錄器進行一些
初始化工做,初始化參數的設置過程較爲複雜,並且某些參數的設置有嚴格的前後次序,否
則可能會發生記錄失敗。
需求分析:
(1) 須要封裝日誌記錄器的初始化過程,這些初始化工做較爲複雜,例如須要初始化其餘相關
的類,還有可能須要讀取配置文件(例如鏈接數據庫或建立文件),致使代碼較長,若是將
它們都寫在構造函數中,會致使構造函數龐大,不利於代碼的修改和維護;
(2) 用戶可能須要更換日誌記錄方式,在客戶端代碼中須要提供一種靈活的方式來選擇日誌記
錄器,儘可能在不修改源代碼的基礎上更換或者增長日誌記錄方式。
class LoggerFactory { //靜態工廠方法 public static Logger createLogger(String args) { if(args.equalsIgnoreCase("db")) { //鏈接數據庫,代碼省略 //建立數據庫日誌記錄器對象 Logger logger = new DatabaseLogger(); //初始化數據庫日誌記錄器,代碼省略 return logger; } else if(args.equalsIgnoreCase("file")) { //建立日誌文件 //建立文件日誌記錄器對象 Logger logger = new FileLogger(); //初始化文件日誌記錄器,代碼省略 return logger; } else { return null; } } }
存在的問題:
(1) 工廠類過於龐大,包含了大量的if…else…代碼,致使維護和測試難度增大;
(2) 系統擴展不靈活,若是增長新類型的日誌記錄器,必須修改靜態工廠方法的業務邏輯,違
反了「開閉原則」。
解決方案:
//抽象產品 //日誌紀錄器的接口 public interface Logger { public void writeLog(); }
//具體產品 //文件日誌記錄器 public class FileLogger implements Logger { @Override public void writeLog() { System.out.println("文件日誌記錄"); } }
//具體產品 //數據庫日誌記錄器 public class DataBaseLogger implements Logger { @Override public void writeLog() { System.out.println("數據庫日誌記錄"); } }
//抽象工廠 public interface LoggerFactory { public Logger createLogger(); }
//具體工廠 //數據庫日誌記錄器的工廠類 public class DataBaseLoggerFactory implements LoggerFactory { @Override public Logger createLogger() { Logger logger = new DataBaseLogger(); return logger; } }
//具體工廠 //文件日誌記錄器的工廠類 public class FileLoggerFactory implements LoggerFactory { @Override public Logger createLogger() { Logger logger = new FileLogger(); return logger; } }
測試:
public class client { public static void main(String[] args) { LoggerFactory factory; Logger logger; factory = new FileLoggerFactory(); logger = factory.createLogger(); logger.writeLog(); } }
此時的結構圖:
重載工廠方法:
開發人員經過進一步分析,發現能夠經過多種方式來初始化日誌記錄器,例如能夠
爲各類日誌記錄器提供默認實現;還能夠爲數據庫日誌記錄器提供數據庫鏈接字符串,爲文
件日誌記錄器提供文件路徑;也能夠將參數封裝在一個Object類型的對象中,經過Object對象
將配置參數傳入工廠類。
//抽象工廠 public interface LoggerFactory { public Logger createLogger(); public Logger createLogger(String msg); }
//具體工廠 //文件日誌記錄器的工廠類 public class FileLoggerFactory implements LoggerFactory { @Override public Logger createLogger() { Logger logger = new FileLogger(); return logger; } @Override public Logger createLogger(String msg) { //使用參數msg斤西瓜鏈接數據庫 //....... Logger logger = new FileLogger(); return logger; } }
//具體工廠 //數據庫日誌記錄器的工廠類 public class DataBaseLoggerFactory implements LoggerFactory { @Override public Logger createLogger() { Logger logger = new DataBaseLogger(); return logger; } @Override public Logger createLogger(String msg) { //使用參數msg斤西瓜鏈接數據庫 //...... Logger logger = new DataBaseLogger(); return logger; } }
優勢:
(1) 在工廠方法模式中,工廠方法用來建立客戶所須要的產品,同時還向客戶隱藏了哪一種具體
產品類將被實例化這一細節,用戶只須要關心所需產品對應的工廠,無須關心建立細節,甚
至無須知道具體產品類的類名。
(2) 基於工廠角色和產品角色的多態性設計是工廠方法模式的關鍵。它可以讓工廠能夠自主確
定建立何種產品對象,而如何建立這個對象的細節則徹底封裝在具體工廠內部。工廠方法模
式之因此又被稱爲多態工廠模式,就正是由於全部的具體工廠類都具備同一抽象父類。
(3) 使用工廠方法模式的另外一個優勢是在系統中加入新產品時,無須修改抽象工廠和抽象產品
提供的接口,無須修改客戶端,也無須修改其餘的具體工廠和具體產品,而只要添加一個具
體工廠和具體產品就能夠了,這樣,系統的可擴展性也就變得很是好,徹底符合「開閉原則」。
缺點:
(1) 在添加新產品時,須要編寫新的具體產品類,並且還要提供與之對應的具體工廠類,系統
中類的個數將成對增長,在必定程度上增長了系統的複雜度,有更多的類須要編譯和運行,
會給系統帶來一些額外的開銷。
(2) 因爲考慮到系統的可擴展性,須要引入抽象層,在客戶端代碼中均使用抽象層進行定義,
增長了系統的抽象性和理解難度,且在實現時可能須要用到DOM、反射等技術,增長了系統
的實現難度
適用場景:
(1) 客戶端不知道它所須要的對象的類。在工廠方法模式中,客戶端不須要知道具體產品類的
類名,只須要知道所對應的工廠便可,具體的產品對象由具體工廠類建立,可將具體工廠類
的類名存儲在配置文件或數據庫中。
(2) 抽象工廠類經過其子類來指定建立哪一個對象。在工廠方法模式中,對於抽象工廠類只須要提供一個建立產品的接口,而由其子類來肯定具體要建立的對象,利用面向對象的多態性和里氏代換原則,在程序運行時,子類對象將覆蓋父類對象,從而使得系統更容易擴展。