概述
根據(jù)依賴(lài)倒置原則
,我們知道,我們應(yīng)優(yōu)先依賴(lài)抽象類(lèi)而不是具體類(lèi)画畅。在應(yīng)用開(kāi)發(fā)過(guò)程中砸琅,有很多實(shí)體類(lèi)都是非常易變的,依賴(lài)它們會(huì)帶來(lái)問(wèn)題轴踱,所以我們更應(yīng)該依賴(lài)于抽象接口症脂,已使我們免受大多數(shù)變化的影響。
工廠模式(Factory)
允許我們只依賴(lài)于抽象接口就能創(chuàng)建出具體對(duì)象的實(shí)例寇僧,所以在開(kāi)發(fā)中摊腋,如果具體類(lèi)是高度易變的,那么該模式就非常有用嘁傀。
接下來(lái)我們就通過(guò)代碼舉例說(shuō)明什么是工廠模式
簡(jiǎn)單工廠模式
假設(shè)我們現(xiàn)在有個(gè)需求:把一段數(shù)據(jù)用Wi-Fi或者藍(lán)牙發(fā)送出去。
需求很簡(jiǎn)單是吧视粮?刷刷刷就寫(xiě)下了以下實(shí)現(xiàn):
private String mode; //Wi-Fi|Bluetooth
public void onClick() {
byte[] data = {0x00, 0x01};
if ("Wi-Fi".equals(mode)) {
sendDataByWiFi(data);
} else {
sendDataByBluetooth(data);
}
}
private void sendDataByWiFi(byte[] data) {
// send data via Wi-Fi
}
private void sendDataByBluetooth(byte[] data) {
// send data via Bluetooth
}
但是上面的代碼擴(kuò)展性并不高细办,違反了開(kāi)放封閉原則。比如現(xiàn)在又有了個(gè)新的需求蕾殴,需要用zigbee把數(shù)據(jù)發(fā)送出去笑撞,就得再新增一個(gè)sendDataByZigbee方法了,而且還得修改onClick里面的邏輯钓觉。那么比較好的方法是怎么樣的呢茴肥?
定義一個(gè)數(shù)據(jù)發(fā)送器類(lèi):
/**
* 數(shù)據(jù)發(fā)送器Sender
*
* @author HansChen
*/
public interface Sender {
void sendData(byte[] data);
}
實(shí)現(xiàn)WiFi數(shù)據(jù)發(fā)送:
/**
* Sender的實(shí)現(xiàn)類(lèi),通過(guò)Wi-Fi發(fā)送數(shù)據(jù)
*
* @author HansChen
*/
public class WiFiSender implements Sender {
@Override
public void sendData(byte[] data) {
System.out.println("Send data by Wi-Fi");
}
}
實(shí)現(xiàn)藍(lán)牙數(shù)據(jù)發(fā)送:
/**
* Sender的實(shí)現(xiàn)類(lèi)荡灾,通過(guò)藍(lán)牙發(fā)送數(shù)據(jù)
*
* @author HansChen
*/
public class BluetoothSender implements Sender {
@Override
public void sendData(byte[] data) {
System.out.println("Send data by Bluetooth");
}
}
這樣瓤狐,原來(lái)發(fā)送數(shù)據(jù)的地方就改為了:
private String mode; //Wi-Fi|Bluetooth
public void onClick() {
byte[] data = {0x00, 0x01};
Sender sender;
if ("Wi-Fi".equals(mode)) {
sender = new WiFiSender();
} else {
sender = new BluetoothSender();
}
sender.sendData(data);
}
有沒(méi)有覺(jué)得代碼優(yōu)雅了一點(diǎn)?但是隨著發(fā)送器Sender的實(shí)現(xiàn)類(lèi)越來(lái)越多批幌,每增加一個(gè)實(shí)現(xiàn)類(lèi)础锐,就需要在onClick里面實(shí)例化相應(yīng)的實(shí)現(xiàn)類(lèi),能不能用一個(gè)單獨(dú)的類(lèi)來(lái)做這個(gè)創(chuàng)造實(shí)例的過(guò)程呢荧缘?這就是我們講到的工廠皆警。我們新增一個(gè)工廠類(lèi):
/**
* 簡(jiǎn)單工廠類(lèi)
*
* @author HansChen
*/
public class SimpleFactory {
public static Sender createSender(String mode) {
switch (mode) {
case "Wi-Fi":
return new WiFiSender();
case "Bluetooth":
return new BluetoothSender();
default:
throw new IllegalArgumentException("illegal type: " + mode);
}
}
}
這樣一來(lái),怎么實(shí)例化數(shù)據(jù)發(fā)送器我們也不用管了截粗,最終代碼變?yōu)椋?/p>
private String mode; //Wi-Fi|Bluetooth
public void onClick() {
byte[] data = {0x00, 0x01};
Sender sender = SimpleFactory.createSender(mode);
sender.sendData(data);
}
好了信姓,到這里我們就完成了簡(jiǎn)單工廠模式的應(yīng)用了,下圖就是簡(jiǎn)單工廠模式的結(jié)構(gòu)圖:
工廠方法模式
簡(jiǎn)單工廠模式的優(yōu)點(diǎn)在于工廠類(lèi)包含了必要的判斷邏輯绸罗,根據(jù)傳入的參數(shù)動(dòng)態(tài)實(shí)例化相關(guān)的類(lèi)意推,對(duì)于客戶(hù)端來(lái)說(shuō),去除了與具體產(chǎn)品的依賴(lài)从诲。但是這里還是會(huì)有個(gè)問(wèn)題左痢,假設(shè)上面例子中新增了一個(gè)zigbee發(fā)送器,那么一定是需要修改簡(jiǎn)單工廠類(lèi)的,也就是說(shuō)俊性,我們不但對(duì)擴(kuò)展開(kāi)放了略步,對(duì)修改也開(kāi)放了,這是不好的定页。解決的方法是使用工廠方法模式趟薄,工廠方法模式是指定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類(lèi)決定實(shí)例化哪一個(gè)類(lèi)典徊。下面還是通過(guò)代碼來(lái)說(shuō)明:
在簡(jiǎn)單工廠模式的基礎(chǔ)上杭煎,讓我們對(duì)工廠類(lèi)也升級(jí)一下,首先定義一個(gè)工廠類(lèi)接口:
public interface SenderFactory {
Sender createSender();
}
然后為每一個(gè)發(fā)送器的實(shí)現(xiàn)類(lèi)各創(chuàng)建一個(gè)具體的工廠方法去實(shí)現(xiàn)這個(gè)接口
定義WiFiSender的工廠類(lèi):
public class WiFiSenderFactory implements SenderFactory {
@Override
public Sender createSender() {
return new WiFiSender();
}
}
定義BluetoothSender的工廠類(lèi):
public class BluetoothSenderFactory implements SenderFactory {
@Override
public Sender createSender() {
return new BluetoothSender();
}
}
這樣卒落,即使有新的Sender實(shí)現(xiàn)類(lèi)加進(jìn)來(lái)羡铲,我們只需要新增相應(yīng)的工廠類(lèi)就行了,不需要修改原有的工廠儡毕,下圖就是工廠方法模式的結(jié)構(gòu)圖:
客戶(hù)端調(diào)用代碼:
private String mode; //Wi-Fi|Bluetooth
public void onClick() {
byte[] data = {0x00, 0x01};
SenderFactory factory;
if ("Wi-Fi".equals(mode)) {
factory = new WiFiSenderFactory();
} else {
factory = new BluetoothSenderFactory();
}
Sender sender = factory.createSender();
sender.sendData(data);
}
細(xì)心的讀者可能已經(jīng)發(fā)現(xiàn)了也切,工廠方法模式實(shí)現(xiàn)時(shí),客戶(hù)端需要決定實(shí)例化哪一個(gè)工廠類(lèi)腰湾,相比于簡(jiǎn)單工廠模式雷恃,客戶(hù)端多了一個(gè)選擇判斷的問(wèn)題,也就是說(shuō)费坊,工廠方法模式把簡(jiǎn)單工廠模式的內(nèi)部邏輯判斷移到了客戶(hù)端倒槐!你想要加功能,本來(lái)是修改簡(jiǎn)單工廠類(lèi)的附井,現(xiàn)在改為修改客戶(hù)端讨越。但是這樣帶來(lái)的好處是整個(gè)工廠和產(chǎn)品體系都沒(méi)有“修改”的變化,只有“擴(kuò)展”的變化羡忘,完全符合了開(kāi)放封閉原則谎痢。
總結(jié)
簡(jiǎn)單工廠模式和工廠方法模式都封裝了對(duì)象的創(chuàng)建,它們使得高層策略模塊在創(chuàng)建類(lèi)的實(shí)例時(shí)無(wú)需依賴(lài)于這些類(lèi)的具體實(shí)現(xiàn)卷雕。但是兩種工廠模式之間又有差異:
- 簡(jiǎn)單工廠模式:最大的優(yōu)點(diǎn)在于工廠類(lèi)包含了必要的判斷邏輯节猿,根據(jù)客戶(hù)端的條件動(dòng)態(tài)地實(shí)例化相關(guān)的類(lèi)。但這也是它的缺點(diǎn)漫雕,當(dāng)擴(kuò)展功能的時(shí)候滨嘱,需要修改工廠方法,違反了開(kāi)放封閉原則
- 工廠方法模式:符合開(kāi)放封閉原則浸间,但這帶來(lái)的代價(jià)是擴(kuò)展的時(shí)候要增加相應(yīng)的工廠類(lèi)太雨,增加了開(kāi)發(fā)量,而且需要修改客戶(hù)端代碼