日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

設(shè)計(jì)模式 - 工廠模式

 印度阿三17 2019-09-30

概述

我們都知道Java中共有 23 種設(shè)計(jì)模式,其中工廠模式分為三種,即:簡(jiǎn)單工廠模式(不在 23 種設(shè)計(jì)模式之列)、工廠方法模式和抽象工廠模式;我們平時(shí)說的工廠模式,其實(shí)大都指工廠方法模式,這種模式是我們平時(shí)編碼中用的頻率最高的一種,在Spring源碼中就有很多工廠模式的應(yīng)用,比如 BeanFactory。

下面依次按照簡(jiǎn)單工廠模式、工廠方法模式、抽象工廠模式的順序,依次由淺入深說說這三種模式;文章分別從定義、場(chǎng)景、優(yōu)缺點(diǎn)也示例進(jìn)行講解。

簡(jiǎn)單工廠模式

定義

簡(jiǎn)單工廠模式(Simple Factory Pattern)是指由一個(gè)工廠對(duì)象決定創(chuàng)建出哪一種產(chǎn)品類的實(shí)例,簡(jiǎn)單來說就是,
定義一個(gè)工廠類,根據(jù)傳入的參數(shù)不同返回不同的實(shí)例,被創(chuàng)建的實(shí)例具有共同的父類或接口。

場(chǎng)景

簡(jiǎn)單工廠適用于工廠類負(fù)責(zé)創(chuàng)建的對(duì)象較少的場(chǎng)景,且客戶端只需要傳入工廠類的參數(shù),對(duì)于如何創(chuàng)建對(duì)象的邏輯不需要關(guān)心??偨Y(jié)一下就是:

  1. 需要?jiǎng)?chuàng)建的對(duì)象較少;

  2. 客戶端不關(guān)心對(duì)象的創(chuàng)建過程;

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

實(shí)現(xiàn)了對(duì)責(zé)任的分割,提供了專門的工廠類用于創(chuàng)建對(duì)象

缺點(diǎn)

工廠類的職責(zé)相對(duì)過重,不易于擴(kuò)展過于復(fù)雜的產(chǎn)品結(jié)構(gòu),不符合開閉原則(可解決)

示例

接下來我們構(gòu)造一個(gè)場(chǎng)景來看看簡(jiǎn)單工廠模式的應(yīng)用:現(xiàn)在手機(jī)更新?lián)Q代的比較快,手機(jī)廠商每年基本都會(huì)在不同時(shí)間或者在同一時(shí)間發(fā)布生產(chǎn)不同型號(hào)和配置的手機(jī)。

假設(shè)某手機(jī)公司最近發(fā)布了型號(hào)為 A、B 的手機(jī),其中生產(chǎn)任務(wù)交給代工廠去生產(chǎn);我們都知道不管什么類型的手機(jī)都屬于手機(jī),所以我們先創(chuàng)建一個(gè)手機(jī)類Phone,并在其中聲明一個(gè)公共的手機(jī)型號(hào)方法type

/**
 * @author eamon.zhang
 * @date 2019-09-27 上午10:55
 */
public interface Phone {
    void type();
}

然后定義具體的手機(jī)類型:

型號(hào) A:

/**
 * @author eamon.zhang
 * @date 2019-09-27 上午11:02
 */
public class PhoneA implements Phone {
    @Override
    public void type() {
        System.out.println("型號(hào)為A的手機(jī)!");
    }
}

型號(hào) B:

/**
 * @author eamon.zhang
 * @date 2019-09-27 上午11:03
 */
public class PhoneB implements Phone {
    @Override
    public void type() {
        System.out.println("型號(hào)為B的手機(jī)!");
    }
}

創(chuàng)建手機(jī)代工廠 PhoneFactory 類:

/**
 * @author eamon.zhang
 * @date 2019-09-27 上午10:54
 */
public class PhoneFactory {
    public Phone product(String type) {
        switch (type) {
            case "A":
                return new PhoneA();
            case "B":
                return new PhoneB();
            default:
                return null;
        }
    }
}

測(cè)試:

/**
 * @author eamon.zhang
 * @date 2019-09-27 上午11:09
 */
public class PhoneFactoryTest {

    @Test
    public void product() {
        PhoneFactory phoneFactory = new PhoneFactory();
        phoneFactory.product("A").type();

        phoneFactory.product("B").type();
    }
}

輸出:

型號(hào)為A的手機(jī)!
型號(hào)為B的手機(jī)!

當(dāng)然,為了方便調(diào)用,PhoneFactory 中的product()也可以寫成靜態(tài)的。

類圖:

拓展

解決不符合開閉原則問題

上面的示例中,客戶端調(diào)用是簡(jiǎn)單了,但如果我們業(yè)務(wù)繼續(xù)擴(kuò)展,增加一個(gè)型號(hào) C,那么上面的工廠方法中的product() 方法就得再次修改邏輯。不符合開閉原則;因此我們客戶考慮對(duì)其進(jìn)行進(jìn)一步優(yōu)化,利用反射技術(shù)修改product()方法:

 public Phone product(String className) {
    try {
        if (!(null == className || "".equals(className))) {
            return (Phone) Class.forName(className).newInstance();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

修改客戶端調(diào)用代碼:

public void product() {
    PhoneFactory phoneFactory = new PhoneFactory();
    phoneFactory.product("com.eamon.javadesignpatterns.factory.PhoneA").type();

    phoneFactory.product("com.eamon.javadesignpatterns.factory.PhoneB").type();
}

經(jīng)過優(yōu)化之后,今后再增加型號(hào),就不用去修改工廠方法了;但是又有一個(gè)問題,方法參數(shù)是很長(zhǎng)的字符串,可控性有待提升,而且還需要強(qiáng)制轉(zhuǎn)型,不方便閱讀和維護(hù),所以進(jìn)一步改造:

public Phone product(Class<? extends Phone> clazz) {
    try {
        if (null != clazz) {
            return clazz.newInstance();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

優(yōu)化客戶端調(diào)用代碼:

@Test
public void product() {
    PhoneFactory phoneFactory = new PhoneFactory();
    phoneFactory.product(PhoneA.class).type();

    phoneFactory.product(PhoneB.class).type();
}

再來看一下類圖:


其他

簡(jiǎn)單工廠模式在 JDK 源碼中也無處不足,比如常用的 Calendar類中Calendar.getInstance()方法,跟進(jìn)源碼到createCalendar(TimeZone zone,Locale aLocale)就可以看出。

還有就是 常用的logback,我們可以看到 LoggerFactory 中有多個(gè)重載的方法 getLogger():

 public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}

public final Logger getLogger(final Class<?> clazz) {
    return getLogger(clazz.getName());
}

工廠方法模式

定義

工廠方法模式(Fatory Method Pattern)是指定義一個(gè)創(chuàng)建對(duì)象的接口,但讓實(shí)現(xiàn)這個(gè) 接口的類來決定實(shí)例化哪個(gè)類,工廠方法讓類的實(shí)例化推遲到子類中進(jìn)行。

在工廠方法模式中用戶只需要關(guān)心所需產(chǎn)品對(duì)應(yīng)的工廠,無須關(guān)心創(chuàng)建細(xì)節(jié),而且加入新的產(chǎn)品符 合開閉原則。

工廠方法模式主要解決產(chǎn)品擴(kuò)展的問題,在簡(jiǎn)單工廠中,隨著產(chǎn)品鏈的豐富,如果每個(gè)手機(jī)的創(chuàng)建邏輯有區(qū)別的話,工廠的職責(zé)會(huì)變得越來越多,有點(diǎn)像萬能工廠,并不便于維護(hù)。根據(jù)單一職責(zé)原則我們將職能繼續(xù)拆分,專人干專事。

場(chǎng)景

工廠方法適用于以下場(chǎng)景:

  1. 創(chuàng)建對(duì)象需要大量重復(fù)的代碼。

  2. 客戶端(應(yīng)用層)不依賴于產(chǎn)品類實(shí)例如何被創(chuàng)建、實(shí)現(xiàn)等細(xì)節(jié)。

  3. 一個(gè)類通過其子類來指定創(chuàng)建哪個(gè)對(duì)象。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  1. 具有良好的封裝性,代碼結(jié)構(gòu)清晰,井底了模塊間的耦合。

  2. 拓展性非常優(yōu)秀。(在增加產(chǎn)品類的情況下,只要修改具體的工廠類或擴(kuò)展一個(gè)工廠類)

  3. 屏蔽了產(chǎn)品類。(產(chǎn)品類的實(shí)現(xiàn)如何變化,調(diào)用者不需要關(guān)心)

缺點(diǎn):

1、類的個(gè)數(shù)容易過多,增加復(fù)雜度。
2、增加了系統(tǒng)的抽象性和理解難度。

示例

A 型號(hào)手機(jī)由PhoneA工廠創(chuàng)建,B 型號(hào)手機(jī)由PhoneB工廠創(chuàng)建,對(duì)工廠本身也做一個(gè)抽象。來看代碼,先創(chuàng)建 PhoneFactory 接口:

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午1:45
 */
public interface PhoneFactory {
   Phone product();
}

分別創(chuàng)建子工廠 PhoneAFactory

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午1:50
 */
public class PhoneAFactory implements PhoneFactory {
    @Override
    public Phone product() {
        return new PhoneA();
    }
}

PhoneBFactory 類:

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午1:50
 */
public class PhoneBFactory implements PhoneFactory {
    @Override
    public Phone product() {
        return new PhoneB();
    }
}

看測(cè)試代碼:

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午1:54
 */
public class PhoneFactoryTest {

    @Test
    public void product() {
        PhoneFactory factory = new PhoneAFactory();
        factory.product().type();

        factory = new PhoneBFactory();
        factory.product().type();

    }
}

測(cè)試結(jié)果:

型號(hào)為A的手機(jī)!
型號(hào)為B的手機(jī)!

再看一下類圖:

拓展

再來看看 logback 中工廠方法模式的應(yīng)用,看看類圖就 OK 了:


抽象工廠模式

定義

抽象工廠模式(Abastract Factory Pattern)是指提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口,無需指定他們具體的類。

客戶端(應(yīng)用層)不依賴于產(chǎn)品類實(shí)例如何被創(chuàng)建、實(shí)現(xiàn)等細(xì)節(jié)。強(qiáng)調(diào)的是一系列相關(guān)的產(chǎn)品對(duì)象(屬于同一產(chǎn)品族)一起使用創(chuàng)建對(duì)象需要大量重復(fù)的代碼。需要提供一個(gè)產(chǎn)品類的庫(kù),所有的產(chǎn)品以同樣的接口出現(xiàn),從而使客戶端不依賴于具體實(shí)現(xiàn)。

理解

為了便于大家理解抽象工廠,我們先了解兩個(gè)概念產(chǎn)品等級(jí)結(jié)構(gòu)和產(chǎn)品族,看下面的圖:

從上圖中看出有正方形,圓形和三角形三種圖形,相同顏色深淺的就代表同一個(gè)產(chǎn)品族,相同形狀的代表同一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)。同樣可以從生活中來舉例,比如,美的電器生產(chǎn)多種家用電器。那么上圖中,顏色最深的正方形就代表美的洗衣機(jī)、顏色最深的圓形代表美的空調(diào)、顏色最深的三角形代表美的熱水器,顏色最深的一排都屬于美的品牌,都是美的電器這個(gè)產(chǎn)品族。再看最右側(cè)的三角形,顏色最深的我們指定了代表美的熱水器,那么第二排顏色稍微淺一點(diǎn)的三角形,代表海信的熱水器。同理,同一產(chǎn)品結(jié)構(gòu)下還有格力熱水器,格力空調(diào),格力洗衣機(jī)。

再看下面這張圖,最左側(cè)的箭頭代表具體的工廠,有美的工廠、海信工廠、格力工廠。每個(gè)品牌的工廠都生產(chǎn)洗衣機(jī)、熱水器、空調(diào)。

通過上面兩張圖的對(duì)比理解,相信大家對(duì)抽象工廠有了非常形象的理解。

場(chǎng)景

一個(gè)對(duì)象族(或是一組沒有任何關(guān)系的對(duì)象)都有相同的約束,則可以使用抽象工廠模式。簡(jiǎn)單來說:

  1. 和工廠方法一樣客戶端不需要知道它所創(chuàng)建的對(duì)象的類。

  2. 需要一組對(duì)象共同完成某種功能時(shí)。并且可能存在多組對(duì)象完成不同功能的情況。

  3. 系統(tǒng)結(jié)構(gòu)穩(wěn)定,不會(huì)頻繁的增加對(duì)象。(因?yàn)橐坏┰黾泳托枰薷脑写a,不符合開閉原則)

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 封裝性,每個(gè)產(chǎn)品的實(shí)現(xiàn)類不是高層模塊要關(guān)心的,它要關(guān)心的是接口,不關(guān)心對(duì)象是如何創(chuàng)建的,只要知道工廠類是誰,就能創(chuàng)建出一個(gè)需要的對(duì)象,省時(shí)省力。

  • 產(chǎn)品族內(nèi)的約束為非公開狀態(tài)。

缺點(diǎn)

  • 規(guī)定了所有可能被創(chuàng)建的產(chǎn)品集合,產(chǎn)品族中擴(kuò)展新的產(chǎn)品困難,需要修改抽象工廠的接口

  • 增加了系統(tǒng)的抽象性和理解難度

示例

比如現(xiàn)在有一個(gè)應(yīng)用,假如是某視頻軟件,需要在三個(gè)不同的平臺(tái)(Windows、IOS、Android)上運(yùn)行,該應(yīng)用針對(duì)每套系統(tǒng)都設(shè)計(jì)了一套上傳控制器(UploadController)、播放控制(DisplayController),下面通過抽象工廠模式來設(shè)計(jì)該軟件。

視頻軟件里邊的各個(gè)平臺(tái)的UploadControllerDisplayController應(yīng)該是我們最終生產(chǎn)的具體產(chǎn)品。所以新建兩個(gè)抽象產(chǎn)品接口。

UploadController 接口:

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午2:59
 */
public interface UploadController {
    void upload();
}

DisplayController 接口:

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午2:59
 */
public interface DisplayController {
    void display();
}

定義抽象工廠VideoPlayerFactory類,它能夠創(chuàng)建UploadControllerDisplayController

/**
 * 抽象工廠是主入口,在Spring中應(yīng)用的最廣泛的一種設(shè)計(jì)模式,易于擴(kuò)展
 *
 * @author eamon.zhang
 * @date 2019-09-27 下午3:04
 */
public interface VideoPlayerFactory {
    DisplayController createDisplayController();

    UploadController createUploadController();
}

然后在各個(gè)平臺(tái)創(chuàng)建具體的 UploadControllerDisplayController

創(chuàng)建適用于WindowsUploadControllerDisplayController

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午3:09
 */
public class WindowsUploadController implements UploadController {
    @Override
    public void upload() {
        System.out.println("Windows 上傳控制器!");
    }
}

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午3:09
 */
public class WindowsDisplayController implements DisplayController {

    @Override
    public void display() {
        System.out.println("Windows 上的播放器!");
    }
}

創(chuàng)建適用于IOSUploadControllerDisplayController

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午3:10
 */
public class IosUploaderController implements UploadController {
    @Override
    public void upload() {
        System.out.println("IOS 上傳控制器!");
    }
}

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午3:09
 */
public class IosDisplayController implements DisplayController {

    @Override
    public void display() {
        System.out.println("IOS 上的播放器!");
    }
}

創(chuàng)建適用于AndroidUploadControllerDisplayController

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午3:10
 */
public class AndroidUploaderController implements UploadController {
    @Override
    public void upload() {
        System.out.println("Android 上傳控制器!");
    }
}

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午3:09
 */
public class AndroidDisplayController implements DisplayController {

    @Override
    public void display() {
        System.out.println("Android 上的播放器!");
    }
}

在各平臺(tái)具體的工廠類中完成上傳控制器和播放控制器的創(chuàng)建過程:

創(chuàng)建WindowsFactory類:

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午3:15
 */
public class WindowsFactory implements VideoPlayerFactory {
    @Override
    public DisplayController createDisplayController() {
        return new WindowsDisplayController();
    }

    @Override
    public UploadController createUploadController() {
        return new WindowsUploadController();
    }
}

創(chuàng)建IosFactory類:

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午3:17
 */
public class IosFactory implements VideoPlayerFactory {
    @Override
    public DisplayController createDisplayController() {
        return new IosDisplayController();
    }

    @Override
    public UploadController createUploadController() {
        return new IosUploaderController();
    }
}

創(chuàng)建AndroidFactory類:

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午3:18
 */
public class AndroidFactory implements VideoPlayerFactory {
    @Override
    public DisplayController createDisplayController() {
        return new AndroidDisplayController();
    }

    @Override
    public UploadController createUploadController() {
        return new AndroidUploaderController();
    }
}

來看客戶端調(diào)用:

/**
 * @author eamon.zhang
 * @date 2019-09-27 下午3:20
 */
public class VideoPlayerFactoryTest {

    @Test
    public void VideoPlayer() {
        VideoPlayerFactory factory = new WindowsFactory();

        // IOS
//        factory = new IosFactory();
//        // Android
//        factory = new AndroidFactory();

        UploadController uploadController = factory.createUploadController();
        DisplayController displayController = factory.createDisplayController();

        uploadController.upload();
        displayController.display();

    }
}

以調(diào)用 Windows 為例,結(jié)果:

Windows 上傳控制器!
Windows 上的播放器!

上面就是針對(duì)不同平臺(tái)只通過創(chuàng)建對(duì)應(yīng)的工廠對(duì)象就完成了上傳控制器和播放控制器的創(chuàng)建。抽象工廠非常完美清晰地描述這樣一層復(fù)雜的關(guān)系。但是,不知道大家有沒有發(fā)現(xiàn),如果我們?cè)倮^續(xù)擴(kuò)展功能,將下載器也加入到產(chǎn)品中,那么我們的代碼從抽象工廠,到具體工廠要全部調(diào)整,很顯然不符合開閉原則。因此就有了上面優(yōu)缺點(diǎn)中所說的缺點(diǎn)。


總結(jié)

在實(shí)際應(yīng)用中,我們千萬不能犯強(qiáng)迫癥甚至有潔癖。在實(shí)際需求中產(chǎn)品等級(jí)結(jié)構(gòu)升級(jí)是非常正常的一件事情。我們可以根據(jù)實(shí)際情況,只要不是頻繁升級(jí),可以不遵循開閉原則。代碼每半年升級(jí)一次或者每年升級(jí)一次又有何不可呢?

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多