- 設(shè)計(jì)模式解析一 工廠模式的不同
- 設(shè)計(jì)模式解析二 結(jié)構(gòu)模式三劍客
- 設(shè)計(jì)模式解析三 行為模式三劍客
- 設(shè)計(jì)模式解析四 模板方法模式和外觀模式
- 設(shè)計(jì)模式解析五 觀察者模式和橋接模式
- 設(shè)計(jì)模式解析六 單例模式
一. 前言
作為一個(gè)程序開(kāi)發(fā)者,設(shè)計(jì)模式的學(xué)習(xí)總是不變的真理,也許在程序員職業(yè)生涯的前期學(xué)習(xí)設(shè)計(jì)模式還知識(shí)死記硬背,不知道如何應(yīng)用的話(huà),那在工作一段時(shí)間以后再回過(guò)頭來(lái)學(xué)習(xí),會(huì)發(fā)現(xiàn)前人的總結(jié)包含著無(wú)數(shù)的智慧。
用百科的話(huà)來(lái)說(shuō),設(shè)計(jì)模式是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類(lèi)的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。
一千個(gè)人有一千個(gè)哈姆雷特,每個(gè)人學(xué)習(xí)設(shè)計(jì)模式都會(huì)有自己的理解,接下來(lái)這個(gè)系列就是我對(duì)設(shè)計(jì)模式的理解,記錄下來(lái),也作為自己這段時(shí)間的學(xué)習(xí)總結(jié)。
二. 工廠模式
接下來(lái)的設(shè)計(jì)模式解析會(huì)進(jìn)行一個(gè)分組的總結(jié),把一些結(jié)構(gòu)或者容易搞混的設(shè)計(jì)模式會(huì)合并在一起來(lái)進(jìn)行分析,然后分析其中的不同。
java的是面向?qū)ο蟮木幊?,我們到處都在同?duì)象打交道,自然少不了創(chuàng)建這樣那樣的對(duì)象,如何得到對(duì)象呢,比如這個(gè)代碼:
Deserializer deserializer = new LocationInfoDeserializer();
有時(shí)候需要根據(jù)條件來(lái)獲取不同的實(shí)現(xiàn):
//解碼器
Deserializer deserializer;
if (UploadMsgType.LOCATION_INFO.equals(msgType)) {
deserializer = new LocationInfoDeserializer();
} else if (UploadMsgType.MEDIA_INFO.equals(msgType)) {
deserializer = new MediaInfoDeserializer();
}
這樣的確是正確的,但這存在一些問(wèn)題:
- 如果實(shí)現(xiàn)類(lèi)需要替換,我們需要去每一個(gè)
new的地方去修改 - 如果需要根據(jù)不同的條件來(lái)創(chuàng)建不同的實(shí)現(xiàn),這種
if判斷代碼可能充斥的到處都是
回憶一下,我們編寫(xiě)業(yè)務(wù)代碼是不是總是先定義一個(gè)Service的interface,然后在定義一個(gè)ServiceImpl的實(shí)現(xiàn),那么有沒(méi)有想過(guò)為什么要這么做呢?
設(shè)計(jì)模式的思想告訴我們要做接口隔離,針對(duì)接口編程,而不是針對(duì)實(shí)現(xiàn)的編程,這樣我們只要實(shí)現(xiàn)遵循了接口的規(guī)范,我們就可以任意的替換實(shí)現(xiàn),而不必?fù)?dān)心對(duì)上層應(yīng)用造成影響。
Spring是一個(gè)偉大的框架,他為我們隱藏了很多復(fù)雜的細(xì)節(jié),讓我們的開(kāi)發(fā)更加簡(jiǎn)單,實(shí)際上Spring框架中蘊(yùn)含著豐富的設(shè)計(jì)模式,比如@Service注解,它為我們實(shí)現(xiàn)了工廠模式的過(guò)程,而隱藏了工廠模式的細(xì)節(jié),但總有一些時(shí)候是需要我們自己去使用設(shè)計(jì)模式的,這時(shí)候思想的積累就尤為重要了。
1. 簡(jiǎn)單工廠模式
回到解碼器這段代碼,試想一下,如果這段代碼揉進(jìn)了業(yè)務(wù)代碼里面會(huì)有多糟糕,這是一個(gè)解碼器,需要根據(jù)不同的消息類(lèi)型來(lái)得到解碼器,消息類(lèi)型增加的可能性太大了!
我們應(yīng)該封裝這部分,封裝變化這是不變的真理:
public class SimpleDeserializerFactory {
public static Deserializer produce(UploadMsgType msgType) {
Deserializer deserializer = null;
if (UploadMsgType.LOCATION_INFO.equals(msgType)) {
deserializer = new LocationInfoDeserializer();
} else if (UploadMsgType.MEDIA_INFO.equals(msgType)) {
deserializer = new MediaInfoDeserializer();
}
return deserializer;
}
}
所有需要解碼器的地方只要調(diào)用這個(gè)方法就好,無(wú)論增加多少種消息類(lèi)型,我們只需要在這里增加一段else if即可,沒(méi)錯(cuò)這就是簡(jiǎn)單工廠模式,也許這都不能稱(chēng)之為一個(gè)設(shè)計(jì)模式,只是一種編碼習(xí)慣。
下面看類(lèi)圖:

2. 工廠方法模式
上面的簡(jiǎn)單工廠讓我們能夠把獲取解碼器的部分進(jìn)行了封裝,但上面這是一個(gè)極簡(jiǎn)單的例子,每一個(gè)解碼器的實(shí)例化都只是簡(jiǎn)單的進(jìn)行一次new,但現(xiàn)實(shí)中總不是這樣,對(duì)象和對(duì)象之間存在太多的依賴(lài)關(guān)系,一個(gè)對(duì)象需要依賴(lài)其他幾個(gè),甚至十幾個(gè)對(duì)象,如果還像上面這樣,那我們的代碼會(huì)變成非常繁雜,大量的set、new:
public static Deserializer produce(UploadMsgType msgType) {
if (UploadMsgType.LOCATION.equals(msgType)) {
LocationInfoDeserializer locationInfoDeserializer = new LocationInfoDeserializer();
LocationService locationService = new LocationService();
locationInfoDeserializer.setLocationService(locationService);
return locationInfoDeserializer;
} else if (UploadMsgType.MEDIA.equals(msgType)) {
MediaInfoDeserializer mediaInfoDeserializer = new MediaInfoDeserializer();
MediaInfoService mediaInfoService = new MediaInfoService();
mediaInfoDeserializer.setMediaInfoService(mediaInfoService);
return mediaInfoDeserializer;
}
return null;
}
這上面每個(gè)解碼器都只有一個(gè)依賴(lài),看起來(lái)還好,如果有十個(gè)呢?
未來(lái)隨著業(yè)務(wù)的發(fā)展,將會(huì)出現(xiàn)改變,如果位置解碼器出現(xiàn)改變,增加了依賴(lài),我們將會(huì)修改這部分的代碼,但面向?qū)ο蟮幕驹瓌t告訴我們一個(gè)類(lèi)應(yīng)當(dāng)只有一個(gè)發(fā)生變化的原因。但上面的類(lèi)出現(xiàn)了兩個(gè):
- 位置解碼器發(fā)生變化
- 多媒體解碼器發(fā)生變化
那么我們能不能更進(jìn)一步,把上面的變化繼續(xù)封裝起來(lái)呢?
設(shè)計(jì)模式教我們要面向接口編程,那么我們能不能設(shè)計(jì)一個(gè)接口,接口返回解碼器,然后設(shè)計(jì)不同的實(shí)現(xiàn)來(lái)返回不同的解碼器呢?
public interface DeserializerFactory {
Deserializer create();
}
public class LocationInfoDeserializerFactory implements DeserializerFactory {
@Override
public Deserializer create() {
LocationInfoDeserializer deserializer = new LocationInfoDeserializer();
LocationService service = new LocationService();
deserializer.setLocationService(service);
return deserializer;
}
}
public class MediaInfoDeserializerFactory implements DeserializerFactory {
@Override
public Deserializer create() {
MediaInfoDeserializer deserializer = new MediaInfoDeserializer();
MediaInfoService service = new MediaInfoService();
deserializer.setMediaInfoService(service);
return deserializer;
}
}
你看,現(xiàn)在變化的地方都被我們封裝了起來(lái),再回到剛才的方法,讓我們對(duì)這個(gè)方法再進(jìn)行一些改造:
public class SimpleDeserializerFactory {
public static DeserializerFactory produce(UploadMsgType msgType) {
DeserializerFactory factory = null;
if (UploadMsgType.LOCATION.equals(msgType)) {
factory = new LocationInfoDeserializerFactory();
} else if (UploadMsgType.MEDIA.equals(msgType)) {
factory = new MediaInfoDeserializerFactory();
}
return factory;
}
public static void main(String[] args) {
UploadMsgType msgType = UploadMsgType.LOCATION;
DeserializerFactory factory = SimpleDeserializerFactory.produce(msgType);
Deserializer deserializer = factory.create();
}
}
你看這一次,這個(gè)類(lèi)又只剩下了一個(gè)改變的原因,那就是出現(xiàn)新增消息類(lèi)型的時(shí)候。
這就是工廠方法模式,下面看類(lèi)圖:

定義
工廠方法模式定義了一個(gè)創(chuàng)建對(duì)象的接口,但由子類(lèi)決定要實(shí)例化的類(lèi)是哪一個(gè),工廠方法讓類(lèi)把實(shí)例化推遲到子類(lèi)。
這也是著名的依賴(lài)倒置原則,要依賴(lài)抽象,而不是依賴(lài)具體的實(shí)現(xiàn)類(lèi)。
3. 抽象工廠模式
接下來(lái),需求又來(lái)了,客戶(hù)希望我們不僅僅能夠進(jìn)行解碼,還希望我們能夠?qū)獯a后的內(nèi)容進(jìn)行存儲(chǔ)和一些特殊的處理,而不同的內(nèi)容進(jìn)行存儲(chǔ)的方式也是不同的,所以我們抽象一下,可以知道,不論是位置還是多媒體數(shù)據(jù)我們都需要一個(gè)解碼器,一個(gè)倉(cāng)庫(kù)服務(wù)還有一個(gè)處理器,這是他們的相同之處,不同的只是實(shí)現(xiàn)。
首先我們?cè)黾右粋€(gè)倉(cāng)庫(kù)服務(wù)的接口:
public interface StorageService {
void save(Object o);
}
然后增加一個(gè)存儲(chǔ)位置和存儲(chǔ)多媒體的倉(cāng)庫(kù)服務(wù)實(shí)現(xiàn):
public class MediaStorageService implements StorageService {
@Override
public void save(Object o) {
System.out.println("save the media");
}
}
public class LocationStorageService implements StorageService {
@Override
public void save(Object o) {
System.out.println("save the location");
}
}
接下來(lái)創(chuàng)建一個(gè)消息處理工廠的接口:
public interface MsgProcessFactory {
Deserializer createDeserializer();
StorageService createStorage();
}
針對(duì)不同的消息類(lèi)型,創(chuàng)建不同的工廠實(shí)現(xiàn):
public class MediaMsgProcessFactory implements MsgProcessFactory {
@Override
public Deserializer createDeserializer() {
return new MediaInfoDeserializer();
}
@Override
public StorageService createStorage() {
return new MediaStorageService();
}
}
public class LocationMsgProcessFactory implements MsgProcessFactory {
@Override
public Deserializer createDeserializer() {
return new LocationInfoDeserializer();
}
@Override
public StorageService createStorage() {
return new LocationStorageService();
}
}
我們還需要一個(gè)消息處理器,來(lái)整合這些消息處理的過(guò)程:
public interface MsgProcess {
void process(byte[] bytes);
}
針對(duì)不同消息的不同處理過(guò)程:
public class MediaMsgProcess implements MsgProcess {
private MsgProcessFactory msgProcessFactory;
public MediaMsgProcess(MsgProcessFactory msgProcessFactory) {
this.msgProcessFactory = msgProcessFactory;
}
@Override
public void process(byte[] bytes) {
Deserializer deserializer = msgProcessFactory.createDeserializer();
Object o = deserializer.decode(bytes);
StorageService storageService = msgProcessFactory.createStorage();
storageService.save(o);
// TODO 其他針對(duì)多媒體的業(yè)務(wù)邏輯
}
}
public class LocationMsgProcess implements MsgProcess {
private MsgProcessFactory msgProcessFactory;
public LocationMsgProcess(MsgProcessFactory msgProcessFactory) {
this.msgProcessFactory = msgProcessFactory;
}
@Override
public void process(byte[] bytes) {
Deserializer deserializer = msgProcessFactory.createDeserializer();
Object o = deserializer.decode(bytes);
StorageService storageService = msgProcessFactory.createStorage();
storageService.save(o);
// TODO 其他針對(duì)位置的業(yè)務(wù)邏輯
}
}
然后讓我們運(yùn)行他:
public static void main(String[] args) {
byte[] bytes = new byte[1024];
MsgProcessFactory factory = new LocationMsgProcessFactory();
MsgProcess process = new LocationMsgProcess(factory);
process.process(bytes);
factory = new MediaMsgProcessFactory();
process = new MediaMsgProcess(factory);
process.process(bytes);
}
輸出:
save the location
save the media
定義
抽象工廠模式的重點(diǎn)在于提供一個(gè)接口,用于創(chuàng)建相關(guān)或依賴(lài)對(duì)象的家族,而不需要明確具體的實(shí)現(xiàn)。
在這里,我們的解碼器和倉(cāng)庫(kù)服務(wù)就是相關(guān)的對(duì)象家族,因?yàn)槎嗝襟w的解碼器應(yīng)該和多媒體倉(cāng)庫(kù)服務(wù)一起使用,如果用錯(cuò)了就會(huì)出現(xiàn)致命的后果。而無(wú)論什么消息需要處理的時(shí)候都需要一個(gè)解碼器和一個(gè)倉(cāng)庫(kù)服務(wù),所以這才是抽象,實(shí)際應(yīng)用中,抽象自然沒(méi)有這么簡(jiǎn)單,是需要仔細(xì)思考的。
抽象工廠模式和工廠方法模式的不同
為什么抽象工廠模式和工廠方法模式相比只是在一個(gè)工廠接口里多了幾個(gè)方法卻被稱(chēng)為抽象的原因,在我理解,抽象描述的就是抽象出對(duì)不同的業(yè)務(wù)邏輯處理中相同的部分,如果用肉包子來(lái)舉例,我們可以試著抽象,制作一個(gè)鮮肉包子、醬肉包子、牛肉包子的做法是不同的,但是他們相同的地方在哪呢?
- 都需要面粉
- 都需要肉(盡管肉可能是不同的)
- 都需要調(diào)料(調(diào)料可能也是不同的)
這就是抽象。
抽象工廠模式的類(lèi)圖就不給了,大家試著自己畫(huà)一下吧。