一、工廠模式介紹
工廠模式專門負責將大量有共同接口的類實例化焕阿。工廠模式可以動態(tài)決定將哪一個類實例化丰嘉,不必事先知道每次要實例化哪一個類履羞。
工廠模式的幾種形態(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ù)自變量的不同返回不同類的實例。簡單工廠模式專門定義一個類來負責創(chuàng)建其他類的實例旁振,被創(chuàng)建的實例通常都具有共同的父類获询。
2.2簡單工廠模式角色
(1)工廠類(Creator)角色:擔任這個角色的是工廠方法模式的核心,含有與應(yīng)用緊密相關(guān)的商業(yè)邏輯拐袜。工廠類在客戶端的直接調(diào)用下創(chuàng)建產(chǎn)品對象吉嚣,它往往由一個具體Java 類實現(xiàn)。
(2)抽象產(chǎn)品(Product)角色:擔任這個角色的類是工廠方法模式所創(chuàng)建的對象的父類蹬铺,或它們共同擁有的接口尝哆。抽象產(chǎn)品角色可以用一個Java 接口或者Java 抽象類實現(xiàn)。
(3)具體產(chǎn)品(Concrete Product)角色:工廠方法模式所創(chuàng)建的任何對象都是這個角色的實例甜攀,具體產(chǎn)品角色由一個具體Java 類實現(xiàn)秋泄。
2.3簡單工廠模式的優(yōu)缺點
簡單工廠模式的優(yōu)點如下:
(1)工廠類含有必要的判斷邏輯琐馆,可以決定在什么時候創(chuàng)建哪一個產(chǎn)品類的實例,客戶端可以免除直接創(chuàng)建產(chǎn)品對象的責任恒序,而僅僅“消費”產(chǎn)品瘦麸;簡單工廠模式通過這種做法實現(xiàn)了對責任的分割,它提供了專門的工廠類用于創(chuàng)建對象歧胁。
(2)客戶端無需知道所創(chuàng)建的具體產(chǎn)品類的類名,只需要知道具體產(chǎn)品類所對應(yīng)的參數(shù)即可,對于一些復(fù)雜的類名籍凝,通過簡單工廠模式可以減少使用者的記憶量骡和。
(3)通過引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產(chǎn)品類崭参,在一定程度上提高了系統(tǒng)的靈活性呵曹。
簡單工廠模式的缺點如下:
(1)由于工廠類集中了所有產(chǎn)品創(chuàng)建邏輯,一旦不能正常工作阵翎,整個系統(tǒng)都要受到影響逢并。
(2)使用簡單工廠模式將會增加系統(tǒng)中類的個數(shù),在一定程序上增加了系統(tǒng)的復(fù)雜度和理解難度郭卫。
(3)系統(tǒng)擴展困難砍聊,一旦添加新產(chǎn)品就不得不修改工廠邏輯,在產(chǎn)品類型較多時贰军,有可能造成工廠邏輯過于復(fù)雜玻蝌,不利于系統(tǒng)的擴展和維護。
(4)簡單工廠模式由于使用了靜態(tài)工廠方法词疼,造成工廠角色無法形成基于繼承的等級結(jié)構(gòu)俯树。
2.4簡單工廠模式的適用環(huán)境
(1)工廠類負責創(chuàng)建的對象比較少:由于創(chuàng)建的對象較少,不會造成工廠方法中的業(yè)務(wù)邏輯太過復(fù)雜贰盗;
(2)客戶端只知道傳入工廠類的參數(shù)许饿,對于如何創(chuàng)建對象不關(guān)心:客戶端既不需要關(guān)心創(chuàng)建細節(jié),甚至連類名都不需要記住舵盈,只需要知道類型所對應(yīng)的參數(shù)陋率。
2.5簡單工廠模式的舉例
假設(shè)一家工廠,生產(chǎn)電視秽晚,汽車等等瓦糟,我們先為所有產(chǎn)品定義一個共同的產(chǎn)品接口
public interface Product {
}
接著我們讓這個工廠的所有產(chǎn)品都必須實現(xiàn)此接口
public class Tv implements Product {
public Tv(){
System.out.println("電視被制造了");
}
}
public class Car implements Product {
public Car(){
System.out.println("汽車被制造了");
}
}
下面有幾種模式來實現(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é)果:
汽車被制造了
這樣的實現(xiàn)有個問題,如果我們新增產(chǎn)品類的話赴蝇,需要不斷的在工廠類中新增case菩浙,這樣需要修改的地方比較多,所以不建議使用這樣的方法來實現(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é)果:
電視被制造了
這種的缺點在于劲蜻,每次創(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é)果:
電視被制造了
當然這個方法也有可以改進的地方,比如將map在程序啟動時就加載拗小,這樣就不必要每次調(diào)用的時候重罪,都去解析配置文件了,節(jié)省了一批開銷哀九。
三剿配、工廠方法模式
3.1工廠方法模式的介紹
工廠方法模式定義一個用于創(chuàng)建對象的接口,讓子類決定實例化哪一個類阅束。Factory Method是一個類的實例化延遲到其子類呼胚。
在工廠方法模式中,核心的工廠類不再負責所有的產(chǎn)品的創(chuàng)建息裸,而是將具體創(chuàng)建的工作交給子類去做蝇更。這個核心類則搖身一變,成為了一個抽象工廠角色呼盆,僅負責給出具體工廠子類必須實現(xiàn)的接口年扩,而不接觸哪一個產(chǎn)品類應(yīng)當被實例化這種細節(jié)。
3.2工廠方法模式角色
(1)抽象工廠(Creator)角色:擔任這個角色的是工廠方法模式的核心访圃,它是與應(yīng)用程序無關(guān)的厨幻。任何在模式中創(chuàng)建對象的工廠類必須實現(xiàn)這個接口。在上面的系統(tǒng)中這個角色由Java 接口Creator 扮演腿时;在實際的系統(tǒng)中克胳,這個角色也常常使用抽象Java 類實現(xiàn)。
(2)具體工廠(Concrete Creator)角色:擔任這個角色的是實現(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 扮演;在實際的系統(tǒng)中叹括,這個角色也常常使用抽象Java 類實現(xiàn)算墨。
(4)具體產(chǎn)品(Concrete Product)角色:這個角色實現(xiàn)了抽象產(chǎn)品角色所聲明的接口。工廠方法模式所創(chuàng)建的每一個對象都是某個具體產(chǎn)品角色的實例汁雷。
3.3工廠方法模式的優(yōu)缺點
工廠方法模式的優(yōu)點如下:
(1)在工廠方法模式中净嘀,工廠方法用來創(chuàng)建客戶所需要的產(chǎn)品,同時還向客戶隱藏了哪種具體產(chǎn)品類將被實例化這一細節(jié)侠讯,用戶只需要關(guān)心所需產(chǎn)品對應(yīng)的工廠挖藏,無需關(guān)心創(chuàng)建細節(jié),甚至無需知道具體產(chǎn)品類的類名厢漩。
(2)基于工廠角色和產(chǎn)品角色的多態(tài)性設(shè)計是工廠方法模式的關(guān)鍵膜眠。它能夠使工廠可以自主確定創(chuàng)建何種產(chǎn)品對象,而如何創(chuàng)建這個對象的細節(jié)則完全封裝在具體工廠內(nèi)部溜嗜。工廠方法模式之所以又被稱為多態(tài)工廠模式宵膨,正是因為所有的具體工廠類都具有同一抽象父類。
(3)使用工廠方法模式的另一個優(yōu)點是在系統(tǒng)中加入新產(chǎn)品時炸宵,無需修改抽象工廠和抽象產(chǎn)品提供的接口辟躏,無需修改客戶端,也無需修改其他的具體工廠和具體產(chǎn)品焙压,而只要添加一個具體工廠和具體產(chǎn)品就可以了鸿脓,這樣,系統(tǒng)的可擴展性也就變得非常好涯曲,完全符合“開閉原則”野哭。
工廠方法模式的缺點如下:
(1)在添加新產(chǎn)品時,需要編寫新的具體產(chǎn)品類幻件,而且還要提供與之對應(yīng)的具體工廠類拨黔,系統(tǒng)中類的個數(shù)將成對增加,在一定程度上增加了系統(tǒng)的復(fù)雜度绰沥,有更多的類需要編譯和運行篱蝇,會給系統(tǒng)帶來一些額外的開銷。
(2)由于考慮到系統(tǒng)的可擴展性徽曲,需要引入抽象層零截,在客戶端代碼中均使用抽象層進行定義,增加了系統(tǒng)的抽象性和理解難度秃臣,且在實現(xiàn)時可能需要用到DOM涧衙、反射等技術(shù)哪工,增加了系統(tǒng)的實現(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)性和里氏代換原則,在程序運行時两踏,子類對象將覆蓋父類對象败京,從而使得系統(tǒng)更容易擴展。
(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{}
以下是實現(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 {
@Override
public Product produce() {
return new Tv();
}
}
public class CarFactory implements Factory {
@Override
public Product produce() {
return new Car();
}
}
四伪货、抽象工廠模式
4.1抽象工廠模式的介紹
抽象工廠模式提供一個創(chuàng)建一系列或相互依賴的對象的接口们衙,而無需指定它們具體的類。
4.2抽象工廠模式角色
抽象工廠模式涉及到的系統(tǒng)角色
(1)抽象工廠(AbstractFactory)角色:擔任這個角色的是工廠方法模式的核心碱呼,它是與應(yīng)用系統(tǒng)的商業(yè)邏輯無關(guān)的蒙挑。通常使用Java 接口或者抽象Java 類實現(xiàn),而所有的具體工廠類必須實現(xiàn)這個Java 接口或繼承這個抽象Java 類愚臀。
(2)具體工廠類(Conrete Factory)角色:這個角色直接在客戶端的調(diào)用下創(chuàng)建產(chǎn)品的實例忆蚀。這個角色含有選擇合適的產(chǎn)品對象的邏輯,而這個邏輯是與應(yīng)用系統(tǒng)的商業(yè)邏輯緊密相關(guān)的姑裂。通常使用具體Java 類實現(xiàn)這個角色馋袜。
(3)抽象產(chǎn)品(Abstract Product)角色:擔任這個角色的類是工廠方法模式所創(chuàng)建的對象的父類,或它們共同擁有的接口舶斧。通常使用Java 接口或者抽象Java 類實現(xiàn)這一角色欣鳖。
(4)具體產(chǎn)品(Concrete Product)角色:抽象工廠模式所創(chuàng)建的任何產(chǎn)品對象都是某一個具體產(chǎn)品類的實例。這是客戶端最終需要的東西茴厉,其內(nèi)部一定充滿了應(yīng)用系統(tǒng)的商業(yè)邏輯观堂。通常使用具體Java 類實現(xiàn)這個角色让网。
4.3抽象工廠模式的優(yōu)缺點
優(yōu)點:
(1) 隔離了具體類的生成,使得用戶不需要知道什么被創(chuàng)建了师痕。
(2) 當一個產(chǎn)品族中的多個對象被設(shè)計成一起工作時,它能夠保證客戶端始終只使用同一個產(chǎn)品族中的對象而账。
缺點:
(1)添加新的產(chǎn)品對像時胰坟,難以擴展抽象工廠以便生產(chǎn)新種類的產(chǎn)品。
4.4抽象工廠模式的適用環(huán)境
(1)一個系統(tǒng)不應(yīng)當依賴于產(chǎn)品類實例如何被創(chuàng)建泞辐、組合和表達的細節(jié)笔横。這對于所有形態(tài)的工廠模式都是重要的;
(2)一個系統(tǒng)的產(chǎn)品有多于一個的產(chǎn)品族咐吼,而系統(tǒng)只消費其中某一族的產(chǎn)品吹缔;
(3)同屬于同一個產(chǎn)品族的產(chǎn)品是在一起使用的,這一約束必須要在系統(tǒng)的設(shè)計中體現(xiàn)出來锯茄;
(4)系統(tǒng)提供一個產(chǎn)品類的庫厢塘,所有的產(chǎn)品以同樣的接口出現(xiàn),從而使客戶端不依賴于實現(xiàn)肌幽。
4.5舉例
將汽車和電視定義兩個接口晚碾,對他們進行分類
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("索尼電視機被生產(chǎn)出來了");
}
}
接下來定義工廠行為接口
public interface Factory {
public Tv produceTv();
public Car produceCar();
}
具體工廠類:
public class FactoryA implements Factory {
@Override
public Tv produceTv() {
return new LeTv();
}
@Override
public Car produceCar() {
return new BMW();
}
}
public class FactoryB implements Factory {
@Override
public Tv produceTv() {
return new Sony();
}
@Override
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();
}
}