Java設(shè)計(jì)模式-創(chuàng)建型模式-抽象工廠模式

此系列文章為清華大學(xué)出版社出版劉偉編著《Java設(shè)計(jì)模式》的學(xué)習(xí)筆記髓削。

>>全部23種設(shè)計(jì)模式<<

1 概述

抽象工廠模式又成為工具(Kit)模式,它是一種對(duì)象創(chuàng)建型模式糟把。

1.1 抽象工廠模式概念

提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口皮获,而無(wú)需指定它們具體的類搓侄。

1.2 抽象工廠模式與工廠方法模式的區(qū)別

抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形式瞄桨。抽象工廠模式與工廠方法模式的最大區(qū)別在于,工廠方法模式針對(duì)的是一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)讶踪,而抽象工廠模式需要面對(duì)多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)讲婚,一個(gè)工廠等級(jí)結(jié)構(gòu)可以負(fù)責(zé)多個(gè)不同產(chǎn)品等級(jí)結(jié)構(gòu)中的產(chǎn)品對(duì)象的創(chuàng)建。當(dāng)一個(gè)工廠等級(jí)結(jié)構(gòu)可以創(chuàng)建出分屬于不同產(chǎn)品等級(jí)結(jié)構(gòu)的一個(gè)產(chǎn)品族中的所有對(duì)象時(shí)俊柔,抽象工廠模式比工廠方法模式更為簡(jiǎn)單、更有效率活合。

2 結(jié)構(gòu)與實(shí)現(xiàn)

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

抽象工廠模式包含4個(gè)角色

  1. AbstractFactory(抽象工廠):它聲明了一組用于創(chuàng)建一族產(chǎn)品的方法雏婶,每一個(gè)方法對(duì)應(yīng)一種產(chǎn)品。
  2. ConcreteFactory(具體工廠):它實(shí)現(xiàn)了在抽象工廠中聲明的創(chuàng)建產(chǎn)品的方法白指,生成一組具體產(chǎn)品留晚,這些產(chǎn)品構(gòu)成一個(gè)產(chǎn)品族,每一個(gè)產(chǎn)品都位于某個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)中告嘲。
  3. AbstractProduct(抽象產(chǎn)品):它為每種產(chǎn)品聲明接口错维,在抽象產(chǎn)品中聲明了產(chǎn)品所具備的業(yè)務(wù)方法。
  4. ConcreteProduct(具體產(chǎn)品):它定義具體工廠生產(chǎn)的具體產(chǎn)品對(duì)象橄唬,實(shí)現(xiàn)抽象產(chǎn)品接口中聲明的業(yè)務(wù)方法赋焕。

2.2 抽象工廠模式舉例

一、背景介紹

某軟件公司要開(kāi)發(fā)一套界面皮膚庫(kù)仰楚,可以基于Java的桌面軟件進(jìn)行界面美化隆判。用戶在使用時(shí)可以通過(guò)菜單來(lái)選擇皮膚,不同的皮膚將提供不同的組件視覺(jué)效果僧界。組件包括按鈕(Button)侨嘀、文本框(TextField)、組合框(ComboBox)等界面元素捂襟,皮膚分為 春天系皮膚和夏天系皮膚咬腕。要求該皮膚庫(kù)具備良好的靈活性和可擴(kuò)展性,用戶可以自由選擇不同的皮膚葬荷,開(kāi)發(fā)人員可以在不修改既有代碼的基礎(chǔ)上增加新的皮膚涨共,請(qǐng)使用抽象工廠模式來(lái)設(shè)計(jì)。

二闯狱、項(xiàng)目結(jié)構(gòu)

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

三煞赢、抽象產(chǎn)品

我們需要生產(chǎn)皮膚包括的組件有按鈕(Button)、文本框(TextField)哄孤、組合框(ComboBox)照筑。即使皮膚系列不同,它們提供的功能類型是一致的,只是視覺(jué)上不同凝危,對(duì)功能的實(shí)現(xiàn)不同波俄。所以有三個(gè)抽象接口Button、TextField蛾默、ComboBox懦铺。

public interface Button {
    public void display();
}
public interface TextField {
    public void display();
}
public interface ComboBox {
    public void display();
}

四、抽象工廠

我們面向接口編程支鸡,抽象工廠中創(chuàng)建的對(duì)象是抽象的對(duì)象冬念。

public interface SkinFactory {
    public Button createButton();
    public TextField createTextField();
    public ComboBox createComboBox();
}

五、具體產(chǎn)品

春天系皮膚組件

public class SpringButton implements Button {
    @Override
    public void display() {
        System.out.println("春天系皮膚【按鈕】");
    }
}
public class SpringTextField implements TextField {
    @Override
    public void display() {
        System.out.println("春天系皮膚【文本框】");
    }
}
public class SpringComboBox implements ComboBox {
    @Override
    public void display() {
        System.out.println("春天系皮膚【組合框】");
    }
}

夏天系皮膚組件

public class SummerButton implements Button {
    @Override
    public void display() {
        System.out.println("夏天系皮膚【按鈕】");
    }
}
public class SummerTextField implements TextField {
    @Override
    public void display() {
        System.out.println("夏天系皮膚【文本框】");
    }
}
public class SummerComboBox implements ComboBox {
    @Override
    public void display() {
        System.out.println("夏天系皮膚【組合框】");
    }
}

六牧挣、具體工廠

春天皮膚工廠

public class SpringSkinFactory implements SkinFactory {
    @Override
    public Button createButton() {
        return new SpringButton();
    }
    @Override
    public TextField createTextField() {
        return new SpringTextField();
    }
    @Override
    public ComboBox createComboBox() {
        return new SpringComboBox();
    }
}

夏天皮膚工廠

public class SummerSkinFactory implements SkinFactory {
    @Override
    public Button createButton() {
        return new SummerButton();
    }
    @Override
    public TextField createTextField() {
        return new SummerTextField();
    }
    @Override
    public ComboBox createComboBox() {
        return new SummerComboBox();
    }
}

七急前、XML確定皮膚類型

編寫config.xml

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <className>CreationalPattern.AbstractFactoryPattern.ConcreteFactory.SpringSkinFactory</className>
    <!--<className>CreationalPattern.AbstractFactoryPattern.ConcreteFactory.SummerSkinFactory</className>-->
</config>

八、編寫XMLUtil

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

public class XMLUtil {
    public static Object getBean(){
        try {
            //todo:創(chuàng)建 DOM 文檔對(duì)象
            DocumentBuilderFactory documentBuilderFactory= DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            Document doc;
            doc = documentBuilder.parse("src\\CreationalPattern\\AbstractFactoryPattern\\config.xml");
            //todo:獲取包含類名的文本節(jié)點(diǎn)
            NodeList nodeList = doc.getElementsByTagName("className");
            Node classNode = nodeList.item(0).getFirstChild();
            String cName = classNode.getNodeValue();
            //todo:通過(guò)類名生成實(shí)例對(duì)象并將其返回
            Class clz = Class.forName(cName);
            Object obj = clz.newInstance();
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

九瀑构、測(cè)試Client

public class Client {
    public static void main(String[] args) {
        //todo:抽象層定義
        SkinFactory skinFactory;
        Button button;
        TextField textField;
        ComboBox comboBox;
        skinFactory = (SkinFactory) XMLUtil.getBean();
        //todo:開(kāi)始生產(chǎn)
        button = skinFactory.createButton();
        textField = skinFactory.createTextField();
        comboBox = skinFactory.createComboBox();
        //todo:檢驗(yàn)生產(chǎn)結(jié)果
        button.display();
        textField.display();
        comboBox.display();
    }
}

十裆针、打印結(jié)果

當(dāng)config.xml中的className節(jié)點(diǎn)值為CreationalPattern.AbstractFactoryPattern.ConcreteFactory.SpringSkinFactory時(shí),抽象工廠類的實(shí)現(xiàn)類為SpringFactory寺晌,生產(chǎn)的組件檢驗(yàn)結(jié)果如下:

春天系工廠生產(chǎn)組件檢驗(yàn).png

當(dāng)config.xml中的className節(jié)點(diǎn)值為CreationalPattern.AbstractFactoryPattern.ConcreteFactory.SummerSkinFactory時(shí)世吨,抽象工廠類的實(shí)現(xiàn)類為SummerFactory,生產(chǎn)的組件檢驗(yàn)結(jié)果如下:

夏天系工廠生產(chǎn)組件檢驗(yàn).png

3 總結(jié)

3.1 實(shí)現(xiàn)過(guò)程梳理

在測(cè)試的main()方法中呻征,首先定義了所有的頂級(jí)抽象對(duì)象耘婚,抽象的skinFactory、抽象的button怕犁、抽象的textField边篮、抽象的comboBox。然后奏甫,我們通過(guò)讀取靜態(tài)的XML文件戈轿,獲取具體工廠的實(shí)現(xiàn),是春天系皮膚工廠或是夏天系皮膚工廠阵子,這里的XML文件僅僅是提供類名的一個(gè)作用思杯,完全可以用字符串直接完成。具體的工廠生產(chǎn)的是具體的組件挠进,調(diào)用他們的方法色乾,將產(chǎn)生不同的效果。

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

  1. 抽象工廠模式隔離了具體類的生成领突,使得客戶端并不需要知道什么被創(chuàng)建暖璧。由于這種隔離,更換一個(gè)具體工廠變得容易君旦,所有的具體工廠都實(shí)現(xiàn)了抽象工廠中定義的接口方法澎办,因此只需要改變具體工廠的實(shí)例就可以在某種程度上改變整個(gè)軟件系統(tǒng)的行為細(xì)節(jié)嘲碱。
  2. 當(dāng)一個(gè)產(chǎn)品族中的多個(gè)對(duì)象被設(shè)計(jì)成一起工作時(shí),它能夠保證客戶端始終只使用同一個(gè)產(chǎn)品族中的對(duì)象局蚀。
  3. 增加新的產(chǎn)品族很方便麦锯,無(wú)需修改已有系統(tǒng),符合開(kāi)閉原則琅绅。

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

  • 抽象工廠模式的缺點(diǎn)是:增加新的產(chǎn)品結(jié)構(gòu)很麻煩扶欣,需要對(duì)原有系統(tǒng)進(jìn)行較大的修改,甚至需要修改抽象層代碼千扶,違背了開(kāi)閉原則料祠。

3.4 抽象工廠模式使用場(chǎng)景

  1. 一個(gè)系統(tǒng)不應(yīng)該依賴于產(chǎn)品類實(shí)例如何被創(chuàng)建、組合和表達(dá)的細(xì)節(jié)澎羞,這對(duì)于所有類型的工廠模式都是很重要的术陶,用戶無(wú)需關(guān)心對(duì)象的創(chuàng)建過(guò)程,將對(duì)象的創(chuàng)建和使用解耦煤痕。
  2. 系統(tǒng)中有多于一個(gè)的產(chǎn)品族,而每次只使用其中某一個(gè)產(chǎn)品族接谨“诘铮可以通過(guò)配置文件等方式來(lái)使用戶能夠動(dòng)態(tài)改變產(chǎn)品族,也可以很方便地增加新的產(chǎn)品族脓豪。
  3. 屬于同一個(gè)產(chǎn)品族地產(chǎn)品將在一起使用巷帝,這一約束必須在系統(tǒng)地設(shè)計(jì)中體現(xiàn)出來(lái)。同一個(gè)產(chǎn)品族中的產(chǎn)品可以是沒(méi)有任何關(guān)系的對(duì)象扫夜,但是它們都具有一些共同的約束楞泼,如同易操作系統(tǒng)下的按鈕和文本框,按鈕與文本框之間沒(méi)有直接關(guān)系笤闯,但它們都是屬于某一操作系統(tǒng)的堕阔,此時(shí)具有一個(gè)共同的約束條件,及操作系統(tǒng)的類型颗味。
  4. 產(chǎn)品等級(jí)結(jié)構(gòu)穩(wěn)定超陆,在設(shè)計(jì)完成之后不會(huì)向系統(tǒng)中增加新的產(chǎn)品等級(jí)結(jié)構(gòu)或者刪除已經(jīng)有的產(chǎn)品等級(jí)結(jié)構(gòu)。

>>全部23種設(shè)計(jì)模式<<

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浦马,一起剝皮案震驚了整個(gè)濱河市时呀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晶默,老刑警劉巖谨娜,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異磺陡,居然都是意外死亡趴梢,警方通過(guò)查閱死者的電腦和手機(jī)漠畜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)垢油,“玉大人盆驹,你說(shuō)我怎么就攤上這事√渤睿” “怎么了躯喇?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)硝枉。 經(jīng)常有香客問(wèn)我廉丽,道長(zhǎng),這世上最難降的妖魔是什么妻味? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任正压,我火速辦了婚禮,結(jié)果婚禮上责球,老公的妹妹穿的比我還像新娘焦履。我一直安慰自己,他們只是感情好雏逾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布嘉裤。 她就那樣靜靜地躺著,像睡著了一般栖博。 火紅的嫁衣襯著肌膚如雪屑宠。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天仇让,我揣著相機(jī)與錄音典奉,去河邊找鬼。 笑死丧叽,一個(gè)胖子當(dāng)著我的面吹牛卫玖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播踊淳,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼骇笔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了嚣崭?” 一聲冷哼從身側(cè)響起笨触,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雹舀,沒(méi)想到半個(gè)月后芦劣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡说榆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年虚吟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寸认。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡串慰,死狀恐怖偏塞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情邦鲫,我是刑警寧澤灸叼,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站庆捺,受9級(jí)特大地震影響古今,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜滔以,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一捉腥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧你画,春花似錦抵碟、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至剥槐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宪摧,已是汗流浹背粒竖。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留几于,地道東北人蕊苗。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像沿彭,于是被迫代替她去往敵國(guó)和親朽砰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容