設(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è)Serviceinterface,然后在定義一個(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)圖:

簡(jiǎn)單工廠模式

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ì)變成非常繁雜,大量的setnew:

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à)一下吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 鏈接:https://github.com/WiKi123/DesignPattern作者: WiKi123(gi...
    樹(shù)懶啊樹(shù)懶閱讀 3,845評(píng)論 0 2
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,665評(píng)論 1 32
  • 本文首發(fā)于個(gè)人博客:Lam's Blog - 談?wù)?3種設(shè)計(jì)模式在Android源碼及項(xiàng)目中的應(yīng)用,文章由Mark...
    格子林ll閱讀 4,774評(píng)論 1 105
  • 一生所愛(ài)始終只有你一人而已,你是一縷縷春風(fēng),吹得我滿(mǎn)心歡喜;你是冬日里溫暖的陽(yáng)光,明媚而燦爛;你是我清澈透...
    王子懿閱讀 348評(píng)論 0 1
  • 一聲問(wèn)候,隨著新年的喜慶悄然送達(dá) 一份深情,伴著友誼的甜蜜悠然發(fā)酵 一種情誼,聽(tīng)著歡快的頌歌吟唱恒久 一道祝福,順...
    微笑nice閱讀 208評(píng)論 0 1

友情鏈接更多精彩內(nèi)容