此系列文章為清華大學(xué)出版社出版劉偉編著《Java設(shè)計(jì)模式》的學(xué)習(xí)筆記髓削。
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è)角色:
- AbstractFactory(抽象工廠):它聲明了一組用于創(chuàng)建一族產(chǎn)品的方法雏婶,每一個(gè)方法對(duì)應(yīng)一種產(chǎn)品。
- 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)中告嘲。
- AbstractProduct(抽象產(chǎn)品):它為每種產(chǎn)品聲明接口错维,在抽象產(chǎn)品中聲明了產(chǎn)品所具備的業(yè)務(wù)方法。
- 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)
三煞赢、抽象產(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é)果如下:
當(dāng)config.xml中的className節(jié)點(diǎn)值為CreationalPattern.AbstractFactoryPattern.ConcreteFactory.SummerSkinFactory時(shí)世吨,抽象工廠類的實(shí)現(xiàn)類為SummerFactory,生產(chǎn)的組件檢驗(yàn)結(jié)果如下:
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)
- 抽象工廠模式隔離了具體類的生成领突,使得客戶端并不需要知道什么被創(chuàng)建暖璧。由于這種隔離,更換一個(gè)具體工廠變得容易君旦,所有的具體工廠都實(shí)現(xiàn)了抽象工廠中定義的接口方法澎办,因此只需要改變具體工廠的實(shí)例就可以在某種程度上改變整個(gè)軟件系統(tǒng)的行為細(xì)節(jié)嘲碱。
- 當(dāng)一個(gè)產(chǎn)品族中的多個(gè)對(duì)象被設(shè)計(jì)成一起工作時(shí),它能夠保證客戶端始終只使用同一個(gè)產(chǎn)品族中的對(duì)象局蚀。
- 增加新的產(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)景
- 一個(gè)系統(tǒng)不應(yīng)該依賴于產(chǎn)品類實(shí)例如何被創(chuàng)建、組合和表達(dá)的細(xì)節(jié)澎羞,這對(duì)于所有類型的工廠模式都是很重要的术陶,用戶無(wú)需關(guān)心對(duì)象的創(chuàng)建過(guò)程,將對(duì)象的創(chuàng)建和使用解耦煤痕。
- 系統(tǒng)中有多于一個(gè)的產(chǎn)品族,而每次只使用其中某一個(gè)產(chǎn)品族接谨“诘铮可以通過(guò)配置文件等方式來(lái)使用戶能夠動(dòng)態(tài)改變產(chǎn)品族,也可以很方便地增加新的產(chǎn)品族脓豪。
- 屬于同一個(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)的類型颗味。
- 產(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)。