詳解Java設計模式之《工廠方法模式》

上一篇介紹簡單工廠模式 的時候提到它對開閉原則 支持的不夠,因為如果有新的產品加入到系統中去,就需要修改工廠類,就違反了開閉原則 了,這次介紹的工廠方法模式在保持簡單工廠模式優點的前提下,不僅滿足了開閉原則,關鍵在于它的多態性。

開閉原則(Open Close Principle):就是說**對擴展開放,對修改關閉 ** 。在程序需要進行擴展的時候,不能去修改原有的代碼,而是要擴展原有的代碼,實現一個熱插拔的效果。為了使程序的擴展性好,易于維護和升級。想要達到這樣的效果,我們需要使用接口和抽象類等。

概念

工廠方法模式(Factory Method Pattern):定義一個用于創建對象的接口,讓子類決定將哪一個類實例化。工廠方法模式讓一個類的實例化延遲到其子類。工廠方法模式又簡稱工廠模式(Factory Pattern),有可稱作虛擬構造器模式(Virtual Constructor Pattern)或多態工廠模式(Polymorphic Factory Pattern)。工廠方法模式是一種類創建型模式。

作用

在工廠方法模式中,我們不再提供一個統一的工廠類來創建所有的產品對象,而是針對不同的產品提供不同的工廠,系統提供一個與產品等級結構對應的工廠等級結構。

實現方式

工廠方法模式的結構圖如下:

從上圖可以看出,工廠方法模式涉及到抽象工廠角色、具體工廠角色、抽象產品角色以及具體產品角色等四個角色:

  • 抽象工廠角色:擔任這個角色是工廠方法模式的核心,它是與應用程序無關的。任何在模式中創建對象的工廠類必須實現這個接口。
  • 具體工廠角色:擔任這個角色的是實現了抽象工廠接口的具體Java類,具體工廠角色含有與應用密切相關的邏輯,并且受到應用程序的調用以創建產品對象。
  • 抽象產品角色:工廠方法模式所創建的對象的超類型,也就是產品對象的共同父類或共同擁有的接口。
  • 具體產品角色:這個角色實現了抽象產品角色所申明的接口。工廠方法模式所創建的每一個對象都是某個具體產品角色的實例。

結合披薩系統,就是之前廚師(工廠類)負責所有的烤披薩任務,太累了。于是招了兩個廚師分別負責烤GreekPizza 披薩和ChesePizza 披薩,之前的廚師升級為廚師長(抽象工廠類),負責教那兩位廚師(具體工廠類)烤披薩,自己則不用親自動手烤披薩了。

附上完整的類圖:

下面是抽象產品的角色Pizza的源代碼:

public abstract class Pizza{
    public abstract void prepare();
    public abstract void bake();
    public abstract void cut();
}

下面是具體角色CheesePizza的源代碼:

public class CheesePizza extends Pizza{
    public void prepare(){
        System.out.pringln("準備CheesePizza");
    }
    public void bake(){
        System.out.pringln("準備CheesePizza");
    }
    public void cut(){
        System.out.pringln("準備CheesePizza");
    }
    public void box(){
        System.out.pringln("準備CheesePizza");
    }
}

下面是具體角色GreekPizza的源代碼:

public class GreekPizza extends Pizza{
    public void prepare(){
        System.out.pringln("準備GreekPizza");
    }
    public void bake(){
        System.out.pringln("準備GreekPizza");
    }
    public void cut(){
        System.out.pringln("準備GreekPizza");
    }
    public void box(){
        System.out.pringln("準備GreekPizza");
    }
}

下面是抽象工廠角色PizzaFactory的代碼,這個角色是使用一個Java接口實現,它聲明了一個工廠方法,要求所有的具體工廠角色實現這個工廠方法:

public interface PizzaFactory{
    //工廠方法
    public Pizza createPizza();
}

下面是具體工廠角色CheesePizzaFactory的代碼,這個角色實現了抽象工廠角色PizzaFactory所聲明的工廠方法:

public class CheesePizzaFactory implements PizzaFactory{
    @Override
    public Pizza createPizza(){
        return new CheesePizza();
    }
}

下面是具體工廠角色GreekPizzaFactory的代碼,這個角色實現了抽象工廠角色PizzaFactory所聲明的工廠方法:

public class GreekPizzaFactory implements PizzaFactory{
    @Override
    public Pizza createPizza(){
        return new GreekPizza();
    }
}

下面是客戶端角色的源代碼:

public class OrderPizaa{
    public static void main(String[] args){
        //創建CheesePizzaFactory--具體工廠類
        //可以通過反射和配置文件來獲取和存儲具體工廠類的類名,更換新的具體工廠時無須修改源代碼,系統擴展更為方便
        PizzaFactory factory = new CheesePizzaFactory();
        Pizza pizza = factory.createPizza();
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        //創建GreekPizzaFactory()--具體工廠類
        pizza = factory.createPizza();
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
    }
}

結果演示:

準備CheesePizza
正在烤CheesePizza
正在切CheesePizza
正在打包CheesePizza
準備GreekPizza
正在烤GreekPizza
正在切GreekPizza
正在打包GreekPizza

這里使用工廠方法模式的要點:

工廠方法創建對象

  • 工廠方法不一定每一次都返回一個新的對象,但是它所返回的對象一定是它自己創建 的。

工廠方法返回的類型

  • 注意:工廠方法返回的應當是抽象類型 ,而不是具體類型,只有這樣才能保證針對產品的多態性。

工廠等級結構

  • 工廠對象應當有一個抽象的超類型。就是說,應當有數個具體工廠類作為一個抽象超類型的具體子類存在于工廠等級結構中。如果等級結構中只有一個具體工廠類的話,那么抽象工廠角色也可以省略,這時候,工廠方法模式就發生了退化,這一退化表現為針對工廠角色的多態性的喪失。

工廠方法模式的優缺點

優點:

  • 工廠方法模式跟簡單工廠模式在結構上不同是很明顯的,工廠方法模式的核心是一個抽象工廠類,而簡單工廠模式的核心在一個具體類。顯而易見,工廠方法模式這種結構更好擴展。
  • 如果系統需要加入一個新的產品,那么所需要的就是想系統中加入這個產品類以及它所對應的工廠類,沒有必要修改客戶端,也沒有必要修改抽象工廠角色或者其他已有的具體工廠角色。對于增加新的產品類而言,這個系統完全支持開閉原則。

應用場景

不管是簡單工廠模式,工廠方法模式還是抽象工廠模式,他們具有類似的特性,所以他們的適用場景也是類似的。

? 首先,作為一種創建類模式,在任何需要生成復雜對象的地方,都可以使用工廠方法模式。有一點需要注意的地方就是復雜對象適合使用工廠模式,而簡單對象,特別是只需要通過new就可以完成創建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的復雜度。

? 其次,工廠模式是一種典型的解耦模式,迪米特法則在工廠模式中表現的尤為明顯。假如調用者自己組裝產品需要增加依賴關系時,可以考慮使用工廠模式。將會大大降低對象之間的耦合度。

? 再次,由于工廠模式是依靠抽象架構的,它把實例化產品的任務交由實現類完成,擴展性比較好。也就是說,當需要系統有比較好的擴展性時,可以考慮工廠模式,不同的產品用不同的實現工廠來組裝。

迪米特法則(Law of Demeter):一個軟件實體應當盡可能少地與其他實體發生相互作用。

在將迪米特法則運用到系統設計中時,要注意下面的幾點:

  • 在類的劃分中,應當盡量創建松耦合的類,類之間的耦合度越低,就月有利于復用,一個處在松耦合的類一旦被修改,不會對關聯的類造成太大的波及。
  • 在類的結構設計上,每一個類都應當盡量降低其成員變量和成員函數的訪問權限。
  • 在類的設計上,只要有可能,一個類型應當設計成不變類。
  • 在對其他類的引用上,一個對象對其他對象的引用應當降到最低。

參考資料

嘟嘟獨立博客
劉偉

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容