簡單工廠、工廠方法和抽象工廠之間的區別
計模式中的工廠模式(Factory Design pattern)是一個比較常用的創建型設計模式,其中可以細分為三種:簡單工廠(Simple Factory)、工廠方法(Factory Method)和抽象工廠(Abstract Factory)。那么三者有什么區別呢?先說結論:
簡單工廠:只有唯一工廠(簡單工廠),一個產品接口/抽象類,根據簡單工廠中的靜態方法來創建具體產品對象。適用于產品較少,幾乎不擴展的情景
工廠方法:有多個工廠(抽象工廠+多個具體工廠),一個產品接口/抽象類,根據繼承抽象工廠中的方法來多態創建具體產品對象。適用于一個類型的多個產品
抽象方法:有多個工廠(抽象工廠+多個具體工廠),多個產品接口/抽象類,對產品子類進行分組,根據繼承抽象工廠中的方法多態創建同組的不同具體產品對象。適用于多個類型的多個產品
下面具體展開說明
一、簡單工廠模式(Simple Factory Pattern)
1.1 簡單工廠模式介紹
簡單工廠模式又叫做靜態工廠方法模式(static Factory Method pattern),它是通過使用靜態方法接收不同的參數來返回不同的實例對象。我們通過一個類圖來進行講解:
Product接口:定義要創建的產品對象的接口
ProductA、ProductB、ProductC產品類:實現產品接口,具有產品接口特性的具體產品
SimpleFactory簡單工廠:只有一個工廠,通過靜態方法createProduct創建具體的產品對象
client客戶端:客戶端有多個,每個客戶端可以通過簡單工廠來創建具體的產品對象
1.2 簡單工廠模式實現
我們以上面類圖為例,實現簡單工廠模式:
/**產品接口**/ public interface Product { void doSomething(); } /**具體產品實現**/ class ProductA implements Product{ @Override public void doSomething() { System.out.println("我是ProductA"); } } class ProductB implements Product{ @Override public void doSomething() { System.out.println("我是ProductB"); } } class ProductC implements Product{ @Override public void doSomething() { System.out.println("我是ProductC"); } } /**簡單工廠**/ public class SimpleFactory { /**工廠類創建產品靜態方法**/ public static Product createProduct(String productName) { Product instance = null; switch (productName){ case "A": instance = new ProductA(); break; case "B": instance = new ProductB(); break; case "C": instance = new ProductC(); } return instance; } /**客戶端(client)調用工廠類**/ public static void main(String[] args) { SimpleFactory simpleFactory = new SimpleFactory(); createProduct("A").doSomething(); createProduct("B").doSomething(); } }
優點:簡單工廠可以使客戶端免除直接創建對象的職責,能夠根據需要創建出對應的產品。實現客戶端和產品類代碼分離。此外可以通過配置文件來實現不修改客戶端代碼的情況下添加新的具體產品類(改進)。
缺點:違背開閉原則,如果需要新增其他產品類,就必須在工廠類中新增if-else邏輯判斷(可以通過配置文件來改進)。但是整體來說,系統擴展還是相對其他工廠模式要困難。
我們發現,簡單工廠模式中的工廠類使用的是靜態方法,那么為什么要這樣做呢?可不可以使用非靜態的方法呢?
使用靜態方法可以不需要使用new的方式創建對象,方便調用
靜態方法意味著可以直接獲得實例對象,非靜態方法只能通過構造方法(一般私有)調用,在工廠類以外不能被訪問
對于一些實例化和銷毀對象比較敏感的場景,比如數據庫連接池,實例化對象能夠重復穩定的被使用
綜上來說,簡單工廠模式適用于業務簡單,產品固定不會經常改變工廠類的情況。
1.3 簡單工廠模式使用場景
下面來看看簡單工廠模式一般用于哪些業務場景
在Java 中就有這樣的設計,比如DateFormat中的這個方法就是簡單工廠的應用
private static DateFormat get(LocaleProviderAdapter adapter, int timeStyle, int dateStyle, Locale loc) { DateFormatProvider provider = adapter.getDateFormatProvider(); DateFormat dateFormat; //邏輯判斷實現那個具體對象 if (timeStyle == -1) { dateFormat = provider.getDateInstance(dateStyle, loc); } else { if (dateStyle == -1) { dateFormat = provider.getTimeInstance(timeStyle, loc); } else { dateFormat = provider.getDateTimeInstance(dateStyle, timeStyle, loc); } } return dateFormat; }
此外還有Calender等,在Spring 源碼中也可以看到一些以"Factory"結尾的類,這些都是工廠模式的使用。
比如在業務連接數據庫時,需要支持不同的數據庫,比如有dbcp、c3p0、druid等等,這個時候數據庫連接方式有限,而且比較固定不容易更改,所以可以嘗試采用簡單工廠模式來進行管理數據庫連接對象。
二、工廠方法模式(Factory Method Pattern)
我們知道簡單工廠模式有違背開閉原則,不容易擴展的缺點,所以在 GOF 23種設計模式中也沒有簡單工廠模式,下面我們就來看看另外一種工廠模式:工廠方法模式
2.1 工廠方法模式介紹
抽象工廠模式所要解決的問題是在一個產品族上,若存在多個不同類型的產品情況下,接口選擇的問題。
工廠方法模式實際上是簡單工廠模式的升級,工廠方法模式定義除了產品接口外,還定義了一個用于創建對象工廠的接口,讓工廠子類再去實例化對應的產品類。通過類圖來解釋:
Product接口:和簡單工廠相同,提供產品對象的接口
ProductA、ProductB和productC:具體類型的產品對象
FactoryA、FactoryB和FactoryC:具體的產品工廠,實現具體的產品對象
AbstractFactory:抽象工廠,可以有多個,其中的方法負責返回創建的產品對象
Client:使用該模式的客戶端
2.2 工廠方法模式實現
對照著上面的類圖,我們可以對應實現相應的代碼:
/**產品接口**/ public interface Product { void doSomething(); } /**具體產品實現**/ class ProductA implements Product{ @Override public void doSomething() { System.out.println("我是ProductA"); } } class ProductB implements Product{ @Override public void doSomething() { System.out.println("我是ProductB"); } } class ProductC implements Product{ @Override public void doSomething() { System.out.println("我是ProductC"); } } /**工廠接口**/ public interface AbstractFactory { /**創建Product方法,區別與工廠模式的靜態方法**/ public Product createProduct(); } /**具體工廠實現**/ class FactoryA implements AbstractFactory{ @Override public Product createProduct() { return new ProductA(); } } class FactoryA implements AbstractFactory{ @Override public Product createProduct() { return new ProductA(); } } class FactoryA implements AbstractFactory{ @Override public Product createProduct() { return new ProductA(); } } /**客戶端調用工廠**/ public class Client { public static void main(String[] args) { Product productA = new FactoryA().createProduct(); productA.doSomething(); Product productB = new FactoryB().createProduct(); productB.doSomething(); } }
其中最主要的是 AbstractFactory類中的createProduct方法,通過這個方法來生成具體產品,這也是為什么叫工廠方法的原因。和簡單工廠的靜態方法不同,這里是使用的非靜態調用方式。而且可以發現,沒有了簡單工廠中的 if-else邏輯判斷,相對而言擴展性也要強的多。
優點:完全實現開閉原則,實現了可擴展和更復雜的層次結構。明確了職責,具有多態性,適用于任何實體類。
缺點:如果業務增加,會使得系統中類的個數成倍增加,提高了代碼的復雜度
2.3 工廠方法模式使用場景
在Slf4j 這個我們經常使用的日志框架中,就有工廠方法模式的應用,比如使用頻率很高的獲取logger對象實例中:
private Logger logger = LoggerFactory.getLogger(Client.class);
點進源碼看我們會發現這個getLogger方法:
//簡單工廠模式 public static Logger getLogger(String name) { /**工廠方法模式的使用**/ ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); } //工廠接口 public interface ILoggerFactory { Logger getLogger(String var1); } //Logger產品接口 public interface Logger { String ROOT_LOGGER_NAME = "ROOT"; ... }
需要調用工廠方法接口來實現具體logger 對象實例,這就是一個工廠方法模式的一個典型應用
在一些需要不同類型的規則配置解析時,我們也可以用到工廠方法模式,比如引用《設計模式之美》的代碼:
public class RuleConfigSource { public RuleConfig load(String ruleConfigFilePath) { String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath); IRuleConfigParserFactory parserFactory = RuleConfigParserFactoryMap.getParserFactory(ruleConfigFileExtension); if (parserFactory == null) { throw new InvalidRuleConfigException("Rule config file format is not supported: " + ruleConfigFilePath); } IRuleConfigParser parser = parserFactory.createParser(); String configText = ""; //從ruleConfigFilePath文件中讀取配置文本到configText中 RuleConfig ruleConfig = parser.parse(configText); return ruleConfig; } private String getFileExtension(String filePath) { //...解析文件名獲取擴展名,比如rule.json,返回json return "json"; } } //因為工廠類只包含方法,不包含成員變量,完全可以復用, //不需要每次都創建新的工廠類對象,所以,簡單工廠模式的第二種實現思路更加合適。 public class RuleConfigParserFactoryMap { //工廠的工廠 private static final Map
在需要添加新的規則配置解析器時,只需要創建新的 parser 類和 parserfactory 完成不同的配置
三、抽象工廠模式(Abastract Factory Pattern)
抽象工廠模式沒有簡單工廠和工廠方法模式那么常用,場景比較特殊,在簡單工廠和工廠方法中,對于類只有一種分類方式,比如簡單工廠中,根據產品類型分為ProductA、ProductB和ProductC。但是如果有多種分類方式,比如按照產品的生產商分類,ProductA可能和ProductC為一類。這樣就用到了抽象工廠模式
3.1 抽象工廠模式介紹
抽象工廠模式(Abstract Factory Pattern)屬于創建型模式,它實際上是對工廠方法模式的擴展,相當于一個超級工廠,用于創建其他工廠的模式。在抽象工廠模式中,接口是負責創建一個相關對象的工廠,而且每個工廠都能按照工廠模式提供對象。其實抽象工廠也是為了減少工廠方法中的子類和工廠類數量,基于此提出的設計模式,如下圖(來源淘系技術):
比如在工廠方法中,我們只能按照鍵盤、主機、顯示器分別進行分類,這樣會造成大量的工廠類和產品子類。而抽象工廠可以將上述三種產品類進行分組,可以大大減少工廠類的數量。我們再來看看對應的類圖:
Product1和Product2:定義一種類型的產品對象接口
Product1A、Product1B等:各種類型的具體產品對象
FactoryA和FactoryB:具體產品工廠,負責創建該工廠類型下的產品對象
AbstractFactory:抽象工廠接口,定義一類產品對象
Client:客戶端,使用抽象工廠,調用產品對象
3.2 抽象工廠模式實現
下面就根據上面的類圖,利用代碼實現抽象工廠:
/**Product1類的產品接口**/ public interface Product1 { void doSomething(); } class Product1A implements Product1{ @Override public void doSomething() { System.out.println("我是Product1A"); } } class Product1B implements Product1{ @Override public void doSomething() { System.out.println("我是Product1B"); } } /** Product2類的產品接口**/ public interface Product2 { void doSomething(); } class Product2A implements Product1{ @Override public void doSomething() { System.out.println("我是Product2A"); } } class Product2B implements Product1{ @Override public void doSomething() { System.out.println("我是Product2B"); } } /**抽象工廠接口**/ public interface AbstractFactory { public Product1 createProduct1(); public Product2 createProduct2(); } /**A類工廠**/ public class FactoryA implements AbstractFactory{ @Override public Product1 createProduct1() { return new Product1A(); } @Override public Product2 createProduct2() { return new Product2A(); } } /**B類工廠**/ public class FactoryB implements AbstractFactory{ @Override public Product1 createProduct1() { return new Product1B(); } @Override public Product2 createProduct2() { return new Product2B(); } } /**Client客戶端調用**/ public class Client { public static void main(String[] args) { new FactoryA().createProduct1().doSomething(); new FactoryB().createProduct2().doSomething(); } }
優點:增加分組比較容易,而且能大大減少工廠類的數量
缺點:因為分組,所以分組中的產品擴展就比較困難,比如再新增一個Product3,就需要改動AbstractFactory、FactoryA和FactoryB幾乎所有工廠類
綜上,沒有哪種方法是萬金油,要針對業務場景來使用哪種工廠模式
參考資料
https://www.zhihu.com/question/27125796/answer/1615074467
《重學設計模式》
https://www.cnblogs.com/sunweiye/p/10815928.html
https://time.geekbang.org/column/article/197254
數據庫
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。