什麼是工廠模式?
定義: 工廠模式又稱爲創建模式,它是建對象的一種最佳方式。工廠模式的本質就是用工廠方法代替new操作創建一種實例化對象的方式。一句話中總結就是方便創建 同種類型接口產品 的 複雜對象。
核心:
- 實現了創建者和調用者分離
- 實例對象不用new,而用工廠方法代替
- 將選擇實現類,創建對象統一管理和控制。從而將調用者和我們的實現類解耦
三種模式:
- 簡單工廠模式:用來生產同一等級結構的任意商品(對於增加新的商品,需要修改已有代碼)
- 工廠方法模式:用來生產同一等級結構中固定商品(支持增加任意產品)
- 抽象工廠模式:圍繞一個超級工廠創建其他工廠,該超級工廠又城其他工廠的工廠。
應用實例: 1、您需要一輛汽車,可以直接從工廠裏面提貨,而不用去管這輛汽車是怎麼做出來的,以及這個汽車裏面的具體實現。 2、Hibernate 換數據庫只需換方言和驅動就可以。
優點: 1、一個調用者想創建一個對象,只要知道其名稱就可以了。 2、擴展性高,如果想增加一個產品,只要擴展一個工廠類就可以。 3、屏蔽產品的具體實現,調用者只關心產品的接口。
缺點: 每次增加一個產品時,都需要增加一個具體類和對象實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事。
使用場景: 1、日誌記錄器:記錄可能記錄到本地硬盤、系統事件、遠程服務器等,用戶可以選擇記錄日誌到什麼地方。 2、數據庫訪問,當用戶不知道最後系統採用哪一類數據庫,以及數據庫可能有變化時。 3、設計一個連接服務器的框架,需要三個協議,“POP3”、“IMAP”、“HTTP”,可以把這三個作爲產品類,共同實現一個接口。
注意事項: 作爲一種創建類模式,在任何需要生成複雜對象的地方,都可以使用工廠方法模式。有一點需要注意的地方就是複雜對象適合使用工廠模式,而簡單對象,特別是只需要通過 new 就可以完成創建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的複雜度。
簡單工廠模式
在介紹簡單工程模式前,我們現需要回顧一下OOP的七大原則,因爲工廠模式也是需要符合這七大原則的。
OOP七大原則:
- 開閉原則:一個軟件的實體應該對擴展開放,對修改關閉
- 依賴倒轉原則:要針對接口編程,不要針對實現編程
- 迪米特法則:直與你直接的朋友通信,避免和陌生人通信
然後我們寫一個車的接口
public interface Car {
void Name();
}
再寫兩個車的實現類
public class Wuling implements Car{
@Override
public void Name() {
System.out.println("五菱宏光!");
}
}
public class TesLa implements Car{
@Override
public void Name() {
System.out.println("特斯拉!");
}
}
再過去我們想要得到Wuling和Tesla的對象,最常用的方法就是直接new。
//消費者(測試類)
public class Consumer {
public static void main(String[] args) {
Car car1=new Wuling();
Car car2=new TesLa();
car1.Name();car2.Name();
}
}
但是上面這種操作,我們需要知道接口和所有的實現類才能得到對象。就好像你想要一臺車,然後你自己造(new)了一輛車,但是現實生活中,如果你想要的一臺車,你會怎麼做?去工廠買,你不需要知道工廠怎麼造車。
工廠模式的核心就是實例對象不用new,而用工廠方法代替,我們用代碼實現一個車工廠
public class CarFactory {
public static Car getCar(String car){
if (car.equals("五菱宏光"))
return new Wuling();
else if (car.equals("特斯拉"))
return new TesLa();
else
return null;
}
}
這樣我們的消費者需要車,直接去工廠買就可以了,不需要自己造了。需要什麼車(Car),直接給車工廠(CarFactory)傳一個字符串參數就可以了,不需要知道里面的細節,甚至不需要知道實現類(符合OOP七大原則之依賴倒轉原則:要針對接口編程,不要針對實現編程)。
//用工廠實現
Car car1 = CarFactory.getCar("五菱宏光");
Car car2 = CarFactory.getCar("特斯拉");
問題來了,如果我們需要增加一個實現類呢?
public class DaZhong implements Car{
@Override
public void Name() {
System.out.println("大衆!");
}
}
我們需要修改我們的車工廠(CarFactory),在裏面加一個判斷
public class CarFactory {
public static Car getCar(String car){
if (car.equals("五菱宏光"))
return new Wuling();
else if (car.equals("特斯拉"))
return new TesLa();
else if (car.equals("大衆"))
return new DaZhong();
else
return null;
}
}
這樣就違反了OOP七大原則(開閉原則:一個軟件的實體應該對擴展開放,對修改關閉),所以簡單工廠模式是有一點小問題的,不能動態的增加實現類,無法在不修改車工廠代碼的前提下增加實現類,而且簡單工廠裏面車工廠的方法都是靜態的,因此簡單工廠模式又叫靜態工廠模式。
但是事實上,看源碼可知,我們使用的最多的還是簡單工廠模式,因爲如果想做滿足開閉原則,你將會付出很大代價,代碼量將會增大很多,就比如下面的 工廠方法模式。
工廠方法模式
工廠方法模式是思路很簡單,就是再寫一個車工廠的接口。
public interface CarFactory {
Car getCar();
}
讓每個車有自己的工廠,每個工廠再實現車工廠的接口。
//五菱車工廠
public class WulingFactory implements CarFactory{
@Override
public Car getCar() {
return new Wuling();
}
}
//特斯拉車工廠
public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new TesLa();
}
}
消費者如果想要車,只需去對應的車工廠去買車就可以了。要五菱去五菱車工廠,要特斯拉去特斯拉車工廠。
Car car1 = new WulingFactory().getCar();
Car car2 = new TeslaFactory().getCar();
這樣的話,即使再增加實現類,也只需要繼承車工廠的接口寫一個實現類即可,不需要修改車工廠的代碼了。和簡單工廠模式相比,簡單工廠模式只有一個車工廠類,所有的車都是在那裏面生成。工廠方法模式有多個車工廠,每個車都有自己的車工廠,但是所有的車工廠又都實現車工廠的接口,可以實現動態的增加車的實現類。
比如我們增加一個摩拜單車
public class Mobai implements Car{
@Override
public void Name() {
System.out.println("摩拜單車!");
}
}
只需要增加摩拜單車的車工廠,完全不需要改動其他類的代碼。
public class MobaiFactory implements CarFactory{
@Override
public Car getCar() {
return new Mobai();
}
}
消費者想要摩拜只需要去摩拜車工廠去買即可。
Car car3 = new MobaiFactory().getCar();
既然工廠方法模式可以動態的增加實現類還符合開閉原則,那爲什麼在源碼中簡單工程模式使用的更多呢?
簡單工廠模式和工廠方法模式進行對比
- 結構複雜度上,simple(簡單工廠模式)更簡單
- 代碼複雜度上,simple(簡單工廠模式)更簡單
- 編程複雜度上,simple(簡單工廠模式)更簡單
- 管理複雜度上,simple(簡單工廠模式)更簡單
所以根據設計原則我們應該選擇工廠方法模式,但是在實際開發中,簡單工程模式使用更多。(當然不是想偷懶)
抽象工廠模式
抽象工廠模式提供了一個 創建一系列相關或相互依賴對象的接口,無需指定他們具體的類。
在介紹抽象工廠之前,我們需要先清除一個概念,產品族和產品等級。就拿小米和華爲兩家公司生產的手機和路由器來舉例:小米手機和小米路由器都是小米的產品,屬於同一個產品族,而小米手機和華爲手機都是手機,屬於同一個產品等級。
我們先定義手機和路由器的接口
//手機產品接口
public interface IphoneProduct {
void start();//開機
void shutdown();//關機
void callup();//打電話
void sendSMS();//發信息
}
//路由器產品接口
public interface IRouterProduct {
void start();//開機
void shutdown();//關機
void openWifi();//打開WiFi
void setting();//設置
}
因爲小米和華爲都有自己的手機和路由器,所以需要分別實現他們的實現類。
//小米手機
public class XiaomiIphone implements IphoneProduct{
@Override
public void start() {
System.out.println("開啓小米手機");
}
@Override
public void shutdown() {
System.out.println("關閉小米手機");
}
@Override
public void callup() {
System.out.println("用小米手機打電話");
}
@Override
public void sendSMS() {
System.out.println("用小米手機發信息");
}
}
//華爲手機
public class HuaweiIphone implements IphoneProduct{
@Override
public void start() {
System.out.println("開啓華爲手機");
}
@Override
public void shutdown() {
System.out.println("關閉華爲手機");
}
@Override
public void callup() {
System.out.println("用華爲手機打電話");
}
@Override
public void sendSMS() {
System.out.println("用華爲手機發信息");
}
}
//小米路由器
public class XiaomiIRouter implements IRouterProduct{
@Override
public void start() {
System.out.println("開啓小米路由器");
}
@Override
public void shutdown() {
System.out.println("關閉小米路由器");
}
@Override
public void openWifi() {
System.out.println("打開小米WiFi");
}
@Override
public void setting() {
System.out.println("打開小米設置");
}
}
//華爲路由器
public class HuaweiIRouter implements IRouterProduct{
@Override
public void start() {
System.out.println("開啓華爲路由器");
}
@Override
public void shutdown() {
System.out.println("關閉華爲路由器");
}
@Override
public void openWifi() {
System.out.println("打開華爲WiFi");
}
@Override
public void setting() {
System.out.println("打開華爲設置");
}
}
現在產品都已經有了,我們需要去寫製造他們的工廠,小米工廠和華爲工廠。無論是小米還是華爲,工廠都是要生產手機和路由器的,所以我們可以先寫一個抽象工廠接口,小米和華爲都去繼承這個抽象工廠,不過小米生產小米的手機和路由器,華爲生產華爲的手機和路由器。
//抽象工廠
public interface IProductFactory {
//生產手機
IphoneProduct iphoneproduct();
//生產路由器
IRouterProduct irouterproduct();
}
//華爲工廠
public class HuaweiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneproduct() {
return new HuaweiIphone();//生產華爲手機
}
@Override
public IRouterProduct irouterproduct() {
return new HuaweiIRouter();//生產華爲路由器
}
}
//小米工廠
public class XiaomiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneproduct() {
return new XiaomiIphone();//生產小米手機
}
@Override
public IRouterProduct irouterproduct() {
return new XiaomiIRouter();//生產小米路由器
}
}
做完這些後,我們如果想要小米的產品就去小米工廠製造,如果想要華爲的產品就去華爲工廠製造。
public static void main(String[] args) {
System.out.println("======================小米系列產品======================");
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct Xiaomiiphone = xiaomiFactory.iphoneproduct();
IRouterProduct Xiaomiirouter = xiaomiFactory.irouterproduct();
Xiaomiiphone.callup();Xiaomiirouter.openWifi();
System.out.println("======================華爲系列產品======================");
HuaweiFactory huaweiFactory = new HuaweiFactory();
IphoneProduct Huaweiiphone = huaweiFactory.iphoneproduct();
IRouterProduct Huaweiirouter = huaweiFactory.irouterproduct();
Huaweiiphone.callup();Huaweiirouter.openWifi();
}
最後的所有類的結構圖就是這個樣子,IProductFactory就是抽象工廠,圍繞着抽象工廠建造其他工廠,所以抽象工廠又稱爲工廠的工廠。
如果華爲和小米的產品族裏面還有筆記本,那我們就需要添加一個產品接口,再修改抽象工廠和實現的類,加入筆記本的製造方法。這樣就違反OOP的開閉原則(一個軟件的實體應該對擴展開放,對修改關閉),但是如果我們的產品是長期穩定不變的,就可以接受。和前兩種工廠 模式相比,抽象工廠把同一產品族放到了一起,一個工廠可以生產一個產品族。
總結
簡單工廠模式雖然違反了OOP七大原則,但是在實際中還是使用最多的。
工廠方法模式通過增加工廠在不違反OOP開閉原則的前提下實現動態擴展,但是代碼量大,編程和管理複雜反而沒有簡單工廠使用多。
抽象工廠模式圍繞一個抽象工廠去創建其他工廠,一個工廠可以生產一個產品族而不只是一個產品,但是不適合產品經常變動的情況。