前言
本篇是設計模式學習筆記的其中一篇文章,如對其他模式有興趣,可從該地址查詢設計模式學習筆記彙總地址
1. 抽象工廠模式概述
工廠方法模式通過引入工廠等級結構,解決了簡單工廠模式中工廠類職責太重的問題,
但由於工廠方法模式中的每個工廠只生產一類產品,可能會導致系統中存在大量的工廠類,勢必會增加系統的開銷.
此時,我們可以考慮將一些相關的產品組成一個"產品族",由同一個工廠來統一生產.這就是抽象工廠模式的基本思想.
2. 模擬業務
開發一套介面皮膚庫,使用者在使用時可以通過選單來選擇皮膚,不同的皮膚將提供視覺效果不同的按鈕、文字框、組合框等介面元素.
2.1 結構圖
2.2 問題
使用大量工廠來建立具體的介面元件,可以通過配置檔案更換具體介面元件從而改變介面風格.
但是此設計方案存在如下問題:
- 增加系統的維護成本和執行開銷
- 客戶端程式碼和配置檔案較為複雜
2.2.1 增加系統的維護成本和執行開銷
當需要增加新的皮膚時,雖然不要修改現有程式碼,但是需要增加大量類,針對每一個新增具體元件都需要增加一個具體工廠,類的個數成對增加,這無疑會導致系統越來越龐大,增加系統的維護成本和執行開銷.
2.2.2 客戶端程式碼和配置檔案較為複雜
由於同一種風格的具體介面元件通常要一起顯示,因此需要為每個元件都選擇一個具體工廠,使用者在使用時必須逐個進行設定,如果某個具體工廠選擇失誤將會導致介面顯示混亂,雖然我們可以適當增加一些約束語句,但客戶端程式碼和配置檔案都較為複雜。
3. 產品等級結構與產品族
在工廠方法模式中具體工廠負責生產具體的產品,每一個具體工廠對應一種具體產品,工廠方法具有唯一性,一般情況下,一個具體工廠中只有一個或者一組過載的工廠方法
但是有時候我們希望一個工廠可以提供多個產品物件,而不是單一的產品物件,如一個電器工廠,它可以生產電視機、電冰箱、空調等多種電器,而不是隻生產某一種電器。
為了更好地理解抽象工廠模式,我們先引入兩個概念:
- 產品等級結構
- 產品族
3.1 產品等級結構
產品等級結構即產品的繼承結構,如一個抽象類是電視機,其子類有海爾電視機、海信電視機、TCL電視機,則抽象電視機與具體品牌的電視機之間構成了一個產品等級結構,抽象電視機是父類,而具體品牌的電視機是其子類。
3.2 產品族
在抽象工廠模式中,產品族是指由同一個工廠生產的,位於不同產品等級結構中的一組產品.
如海爾電器工廠生產的海爾電視機、海爾電冰箱,海爾電視機位於電視機產品等級結構中,海爾電冰箱位於電冰箱產品等級結構中,海爾電視機、海爾電冰箱構成了一個產品族。
4. 抽象工廠模式與工廠方法模式的區別
當系統所提供的工廠生產的具體產品並不是一個簡單的物件,而是多個位於不同產品等級結構、屬於不同型別的具體產品時就可以使用抽象工廠模式。
抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形式。
抽象工廠模式與工廠方法模式最大的區別在於,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式需要面對多個產品等級結構,一個工廠等級結構可以負責多個不同產品等級結構中的產品物件的建立。當一個工廠等級結構可以建立出分屬於不同產品等級結構的一個產品族中的所有物件時,抽象工廠模式比工廠方法模式更為簡單、更有效率.
需要建立一個產品族的物件到時候,相較於工廠方法模式,抽象工廠模式可以極大的減少系統中類的個數
5. 抽象工廠模式
抽象工廠模式為建立一組物件提供了一種解決方案。與工廠方法模式相比,抽象工廠模式中的具體工廠不只是建立一種產品,它負責建立一族產品
5.1 定義
抽象工廠模式(Abstract Factory Pattern):提供一個建立一系列相關或相互依賴物件的介面,而無須指定它們具體的類。抽象工廠模式又稱為Kit模式,它是一種物件建立型模式。
5.2 結構圖
在抽象工廠模式中,每一個具體工廠都提供了多個工廠方法用於產生多種不同型別的產品,
這些產品構成了一個產品族,抽象工廠模式結構圖:
5.3 角色
在抽象工廠模式結構圖中包含如下幾個角色:
- AbstractFactory(抽象工廠): 它宣告瞭一組用於建立一族產品的方法,每一個方法對應一種產品。
- ConcreteFactory(具體工廠: 它實現了在抽象工廠中宣告的建立產品的方法,生成一組具體產品,這些產品構成了一個產品族,每一個產品都位於某個產品等級結構中。
- AbstractProduct(抽象產品): 它為每種產品宣告介面,在抽象產品中宣告瞭產品所具有的業務方法。
- ConcreteProduct(具體產品: 它定義具體工廠生產的具體產品物件,實現抽象產品介面中宣告的業務方法
5.4 程式碼
AbstractFactory(抽象工廠): 在抽象工廠中宣告瞭多個工廠方法,用於建立不同型別的產品,抽象工廠可以是介面,也可以是抽象類或者具體類
/**
* @author liuboren
* @Title: 抽象工廠
* @Description:
* @date 2019/7/16 13:30
*/
public abstract class AbstractFactory {
//工廠方法一
public abstract AbstractProductA createProductA();
//工廠方法二
public abstract AbstractProductB createProductB();
}
ConcreteFactory(具體工廠): 具體工廠實現了抽象工廠,每一個具體的工廠方法可以返回一個特定的產品物件,而同一個具體工廠所建立的產品物件構成了一個產品族
/**
* @author liuboren
* @Title: 具體工廠類
* @Description:
* @date 2019/7/16 13:32
*/
public class ConcreteFactory1 extends AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
}
AbstractProduct(抽象產品):
/**
* @author liuboren
* @Title: 抽象產品A
* @Description:
* @date 2019/7/16 13:31
*/
public class AbstractProductA {
}
ConcreteProduct(具體產品):
/**
* @author liuboren
* @Title: 具體產品A1
* @Description:
* @date 2019/7/16 13:32
*/
public class ConcreteProductA1 extends AbstractProductA {
}
6. 使用抽象工廠模式解決業務問題
結構圖:
/**
* @author liuboren
* @Title: 客戶端類
* @Description:
* @date 2019/7/16 13:48
*/
public class Client {
public static void main(String[] args) {
SkinFactory skinFactory = new SummerSkinFactory();
Button button = skinFactory.createButton();
ComboBox comboBox = skinFactory.createComboBox();
TextField textField = skinFactory.createTextField();
button.display();
comboBox.display();
textField.display();
}
}
/*
* 按鈕介面
* */
interface Button {
void display();
}
//Spring按鈕類:具體產品
class SpringButton implements Button {
@Override
public void display() {
System.out.println("顯示淺綠色按鈕。");
}
}
//Summer按鈕類:具體產品
class SummerButton implements Button {
@Override
public void display() {
System.out.println("顯示淺藍色按鈕。");
}
}
//文字框介面:抽象產品
interface TextField {
void display();
}
//Spring文字框類:具體產品
class SpringTextField implements TextField {
@Override
public void display() {
System.out.println("顯示綠色邊框文字框。");
}
}
//Summer文字框類:具體產品
class SummerTextField implements TextField {
@Override
public void display() {
System.out.println("顯示藍色邊框文字框。");
}
}
//組合框介面:抽象產品
interface ComboBox {
public void display();
}
//Spring組合框類:具體產品
class SpringComboBox implements ComboBox {
@Override
public void display() {
System.out.println("顯示綠色邊框組合框。");
}
}
//Summer組合框類:具體產品
class SummerComboBox implements ComboBox {
@Override
public void display() {
System.out.println("顯示藍色邊框組合框。");
}
}
//介面皮膚工廠介面:抽象工廠
interface SkinFactory {
Button createButton();
TextField createTextField();
ComboBox createComboBox();
}
//Spring皮膚工廠:具體工廠
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();
}
}
//Summer皮膚工廠:具體工廠
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動態生成工廠類,參見上一篇部落格的3.3.3節,地址: 工廠模式
7. "開閉原則"的傾斜性
抽象工廠模式增加新的產品族很方便(通過建立新的工廠實現類和產品實現類),但是增加新的產品等級結構很麻煩(需在抽象工廠模式中新增新的方法,違背了"開閉原則")
抽象工廠模式的這種性質稱為“開閉原則”的傾斜性。“開閉原則”要求系統對擴充套件開放,對修改封閉,通過擴充套件達到增強其功能的目的,對於涉及到多個產品族與多個產品等級結構的系統,其功能增強包括兩方面:
- 增加產品族
- 增加新的產品登記結構
7.1 增加產品族
對於增加新的產品族,抽象工廠模式很好地支援了“開閉原則”,只需要增加具體產品並對應增加一個新的具體工廠,對已有程式碼無須做任何修改。
7.2 增加新的產品等級結構
對於增加新的產品等級結構,需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生產新產品的方法,違背了“開閉原則”。
正因為抽象工廠模式存在“開閉原則”的傾斜性,它以一種傾斜的方式來滿足“開閉原則”,為增加新產品族提供方便,但不能為增加新產品結構提供這樣的方便,因此要求設計人員在設計之初就能夠全面考慮,不會在設計完成之後向系統中增加新的產品等級結構,也不會刪除已有的產品等級結構,否則將會導致系統出現較大的修改,為後續維護工作帶來諸多麻煩。
8. 總結
8.1優點:
易於改變軟體系統的行為: 抽象工廠模式隔離了具體類的生成,使得客戶並不需要知道什麼被建立。由於這種隔離,更換一個具體工廠就變得相對容易,所有的具體工廠都實現了抽象工廠中定義的那些公共介面,因此只需改變具體工廠的例項,就可以在某種程度上改變整個軟體系統的行為
始終只使用同一個產品族中的物件: 當一個產品族中的多個物件被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的物件。
8.2 缺點
不易擴充套件; 增加新的產品等級結構麻煩,需要對原有系統進行較大的修改,甚至需要修改抽象層程式碼,這顯然會帶來較大的不便,違背了“開閉原則”。
8.3 適用場景
(1) 一個系統不應當依賴於產品類例項如何被建立、組合和表達的細節,這對於所有型別的工廠模式都是很重要的,使用者無須關心物件的建立過程,將物件的建立和使用解耦。
(2) 系統中有多於一個的產品族,而每次只使用其中某一產品族。可以通過配置檔案等方式來使得使用者可以動態改變產品族,也可以很方便地增加新的產品族。
(3) 屬於同一個產品族的產品將在一起使用,這一約束必須在系統的設計中體現出來。同一個產品族中的產品可以是沒有任何關係的物件,但是它們都具有一些共同的約束,如同一操作
系統下的按鈕和文字框,按鈕與文字框之間沒有直接關係,但它們都是屬於某一作業系統的,此時具有一個共同的約束條件:作業系統的型別。
(4) 產品等級結構穩定,設計完成之後,不會向系統中增加新的產品等級結構或者刪除已有的產品等級結構。