java中的工厂模式
一、工廠模式介紹
工廠模式專門負責將大量有共同接口的類實例化。工廠模式可以動態決定將哪一個類實例化,不必事先知道每次要實例化哪一個類。
工廠模式的幾種形態:
(1)簡單工廠(Simple Factory)模式,又稱靜態工廠方法模式(Static Factory Method Pattern)。
(2)工廠方法(Factory Method)模式,又稱多態性工廠(Polymorphic Factory)模式或虛擬構造子(Virtual Constructor)模式;
(3)抽象工廠(Abstract Factory)模式,又稱工具箱(Kit 或Toolkit)模式。
二、簡單工廠模式
2.1簡單工廠模式介紹
簡單工廠模式(Simple Factory Pattern):又稱為靜態工廠方法(Static Factory Method)模式,它屬于類創建型模式。在簡單工廠模式中,可以根據自變量的不同返回不同類的實例。簡單工廠模式專門定義一個類來負責創建其他類的實例,被創建的實例通常都具有共同的父類。
2.2簡單工廠模式角色
(1)工廠類(Creator)角色:擔任這個角色的是工廠方法模式的核心,含有與應用緊密相關的商業邏輯。工廠類在客戶端的直接調用下創建產品對象,它往往由一個具體Java 類實現。
(2)抽象產品(Product)角色:擔任這個角色的類是工廠方法模式所創建的對象的父類,或它們共同擁有的接口。抽象產品角色可以用一個Java 接口或者Java 抽象類實現。
(3)具體產品(Concrete Product)角色:工廠方法模式所創建的任何對象都是這個角色的實例,具體產品角色由一個具體Java 類實現。
2.3簡單工廠模式的優缺點
簡單工廠模式的優點如下:
(1)工廠類含有必要的判斷邏輯,可以決定在什么時候創建哪一個產品類的實例,客戶端可以免除直接創建產品對象的責任,而僅僅“消費”產品;簡單工廠模式通過這種做法實現了對責任的分割,它提供了專門的工廠類用于創建對象。
(2)客戶端無需知道所創建的具體產品類的類名,只需要知道具體產品類所對應的參數即可,對于一些復雜的類名,通過簡單工廠模式可以減少使用者的記憶量。
(3)通過引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產品類,在一定程度上提高了系統的靈活性。
簡單工廠模式的缺點如下:
(1)由于工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都要受到影響。
(2)使用簡單工廠模式將會增加系統中類的個數,在一定程序上增加了系統的復雜度和理解難度。
(3)系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,在產品類型較多時,有可能造成工廠邏輯過于復雜,不利于系統的擴展和維護。
(4)簡單工廠模式由于使用了靜態工廠方法,造成工廠角色無法形成基于繼承的等級結構。
2.4簡單工廠模式的適用環境
(1)工廠類負責創建的對象比較少:由于創建的對象較少,不會造成工廠方法中的業務邏輯太過復雜;
(2)客戶端只知道傳入工廠類的參數,對于如何創建對象不關心:客戶端既不需要關心創建細節,甚至連類名都不需要記住,只需要知道類型所對應的參數。
2.5簡單工廠模式的舉例
假設一家工廠,生產電視,汽車等等,我們先為所有產品定義一個共同的產品接口
public interface Product { }接著我們讓這個工廠的所有產品都必須實現此接口
public class Tv implements Product {public Tv(){System.out.println("電視被制造了");} }public class Car implements Product {public Car(){System.out.println("汽車被制造了");} }下面有幾種模式來實現這個工廠:
2.5.1 直接判斷傳入的key
public class ProductFactory {public static Product produce(String productName) throws Exception {switch (productName) {case "tv":return new Tv();case "car":return new Car();default:throw new Exception("沒有該產品");}} }測試方法:
try {ProductFactory.produce("car"); } catch (Exception e) {e.printStackTrace(); }返回結果:
汽車被制造了這樣的實現有個問題,如果我們新增產品類的話,需要不斷的在工廠類中新增case,這樣需要修改的地方比較多,所以不建議使用這樣的方法來實現工廠類。
2.5.2 利用反射
public class ProductFactory2 {public static Product produce(String className) throws Exception {try {Product product = (Product) Class.forName(className).newInstance();return product;} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}throw new Exception("沒有該產品");} }測試方法:
try {ProductFactory2.produce("com.zhaofeng.factory.simple.Tv"); } catch (Exception e) {e.printStackTrace(); }返回結果:
電視被制造了這種的缺點在于,每次創建一個產品時,需要傳入產品的全部類路徑,也就是要記住一個產品的全部路徑,比較麻煩。我們想到可以通過配置文件,來將類路徑全部寫在properties文件中,通過加載配置文件,這樣如果以后新增的話,直接修改配置文件即可。
2.5.3 反射加配置文件
新增配置文件product.properties
tv=com.zhaofeng.factory.simple.Tv car=com.zhaofeng.factory.simple.Car新增配置文件讀取類,將讀出來的內容存儲到一個map中
public class PropertyReader {public static Map<String, String> map = new HashMap<>();public Map<String, String> readPropertyFile(String fileName) {Properties pro = new Properties();InputStream in = getClass().getResourceAsStream(fileName);try {pro.load(in);Iterator<String> iterator = pro.stringPropertyNames().iterator();while (iterator.hasNext()) {String key = iterator.next();String value = pro.getProperty(key);map.put(key, value);}in.close();} catch (IOException e) {e.printStackTrace();}return map;} }全新的工廠類如下:
public class ProductFactory3 {public static Product produce(String key) throws Exception {PropertyReader reader = new PropertyReader();Map<String, String> map = reader.readPropertyFile("product.properties");try {Product product = (Product) Class.forName(map.get(key)).newInstance();return product;} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}throw new Exception("沒有該產品");} }測試類:
try {ProductFactory3.produce("tv"); } catch (Exception e) {e.printStackTrace(); }返回結果:
電視被制造了當然這個方法也有可以改進的地方,比如將map在程序啟動時就加載,這樣就不必要每次調用的時候,都去解析配置文件了,節省了一批開銷。
三、工廠方法模式
3.1工廠方法模式的介紹
工廠方法模式定義一個用于創建對象的接口,讓子類決定實例化哪一個類。Factory Method是一個類的實例化延遲到其子類。
在工廠方法模式中,核心的工廠類不再負責所有的產品的創建,而是將具體創建的工作交給子類去做。這個核心類則搖身一變,成為了一個抽象工廠角色,僅負責給出具體工廠子類必須實現的接口,而不接觸哪一個產品類應當被實例化這種細節。
3.2工廠方法模式角色
(1)抽象工廠(Creator)角色:擔任這個角色的是工廠方法模式的核心,它是與應用程序無關的。任何在模式中創建對象的工廠類必須實現這個接口。在上面的系統中這個角色由Java 接口Creator 扮演;在實際的系統中,這個角色也常常使用抽象Java 類實現。
(2)具體工廠(Concrete Creator)角色:擔任這個角色的是實現了抽象工廠接口的具體Java 類。具體工廠角色含有與應用密切相關的邏輯,并且受到應用程序的調用以創建產品對象。在本系統中給出了兩個這樣的角色,也就是具體Java 類ConcreteCreator1 和ConcreteCreator2。
(3)抽象產品(Product)角色:工廠方法模式所創建的對象的超類型,也就是產品對象的共同父類或共同擁有的接口。在本系統中,這個角色由Java 接口Product 扮演;在實際的系統中,這個角色也常常使用抽象Java 類實現。
(4)具體產品(Concrete Product)角色:這個角色實現了抽象產品角色所聲明的接口。工廠方法模式所創建的每一個對象都是某個具體產品角色的實例。
3.3工廠方法模式的優缺點
工廠方法模式的優點如下:
(1)在工廠方法模式中,工廠方法用來創建客戶所需要的產品,同時還向客戶隱藏了哪種具體產品類將被實例化這一細節,用戶只需要關心所需產品對應的工廠,無需關心創建細節,甚至無需知道具體產品類的類名。
(2)基于工廠角色和產品角色的多態性設計是工廠方法模式的關鍵。它能夠使工廠可以自主確定創建何種產品對象,而如何創建這個對象的細節則完全封裝在具體工廠內部。工廠方法模式之所以又被稱為多態工廠模式,正是因為所有的具體工廠類都具有同一抽象父類。
(3)使用工廠方法模式的另一個優點是在系統中加入新產品時,無需修改抽象工廠和抽象產品提供的接口,無需修改客戶端,也無需修改其他的具體工廠和具體產品,而只要添加一個具體工廠和具體產品就可以了,這樣,系統的可擴展性也就變得非常好,完全符合“開閉原則”。
工廠方法模式的缺點如下:
(1)在添加新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的復雜度,有更多的類需要編譯和運行,會給系統帶來一些額外的開銷。
(2)由于考慮到系統的可擴展性,需要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度,且在實現時可能需要用到DOM、反射等技術,增加了系統的實現難度。
3.4工廠方法模式的適用環境
在以下情況下可以使用工廠方法模式:
(1)一個類不知道它所需要的對象的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品對象由具體工廠類創建;客戶端需要知道創建具體產品的工廠類。
(2)一個類通過其子類來指定創建哪個對象:在工廠方法模式中,對于抽象工廠類只需要提供一個創建產品的接口,而由其子類來確定具體要創建的對象,利用面向對象的多態性和里氏代換原則,在程序運行時,子類對象將覆蓋父類對象,從而使得系統更容易擴展。
(3)將創建對象的任務委托給多個工廠子類中的某一個,客戶端在使用時可以無需關心是哪一個工廠子類創建產品子類,需要時再動態指定,可將具體工廠類的類名存儲在配置文件或數據庫中。
3.5舉例
工廠方法為工廠類定義了接口,用多態來削弱了工廠類的職能,以下是工廠接口的定義:
public interface Factory {public Product produce(); }我們再來定義一個產品接口
public interface Product{}以下是實現了產品接口的產品類:
public class Tv implements Product {public Tv() {System.out.println("電視被制造了");} }public class Car implements Product {public Car(){System.out.println("汽車被制造了");} }接下來,就是工廠方法的核心部分,也就是具體創建產品對象的具體工廠類,
public class TvFactory implements Factory {public Product produce() {return new Tv();} }public class CarFactory implements Factory {public Product produce() {return new Car();} }四、抽象工廠模式
4.1抽象工廠模式的介紹
抽象工廠模式提供一個創建一系列或相互依賴的對象的接口,而無需指定它們具體的類。
4.2抽象工廠模式角色
抽象工廠模式涉及到的系統角色
(1)抽象工廠(AbstractFactory)角色:擔任這個角色的是工廠方法模式的核心,它是與應用系統的商業邏輯無關的。通常使用Java 接口或者抽象Java 類實現,而所有的具體工廠類必須實現這個Java 接口或繼承這個抽象Java 類。
(2)具體工廠類(Conrete Factory)角色:這個角色直接在客戶端的調用下創建產品的實例。這個角色含有選擇合適的產品對象的邏輯,而這個邏輯是與應用系統的商業邏輯緊密相關的。通常使用具體Java 類實現這個角色。
(3)抽象產品(Abstract Product)角色:擔任這個角色的類是工廠方法模式所創建的對象的父類,或它們共同擁有的接口。通常使用Java 接口或者抽象Java 類實現這一角色。
(4)具體產品(Concrete Product)角色:抽象工廠模式所創建的任何產品對象都是某一個具體產品類的實例。這是客戶端最終需要的東西,其內部一定充滿了應用系統的商業邏輯。通常使用具體Java 類實現這個角色。
4.3抽象工廠模式的優缺點
優點:
(1) 隔離了具體類的生成,使得用戶不需要知道什么被創建了。
(2) 當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象。
缺點:
(1)添加新的產品對像時,難以擴展抽象工廠以便生產新種類的產品。
4.4抽象工廠模式的適用環境
(1)一個系統不應當依賴于產品類實例如何被創建、組合和表達的細節。這對于所有形態的工廠模式都是重要的;
(2)一個系統的產品有多于一個的產品族,而系統只消費其中某一族的產品;
(3)同屬于同一個產品族的產品是在一起使用的,這一約束必須要在系統的設計中體現出來;
(4)系統提供一個產品類的庫,所有的產品以同樣的接口出現,從而使客戶端不依賴于實現。
4.5舉例
將汽車和電視定義兩個接口,對他們進行分類
public interface Car { }public interface Tv { }創建汽車和電視的具體產品
public class Audi implements Car {public Audi(){System.out.println("奧迪車生產出來了");} }public class BMW implements Car {public BMW(){System.out.println("一輛寶馬生產出來了");} }public class LeTv implements Tv {public LeTv() {System.out.println("樂視電視被生產出來了");} }public class Sony implements Tv {public Sony(){System.out.println("索尼電視機被生產出來了");} }接下來定義工廠行為接口
public interface Factory {public Tv produceTv();public Car produceCar(); }具體工廠類:
public class FactoryA implements Factory {public Tv produceTv() {return new LeTv();}public Car produceCar() {return new BMW();} }public class FactoryB implements Factory {public Tv produceTv() {return new Sony();}public Car produceCar() {return new Audi();} }測試類:
public class Test {public static void main(String[] args) {FactoryA factoryA = new FactoryA();factoryA.produceCar();FactoryB factoryB = new FactoryB();factoryB.produceTv();} }作者:端木軒
鏈接:https://www.jianshu.com/p/bf8341c75304
來源:簡書
總結
以上是生活随笔為你收集整理的java中的工厂模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈java枚举类
- 下一篇: 论面向组合子程序设计方法 之 创世纪