工廠方法模式是類的建立模式,又叫作虛擬構造子(Virtual Constructor)模式或者多態性工廠(Polymorphic Factory)模式。數據庫
工廠方法模式的用意是定義一個建立產品對象的工廠接口,將實際建立工做推遲到子類中。設計模式
那麼工廠方法模式是在什麼場景下使用呢?ide
相信不少人都作過導入導出功能,就拿導出功能來講。有這麼一個需求:XX系統須要支持對數據庫中的員工薪資進行導出,而且支持多種格式如:HTML、CSV、PDF等,每種格式導出的結構有所不一樣,好比:財務跟其餘人對導出薪資的HTML格式要求可能會不同,由於財務可能須要特定的格式方便覈算或其餘用途。spa
若是使用簡單工廠模式,則工廠類一定過於臃腫。由於簡單工廠模式只有一個工廠類,它須要處理全部的建立的邏輯。假如以上需求暫時只支持3種導出的格式以及2種導出的結構,那工廠類則須要6個if else來建立6種不一樣的類型。若是往後需求不斷增長,則後果不堪設想。設計
這時候就須要工廠方法模式來處理以上需求。在工廠方法模式中,核心的工廠類再也不負責全部的對象的建立,而是將具體建立的工做交給子類去作。這個核心類則搖身一變,成爲了一個抽象工廠角色,僅負責給出具體工廠子類必須實現的接口,而不接觸哪個類應當被實例化這種細節。對象
這種進一步抽象化的結果,使這種工廠方法模式能夠用來容許系統在不修改具體工廠角色的狀況下引進新的產品,這一特色無疑使得工廠方法模式具備超過簡單工廠模式的優越性。下面就針對以上需求設計UML圖:接口
從上圖能夠看出,這個使用的工廠方法模式的系統涉及到如下角色:ci
抽象工廠(ExportFactory)角色:擔任這個角色的是工廠方法模式的核心,任何在模式中建立對象的工廠類必須實現這個接口。在實際的系統中,這個角色也經常使用抽象類實現。開發
具體工廠(ExportHtmlFactory、ExportPdfFactory)角色:擔任這個角色的是實現了抽象工廠接口的具體JAVA類。具體工廠角色含有與業務密切相關的邏輯,而且受到使用者的調用以建立導出類(如:ExportStandardHtmlFile)。產品
抽象導出(ExportFile)角色:工廠方法模式所建立的對象的超類,也就是全部導出類的共同父類或共同擁有的接口。在實際的系統中,這個角色也經常使用抽象類實現。
具體導出(ExportStandardHtmlFile等)角色:這個角色實現了抽象導出(ExportFile)角色所聲明的接口,工廠方法模式所建立的每個對象都是某個具體導出角色的實例。
首先是抽象工廠角色源代碼。它聲明瞭一個工廠方法,要求全部的具體工廠角色都實現這個工廠方法。參數type表示導出的格式是哪種結構,如:導出HTML格式有兩種結構,一種是標準結構,一種是財務須要的結構。
public interface ExportFactory {
public ExportFile factory(String type);
}
具體工廠角色類源代碼:
public class ExportHtmlFactory implements ExportFactory{
@Override
public ExportFile factory(String type) {
// TODO Auto-generated method stub
if("standard".equals(type)){
return new ExportStandardHtmlFile();
}else if("financial".equals(type)){
return new ExportFinancialHtmlFile();
}else{
throw new RuntimeException("沒有找到對象");
}
}
}
public class ExportPdfFactory implements ExportFactory {
@Override
public ExportFile factory(String type) {
// TODO Auto-generated method stub
if("standard".equals(type)){
return new ExportStandardPdfFile();
}else if("financial".equals(type)){
return new ExportFinancialPdfFile();
}else{
throw new RuntimeException("沒有找到對象");
}
}
}
抽象導出角色類源代碼:
public interface ExportFile {
public boolean export(String data);
}
具體導出角色類源代碼,一般狀況下這個類會有複雜的業務邏輯。
public class ExportFinancialHtmlFile implements ExportFile{
@Override
public boolean export(String data) {
// TODO Auto-generated method stub
/**
* 業務邏輯
*/
System.out.println("導出財務版HTML文件");
return true;
}
}
public class ExportFinancialPdfFile implements ExportFile{
@Override
public boolean export(String data) {
// TODO Auto-generated method stub
/**
* 業務邏輯
*/
System.out.println("導出財務版PDF文件");
return true;
}
}
public class ExportStandardHtmlFile implements ExportFile{
@Override
public boolean export(String data) {
// TODO Auto-generated method stub
/**
* 業務邏輯
*/
System.out.println("導出標準HTML文件");
return true;
}
}
public class ExportStandardPdfFile implements ExportFile {
@Override
public boolean export(String data) {
// TODO Auto-generated method stub
/**
* 業務邏輯
*/
System.out.println("導出標準PDF文件");
return true;
}
}
客戶端角色類源代碼:
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String data = "";
ExportFactory exportFactory = new ExportHtmlFactory();
ExportFile ef = exportFactory.factory("financial");
ef.export(data);
}
}
客戶端建立ExportHtmlFactory對象,這時客戶端所持有變量的靜態類型爲ExportFactory,而實際類型爲ExportHtmlFactory。而後客戶端調用ExportHtmlFactory對象的工廠方法factory(),接着後者調用ExportFinancialHtmlFile的構造子建立出導出對象。
工廠方法模式和簡單工廠模式在結構上的不一樣很明顯。工廠方法模式的核心是一個抽象工廠類,而簡單工廠模式把核心放在一個具體類上。
工廠方法模式退化後能夠變得很像簡單工廠模式。設想若是很是肯定一個系統只須要一個具體工廠類,那麼不妨把抽象工廠類合併到具體工廠類中去。因爲只有一個具體工廠類,因此不妨將工廠方法改成靜態方法,這時候就獲得了簡單工廠模式。
若是系統須要加入一個新的導出類型,那麼所須要的就是向系統中加入一個這個導出類以及所對應的工廠類。沒有必要修改客戶端,也沒有必要修改抽象工廠角色或者其餘已有的具體工廠角色。對於增長新的導出類型而言,這個系統徹底支持「開-閉原則」。
一個應用系統是由多人開發的,導出的功能是你實現的,可是使用者(調用這個方法的人)可能倒是其餘人。這時候你應該設計的足夠靈活並儘量下降二者之間的耦合度,當你修改或增長一個新的功能時,使用者不須要修改任何地方。假如你的設計不夠靈活,那麼將沒法面對客戶多變的需求。可能一個極小的需求變動,都會使你的代碼結構發生改變,並致使其餘使用你所提供的接口的人都要修改他們的代碼。牽一處而動全身,這就使得往後這個系統將難以維護。