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