繼續說以前提到的簡單工廠模式,嚴格來講,這不算一個設計模式……並且在業務量比較多了以後,它也有諸多的隱患程序員
一、因爲工廠類集中了全部實例的建立邏輯,這就直接致使一旦這個工廠出了問題,全部的客戶端都會受到牽連設計模式
二、因爲簡單工廠模式的產品基於一個共同的抽象類或者接口,這樣一來,產品的種類增長的時候,即有不一樣的產品接口或者抽象類的時候,簡單工廠類就須要維護大量的if-else判斷,好比導入導出功能,雖然能夠用反射或者配置文件,註解等解耦,可是還有更好的辦法。app
仍是舉那個水果的例子:以前水果園子裏只有蘋果和香蕉兩種水果,如今又新加一個梨的品種。簡單工廠類的代碼以下:ide
// 簡單工廠類 public class FruitFactoryA { public static FruitA getFruit(String type) { if ("apple".equalsIgnoreCase(type)) { return new AppleA(); } else if ("banana".equalsIgnoreCase(type)) { return new BananaA(); } else if ("pear".equalsIgnoreCase(type)) { return new PearA(); } else { System.out.print("error!"); } return null; } }
該例子使用簡單工廠模式,工廠類過於臃腫。ui
public class Main { public static void main(String[] args) { FruitA apple = FruitFactoryA.getFruit("apple"); FruitA banana = FruitFactoryA.getFruit("banana"); FruitA pear = FruitFactoryA.getFruit("pear"); // 不太好,沒有檢測null異常,演示 apple.get(); banana.get(); pear.get(); } }
由於簡單工廠模式只有一個工廠類,它須要處理全部的對象建立的邏輯。若是往後需求不斷增長,則後果不堪設想,違背了單一職責,致使系統喪失靈活性和可維護性。並且更重要的是,簡單工廠模式違背了OCP——開放封閉原則。固然還有單一職責原則(雖然這裏沒具體體現)。那麼如何改進呢?spa
如何才能實現工廠類的代碼不去修改,而開放擴展——能夠把工廠的職責抽象,抽象if-else判斷邏輯,把每個判斷都抽象爲一個工廠類,而這些具體的工廠類統一抽象爲一個接口來約束具體子類去生產產品,這就是傳說中的——工廠方法模式,它一樣屬於類的建立型模式,又被稱爲多態工廠模式。設計
工廠方法模式的意義是定義一個建立產品對象的工廠接口,將實際建立工做推遲到子類當中。核心工廠類再也不負責產品的建立,這樣核心類成爲一個抽象工廠角色,僅負責具體工廠子類必須實現的接口(僅僅起到一個約束生產動做的做用),這樣進一步抽象化的好處是使得工廠方法模式可使系統在不修改具體工廠角色的狀況下引進新的產品。code
抽象工廠角色:工廠方法模式的核心,任何工廠類都必須實現這個接口對象
public interface FactoryA { FruitA getFruit(); }
具體工廠( Concrete Creator)角色;具體工廠類是抽象工廠的一個實現,負責實例化產品對象blog
public class AppleFactoryA implements FactoryA { @Override public FruitA getFruit() { return new AppleA(); } } public class BananaFactoryA implements FactoryA { @Override public FruitA getFruit() { return new BananaA(); } } public class PearFactory implements FactoryA { @Override public FruitA getFruit() { return new PearA(); } }
抽象產品(Product)角色;工廠方法模式所建立的全部對象的父類,它負責描述全部實例所共有的公共接口
public interface FruitA { void get(); }
具體產品(Concrete Product)角色;工廠方法模式所建立的具體實例對象
public class AppleA implements FruitA { @Override public void get() { System.out.println("蘋果"); } } public class BananaA implements FruitA { @Override public void get() { System.out.println("香蕉"); } } public class PearA implements FruitA { @Override public void get() { System.out.println("梨"); } }
客戶端調用
public class Main { public static void main(String[] args) { // 獲得對應的水果的工廠 FactoryA appleF = new AppleFactoryA(); FactoryA bananaF = new BananaFactoryA(); FactoryA peatF = new PearFactory(); // 經過各個工廠去獲得對應的水果 FruitA apple = appleF.getFruit(); FruitA banana = bananaF.getFruit(); FruitA pear = peatF.getFruit(); apple.get(); banana.get(); pear.get(); } }
如上代碼,若是之後有新的水果出現,好比橘子,那麼不用修改工廠類,只須要增長一個橘子以及橘子的工廠類,且橘子工廠同時去遵循工廠的接口便可,客戶端調用就能夠了,而其餘已經寫好的工廠類無需修改!徹底符合OCP原則,同時每一個具體工廠子類只負責對應水果的生成,也遵照了單一職責原則
類圖以下
工廠方法模式與簡單工廠模式在結構上的不一樣不是很明顯。
一、工廠方法類的核心是一個抽象工廠類,而簡單工廠模式把核心放在一個具體類上。
二、工廠方法模式之因此有一個別名叫多態性工廠模式,是由於具體工廠類都有共同的接口,或者有共同的抽象父類。
系統擴展須要添加新的產品對象時,僅僅須要添加一個具體對象以及一個具體工廠對象,原有工廠對象不須要任何修改,也不須要修改客戶端原有的代碼,很好的符合了「開放-封閉」原則。
而簡單工廠在添加新產品對象後,不得不修改工廠方法,擴展性很差。
工廠方法模式退化後能夠演變成簡單工廠模式。
JAVA的 API 裏使用了工廠方法模式的也不少不少,經常使用的好比,Object 類裏的 toString() 方法,Java 的任何類,均可以繼承或者覆寫toString 方法,Object 至關於抽象工廠類,各個Java 的類,至關於具體工廠類。
還有 util 包裏的Calendar類,該
類是一個抽象類,它爲特定瞬間與一組諸如 YEAR
、MONTH
、DAY_OF_MONTH
、HOUR
等日曆字段
之間的轉換提供了一些方法,併爲操做日曆字段(例如得到下星期的日期)提供了一些方法。
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> { public static Calendar getInstance() { return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT)); } }
該節選的代碼片斷有一個靜態的 getInstance() 方法,return 了一個具體的日曆對象,Calendar 是工廠方法模式裏的抽象工廠類,而具體工廠子類(簡單舉幾個例子)有JapaneseImperialCalendar,GregorianCalendar……
createCalendar 方法是 private 的,是屬於 getInstance 方法裏的,也就是說 Calendar 這個抽象工廠類返回具體子類的對象,JDK 裏這樣說:
與其餘語言環境敏感類同樣,
Calendar
提供了一個類方法getInstance
,以得到此類型的一個通用的對象。Calendar
的getInstance
方法返回一個Calendar
對象,其日曆字段已由當前日期和時間初始化……
客戶端經過該方法返回對應的日曆工廠,客戶端再經過這些日曆工廠去生成對應的日曆產品……未來若是須要支持某個其餘地區的特殊曆法,程序員除了必要的增長對應的日曆工廠並 extends Calendar 這個抽象工廠,且增長對應的日曆產品以外,只須要增長 Calendar 的 getInstance 方法的新的邏輯,但 Calendar 的使用者無需承擔這種變化的影響,符合OCP。