Java抽象工廠模式

概述

每一個(gè)模式都是針對(duì)一定問(wèn)題的解決方案。抽象工廠模式與工廠方法模式的最大區(qū)別就在于,工廠方法模式針對(duì)的是一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu);而抽象工廠模式則需要面對(duì)多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)。
  在學(xué)習(xí)抽象工廠具體實(shí)例之前,應(yīng)該明白兩個(gè)重要的概念:產(chǎn)品族和產(chǎn)品等級(jí)。
  所謂產(chǎn)品族,是指位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中,功能相關(guān)聯(lián)的產(chǎn)品組成的家族。比如AMD的主板、芯片組、CPU組成一個(gè)家族,Intel的主板、芯片組、CPU組成一個(gè)家族。而這兩個(gè)家族都來(lái)自于三個(gè)產(chǎn)品等級(jí):主板、芯片組、CPU。一個(gè)等級(jí)結(jié)構(gòu)是由相同的結(jié)構(gòu)的產(chǎn)品組成,示意圖如下:



  顯然,每一個(gè)產(chǎn)品族中含有產(chǎn)品的數(shù)目,與產(chǎn)品等級(jí)結(jié)構(gòu)的數(shù)目是相等的。產(chǎn)品的等級(jí)結(jié)構(gòu)與產(chǎn)品族將產(chǎn)品按照不同方向劃分,形成一個(gè)二維的坐標(biāo)系。橫軸表示產(chǎn)品的等級(jí)結(jié)構(gòu),縱軸表示產(chǎn)品族,上圖共有兩個(gè)產(chǎn)品族,分布于三個(gè)不同的產(chǎn)品等級(jí)結(jié)構(gòu)中。只要指明一個(gè)產(chǎn)品所處的產(chǎn)品族以及它所屬的等級(jí)結(jié)構(gòu),就可以唯一的確定這個(gè)產(chǎn)品。
  上面所給出的三個(gè)不同的等級(jí)結(jié)構(gòu)具有平行的結(jié)構(gòu)。因此,如果采用工廠方法模式,就勢(shì)必要使用三個(gè)獨(dú)立的工廠等級(jí)結(jié)構(gòu)來(lái)對(duì)付這三個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)。由于這三個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)的相似性,會(huì)導(dǎo)致三個(gè)平行的工廠等級(jí)結(jié)構(gòu)。隨著產(chǎn)品等級(jí)結(jié)構(gòu)的數(shù)目的增加,工廠方法模式所給出的工廠等級(jí)結(jié)構(gòu)的數(shù)目也會(huì)隨之增加。如下圖:



  那么,是否可以使用同一個(gè)工廠等級(jí)結(jié)構(gòu)來(lái)對(duì)付這些相同或者極為相似的產(chǎn)品等級(jí)結(jié)構(gòu)呢?當(dāng)然可以的,而且這就是抽象工廠模式的好處。同一個(gè)工廠等級(jí)結(jié)構(gòu)負(fù)責(zé)三個(gè)不同產(chǎn)品等級(jí)結(jié)構(gòu)中的產(chǎn)品對(duì)象的創(chuàng)建。

  可以看出,一個(gè)工廠等級(jí)結(jié)構(gòu)可以創(chuàng)建出分屬于不同產(chǎn)品等級(jí)結(jié)構(gòu)的一個(gè)產(chǎn)品族中的所有對(duì)象。顯然,這時(shí)候抽象工廠模式比簡(jiǎn)單工廠模式、工廠方法模式更有效率。對(duì)應(yīng)于每一個(gè)產(chǎn)品族都有一個(gè)具體工廠。而每一個(gè)具體工廠負(fù)責(zé)創(chuàng)建屬于同一個(gè)產(chǎn)品族,但是分屬于不同等級(jí)結(jié)構(gòu)的產(chǎn)品。

抽象工廠模式結(jié)構(gòu)

抽象工廠模式是對(duì)象的創(chuàng)建模式,它是工廠方法模式的進(jìn)一步推廣。
  假設(shè)一個(gè)子系統(tǒng)需要一些產(chǎn)品對(duì)象,而這些產(chǎn)品又屬于一個(gè)以上的產(chǎn)品等級(jí)結(jié)構(gòu)。那么為了將消費(fèi)這些產(chǎn)品對(duì)象的責(zé)任和創(chuàng)建這些產(chǎn)品對(duì)象的責(zé)任分割開(kāi)來(lái),可以引進(jìn)抽象工廠模式。這樣的話,消費(fèi)產(chǎn)品的一方不需要直接參與產(chǎn)品的創(chuàng)建工作,而只需要向一個(gè)公用的工廠接口請(qǐng)求所需要的產(chǎn)品。
  通過(guò)使用抽象工廠模式,可以處理具有相同(或者相似)等級(jí)結(jié)構(gòu)中的多個(gè)產(chǎn)品族中的產(chǎn)品對(duì)象的創(chuàng)建問(wèn)題。如下圖所示:

  

  由于這兩個(gè)產(chǎn)品族的等級(jí)結(jié)構(gòu)相同,因此使用同一個(gè)工廠族也可以處理這兩個(gè)產(chǎn)品族的創(chuàng)建問(wèn)題,這就是抽象工廠模式。
  根據(jù)產(chǎn)品角色的結(jié)構(gòu)圖,就不難給出工廠角色的結(jié)構(gòu)設(shè)計(jì)圖。

  可以看出,每一個(gè)工廠角色都有兩個(gè)工廠方法,分別負(fù)責(zé)創(chuàng)建分屬不同產(chǎn)品等級(jí)結(jié)構(gòu)的產(chǎn)品對(duì)象。

  

源代碼

/**
 * 處理器產(chǎn)品接口
 */
public interface Cpu {

    public void showMsg();

}

/**
 * 主板產(chǎn)品接口
 */
public interface Mainboard {

    public void showMsg();

}

/**
 * 具體的產(chǎn)品,intel處理器
 */
public class IntelCpu implements Cpu {
    @Override
    public void showMsg() {
        System.out.println("處理器:intel");
    }
}

/**
 * 具體的產(chǎn)品,amd處理器
 */
public class AmdCpu implements Cpu {
    @Override
    public void showMsg() {
        System.out.println("處理器:amd");
    }
}

/**
 * 具體的產(chǎn)品,intel主板
 */
public class IntelMainboard implements Mainboard {
    @Override
    public void showMsg() {
        System.out.println("主板:intel");
    }
}

/**
 * 具體的產(chǎn)品,amd主板
 */
public class AmdMainboard implements Mainboard {
    @Override
    public void showMsg() {
        System.out.println("主板:amd");
    }
}
/**
 * 抽象工廠
 */
public interface AbstractFactory {

    /**
     * 創(chuàng)建cpu
     * @return
     */
    public Cpu createCpu();

    /**
     * 創(chuàng)建主板
     * @return
     */
    public Mainboard createMainboard();

}

/**
 * 具體的工廠,amd產(chǎn)品族工廠
 */
public class AmdFactory implements AbstractFactory {
    @Override
    public Cpu createCpu() {
        return new AmdCpu();
    }

    @Override
    public Mainboard createMainboard() {
        return new AmdMainboard();
    }
}


/**
 * 具體的工廠,intel產(chǎn)品族工廠
 */
public class IntelFactory implements AbstractFactory {
    @Override
    public Cpu createCpu() {
        return new IntelCpu();
    }

    @Override
    public Mainboard createMainboard() {
        return new IntelMainboard();
    }
}
/**
 * 客戶(hù)端
 */
public class Client {

    public static void main(String[] args) {
        //amd工廠,若要生產(chǎn)intel的產(chǎn)品,只需在new 工廠時(shí)改為 new intelFactory即可
        AbstractFactory factory = new AmdFactory();
        Cpu cpu = factory.createCpu();
        Mainboard mainboard = factory.createMainboard();
        cpu.showMsg();
        mainboard.showMsg();
    }

}

抽象工廠的功能是為一系列相關(guān)對(duì)象或相互依賴(lài)的對(duì)象創(chuàng)建一個(gè)接口。一定要注意,這個(gè)接口內(nèi)的方法不是任意堆砌的,而是一系列相關(guān)或相互依賴(lài)的方法。比如上面例子中的主板和CPU,都是為了組裝一臺(tái)電腦的相關(guān)對(duì)象。不同的裝機(jī)方案,代表一種具體的電腦系列。

    
由于抽象工廠定義的一系列對(duì)象通常是相關(guān)或相互依賴(lài)的,這些產(chǎn)品對(duì)象就構(gòu)成了一個(gè)產(chǎn)品族,也就是抽象工廠定義了一個(gè)產(chǎn)品族。
這就帶來(lái)非常大的靈活性,切換產(chǎn)品族的時(shí)候,只要提供不同的抽象工廠實(shí)現(xiàn)就可以了,也就是說(shuō)現(xiàn)在是以一個(gè)產(chǎn)品族作為一個(gè)整體被切換。

在什么情況下應(yīng)當(dāng)使用抽象工廠模式
  1.一個(gè)系統(tǒng)不應(yīng)當(dāng)依賴(lài)于產(chǎn)品類(lèi)實(shí)例如何被創(chuàng)建、組合和表達(dá)的細(xì)節(jié),這對(duì)于所有形態(tài)的工廠模式都是重要的。
  2.這個(gè)系統(tǒng)的產(chǎn)品有多于一個(gè)的產(chǎn)品族,而系統(tǒng)只消費(fèi)其中某一族的產(chǎn)品。
  3.同屬于同一個(gè)產(chǎn)品族的產(chǎn)品是在一起使用的,這一約束必須在系統(tǒng)的設(shè)計(jì)中體現(xiàn)出來(lái)。(比如:Intel主板必須使用Intel CPU、Intel芯片組)****
  4.系統(tǒng)提供一個(gè)產(chǎn)品類(lèi)的庫(kù),所有的產(chǎn)品以同樣的接口出現(xiàn),從而使客戶(hù)端不依賴(lài)于實(shí)現(xiàn)。

抽象工廠模式的起源

抽象工廠模式的起源或者最早的應(yīng)用,是用于創(chuàng)建分屬于不同操作系統(tǒng)的視窗構(gòu)建。比如:命令按鍵(Button)與文字框(Text)都是視窗構(gòu)建,在UNIX操作系統(tǒng)的視窗環(huán)境和Windows操作系統(tǒng)的視窗環(huán)境中,這兩個(gè)構(gòu)建有不同的本地實(shí)現(xiàn),它們的細(xì)節(jié)有所不同。
  在每一個(gè)操作系統(tǒng)中,都有一個(gè)視窗構(gòu)建組成的構(gòu)建家族。在這里就是Button和Text組成的產(chǎn)品族。而每一個(gè)視窗構(gòu)件都構(gòu)成自己的等級(jí)結(jié)構(gòu),由一個(gè)抽象角色給出抽象的功能描述,而由具體子類(lèi)給出不同操作系統(tǒng)下的具體實(shí)現(xiàn)。


 
 
可以發(fā)現(xiàn)在上面的產(chǎn)品類(lèi)圖中,有兩個(gè)產(chǎn)品的等級(jí)結(jié)構(gòu),分別是Button等級(jí)結(jié)構(gòu)和Text等級(jí)結(jié)構(gòu)。同時(shí)有兩個(gè)產(chǎn)品族,也就是UNIX產(chǎn)品族和Windows產(chǎn)品族。UNIX產(chǎn)品族由UNIX Button和UNIX Text產(chǎn)品構(gòu)成;而Windows產(chǎn)品族由Windows Button和Windows Text產(chǎn)品構(gòu)成。


系統(tǒng)對(duì)產(chǎn)品對(duì)象的創(chuàng)建需求由一個(gè)工程的等級(jí)結(jié)構(gòu)滿足,其中有兩個(gè)具體工程角色,即UnixFactory和WindowsFactory。UnixFactory對(duì)象負(fù)責(zé)創(chuàng)建Unix產(chǎn)品族中的產(chǎn)品,而WindowsFactory對(duì)象負(fù)責(zé)創(chuàng)建Windows產(chǎn)品族中的產(chǎn)品。這就是抽象工廠模式的應(yīng)用,抽象工廠模式的解決方案如下圖:


顯然,一個(gè)系統(tǒng)只能夠在某一個(gè)操作系統(tǒng)的視窗環(huán)境下運(yùn)行,而不能同時(shí)在不同的操作系統(tǒng)上運(yùn)行。所以,系統(tǒng)實(shí)際上只能消費(fèi)屬于同一個(gè)產(chǎn)品族的產(chǎn)品。

在現(xiàn)代的應(yīng)用中,抽象工廠模式的使用范圍已經(jīng)大大擴(kuò)大了,不再要求系統(tǒng)只能消費(fèi)某一個(gè)產(chǎn)品族了。因此,可以不必理會(huì)前面所提到的原始用意。

抽象工廠模式的優(yōu)點(diǎn)

分離接口和實(shí)現(xiàn)
  客戶(hù)端使用抽象工廠來(lái)創(chuàng)建需要的對(duì)象,而客戶(hù)端根本就不知道具體的實(shí)現(xiàn)是誰(shuí),客戶(hù)端只是面向產(chǎn)品的接口編程而已。也就是說(shuō),客戶(hù)端從具體的產(chǎn)品實(shí)現(xiàn)中解耦。
使切換產(chǎn)品族變得容易
  因?yàn)橐粋€(gè)具體的工廠實(shí)現(xiàn)代表的是一個(gè)產(chǎn)品族,比如上面例子的從Intel系列到AMD系列只需要切換一下具體工廠。

抽象工廠模式的缺點(diǎn)

不太容易擴(kuò)展新的產(chǎn)品
  如果需要給整個(gè)產(chǎn)品族添加一個(gè)新的產(chǎn)品,那么就需要修改抽象工廠,這樣就會(huì)導(dǎo)致修改所有的工廠實(shí)現(xiàn)類(lèi)。

抽象工廠和工廠方法的區(qū)別

抽象工廠模式與工廠方法模式的最大區(qū)別就在于,工廠方法模式針對(duì)的是一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu);而抽象工廠模式則需要面對(duì)多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)。

在什么情況下應(yīng)當(dāng)使用抽象工廠模式:
這個(gè)系統(tǒng)的產(chǎn)品有多于一個(gè)的產(chǎn)品族,而系統(tǒng)只消費(fèi)其中某一族的產(chǎn)品。比如XX應(yīng)用系統(tǒng)有快捷版跟標(biāo)準(zhǔn)版兩個(gè)版本,這就是兩個(gè)產(chǎn)品族,而具體使用哪一個(gè)產(chǎn)品族是實(shí)施人員在部署的時(shí)候設(shè)置后臺(tái)參數(shù)來(lái)決定的。

同屬于同一個(gè)產(chǎn)品族的產(chǎn)品是在一起使用的,這一約束必須在系統(tǒng)的設(shè)計(jì)中體現(xiàn)出來(lái)。比如:快捷版數(shù)據(jù)保存在XML文件中,而標(biāo)準(zhǔn)版數(shù)據(jù)保存在數(shù)據(jù)庫(kù)中。將UserDAO和DeptDAO看作是這兩個(gè)版本(產(chǎn)品族)擁有的產(chǎn)品,而抽象工廠有兩個(gè)具體工廠實(shí)現(xiàn)類(lèi)ShortcutFactory、StandardFactory負(fù)責(zé)分別創(chuàng)建快捷版ShortcutUserDAO、ShortcutDeptDAO和標(biāo)準(zhǔn)版StandardUserDAO、StandardDeptDAO。當(dāng)用戶(hù)操作刪除一個(gè)部門(mén)的時(shí)候,先需要?jiǎng)h除這個(gè)部門(mén)下的所有用戶(hù)。假如不使用抽象工廠可能會(huì)出現(xiàn)如下情況:后臺(tái)刪除用戶(hù)時(shí)調(diào)用的是快捷版的ShortcutUserDAO類(lèi),刪除部門(mén)時(shí)調(diào)用的是StandardDeptDAO類(lèi)。這樣就會(huì)出現(xiàn)問(wèn)題。
如果使用抽象工廠,那抽象工廠的具體實(shí)現(xiàn)類(lèi)會(huì)幫你創(chuàng)建一個(gè)產(chǎn)品族中的一系列產(chǎn)品對(duì)象,如標(biāo)準(zhǔn)版的StandardFactory工廠會(huì)創(chuàng)建StandardUserDAO、StandardDeptDAO,快捷版的ShortcutFactory工廠會(huì)創(chuàng)建ShortcutUserDAO、ShortcutDeptDAO。
其實(shí)抽象工廠就是起到了一定的約束作用,它所創(chuàng)建的都是同一個(gè)產(chǎn)品族中的一系列產(chǎn)品對(duì)象。防止出現(xiàn)上面例子中創(chuàng)建不同產(chǎn)品族中產(chǎn)品所帶來(lái)的問(wèn)題。

總結(jié)

無(wú)論是簡(jiǎn)單工廠模式,工廠方法模式,還是抽象工廠模式,他們都屬于工廠模式,在形式和特點(diǎn)上也是極為相似的,他們的最終目的都是為了解耦。在使用時(shí),我們不必去在意這個(gè)模式到底工廠方法模式還是抽象工廠模式,因?yàn)樗麄冎g的演變常常是令人琢磨不透的。經(jīng)常你會(huì)發(fā)現(xiàn),明明使用的工廠方法模式,當(dāng)新需求來(lái)臨,稍加修改,加入了一個(gè)新方法后,由于類(lèi)中的產(chǎn)品構(gòu)成了不同等級(jí)結(jié)構(gòu)中的產(chǎn)品族,它就變成抽象工廠模式了;而對(duì)于抽象工廠模式,當(dāng)減少一個(gè)方法使的提供的產(chǎn)品不再構(gòu)成產(chǎn)品族之后,它就演變成了工廠方法模式。
所以,在使用工廠模式時(shí),只需要關(guān)心降低耦合度的目的是否達(dá)到了。

最后編輯于
?著作權(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)容

  • 工廠方法模式通過(guò)引入工廠等級(jí)結(jié)構(gòu),解決了簡(jiǎn)單工廠模式中工廠類(lèi)職責(zé)太重的問(wèn)題,但由于工廠方法模式中的每個(gè)工廠只生產(chǎn)一...
    justCode_閱讀 1,298評(píng)論 1 6
  • 設(shè)計(jì)原則: 要依賴(lài)抽象,不要依賴(lài)具體類(lèi) 目錄 本文的結(jié)構(gòu)如下: 什么是抽象工廠模式 為什么要用該模式 模式的結(jié)構(gòu) ...
    w1992wishes閱讀 1,239評(píng)論 0 6
  • 簡(jiǎn)單工廠模式 工廠模式我的理解是:他就是為了創(chuàng)建對(duì)象的 創(chuàng)建對(duì)象的時(shí)候,我們一般是alloc一個(gè)對(duì)象,如果需要?jiǎng)?chuàng)建...
    GitHubPorter閱讀 8,226評(píng)論 6 16
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 4,094評(píng)論 1 15
  • 想贏?堅(jiān)持?主動(dòng)? 想贏嗎?是的,我想贏,想了已經(jīng)不止n次了,已經(jīng)到n+n+n+n……次了。 可以光想有用嗎?好像...
    肖輝閱讀 215評(píng)論 0 0

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