您現在的位置是:首頁 > 攝影首頁攝影
設計模式——抽象工廠模式
抽象工廠怎麼設計
設計原則:
要依賴抽象,不要依賴具體類
目錄
本文的結構如下:
什麼是抽象工廠模式
為什麼要用該模式
模式的結構
程式碼示例
優點和缺點
適用環境
模式應用
模式擴充套件
總結
一、前言
工廠方法模式透過引入工廠等級結構,解決了簡單工廠模式中工廠類職責太重的問題,但由於工廠方法模式中的每個工廠只生產一類產品,可能會導致系統中存在大量的工廠類,勢必會增加系統的開銷。此時,我們可以
考慮將一些相關的產品組成一個“產品族”,由同一個工廠來統一生產
,這就是我們本文將要學習的抽象工廠模式的基本思想。
二、什麼是抽象工廠模式
抽象工廠模式(Abstract Factory Pattern):提供一個介面,用於建立一系列相關或相互依賴物件的家族,而無須指定它們具體的類。抽象工廠模式又稱為Kit模式,屬於物件建立型模式。
三、為什麼要用該模式
3。1、官方解釋
在工廠方法模式中具體工廠負責生產具體的產品,每一個具體工廠對應一種具體產品,工廠方法也具有唯一性,一般情況下,一個具體工廠中只有一個工廠方法或者一組過載的工廠方法。但是有時候我們需要一個工廠可以提供多個產品物件,而不是單一的產品物件。
為了更清晰地理解工廠方法模式,需要先引入兩個概念:
產品等級結構
:產品等級結構即產品的繼承結構,如一個抽象類是電視機,其子類有海爾電視機、海信電視機、TCL電視機,則抽象電視機與具體品牌的電視機之間構成了一個產品等級結構,抽象電視機是父類,而具體品牌的電視機是其子類。
產品族
:在抽象工廠模式中,產品族是指由同一個工廠生產的,位於不同產品等級結構中的一組產品,如海爾電器工廠生產的海爾電視機、海爾電冰箱,海爾電視機位於電視機產品等級結構中,海爾電冰箱位於電冰箱產品等級結構中。
當系統所提供的工廠所需生產的具體產品並不是一個簡單的物件,而是多個位於不同產品等級結構中屬於不同型別的具體產品時需要使用抽象工廠模式。
抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態。
抽象工廠模式與工廠方法模式最大的區別在於,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式則需要面對多個產品等級結構,一個工廠等級結構可以負責多個不同產品等級結構中的產品物件的建立。當一個工廠等級結構可以創建出分屬於不同產品等級結構的一個產品族中的所有物件時,抽象工廠模式比工廠方法模式更為簡單、有效率。
3。2、舉個例子
你的蛋糕店賣得非常好,為了更好的提供效率,你決定為每個蛋糕店開設一個原料提供工廠,每個蛋糕店都有專門的原料工廠提供原料,比如水果蛋糕需要的雞蛋和水果,但CenterCakeStore更多賣的是草莓類水果蛋糕,用的雞蛋是一般的雞蛋,而CollegeCakeStore更多賣的是菠蘿類水果蛋糕,並且用的雞蛋都是土雞蛋,所以原料工廠提供的具體水果和雞蛋是不一樣的,程式碼怎麼設計呢?
這次你沒有請我,因為你足夠聰明,你知道我肯定會這樣寫:
為每個原料建立一個抽象工廠類,提供抽象方法用於獲取產品:
/**
* Created by w1992wishes on 2017/11/1。
*/
public
abstract
class
FruitFactory
{
public
abstract
Fruit
provideFruit
();
}
/**
*/
public
abstract
class
EggFactory
{
abstract
Egg
provideEgg
();
}
然後為抽象工廠提供多個實現,每個子類工廠提供一種具體的原料:
/**
*/
public
class
CenterFruitFactory
extends
FruitFactory
{
@Override
public
Fruit
provideFruit
() {
return
new
StrawberryFruit
();
}
}
/**
*/
public
class
CollegeFruitFactory
extends
FruitFactory
{
@Override
public
Fruit
provideFruit
() {
return
new
MangoFruit
();
}
}
/**
*/
public
class
CenterEggFactory
extends
EggFactory
{
@Override
Egg
provideEgg
() {
return
new
NormalEgg
()();
}
}
/**
*/
public
class
CollegeEggFactory
extends
EggFactory
{
@Override
Egg
provideEgg
() {
return
new
SpecialEgg
();
}
}
接著還有一堆抽象原料,Egg,Fruit。。。
一堆具體原料,SpecialEgg,NormalEgg,MangoFruit。。。
還要在具體的Cake中用具體的原料:
/**
* Created by w1992wishes on 2017/10/31。
*/
public
class
CenterFruitCake
extends
Cake
{
public
CenterFruitCake
(){
name
=
“center fruit cake”
;
}
@Override
public
void
prepare
(){
FruitFactory
fruitFactory
=
new
CenterFruitFactory
();
EggFactory
eggFactory
=
new
CenterEggFactory
();
fruit
=
fruitFactory
。
provideFruit
();
egg
=
eggFactory
。
provideEgg
();
}
}
/**
*/
public
class
CollegeFruitCake
extends
Cake
{
public
CollegeFruitCake
(){
name
=
“college fruit cake”
;
}
@Override
public
void
prepare
(){
FruitFactory
fruitFactory
=
new
CollegeFruitFactory
();
EggFactory
eggFactory
=
new
CollegeEggFactory
();
fruit
=
fruitFactory
。
provideFruit
();
egg
=
eggFactory
。
provideEgg
();
}
}
經過一堆複雜的類構建,最後才能訂購,而且這還只是一種蛋糕,如果需要增加新的蛋糕時,雖然不要修改現有程式碼,但是需要增加大量類,針對每一個新增具體元件都需要增加一個具體工廠,類的個數成對增加,這無疑會導致系統越來越龐大,增加系統的維護成本和執行開銷;
正是因為每一個具體工廠對應一種具體產品,如果產品族很多,會引入很多類,為了解決這個問題,所以有了抽象工廠模式。
四、模式的結構
抽象工廠模式結構和工廠方法模式結構差不多,只是多了產品族中其他的抽象產品和具體產品。
● Factory(抽象工廠):它聲明瞭一組用於建立一族產品的方法,每一個方法對應一種產品。
● ConcreteFactory(具體工廠):它實現了在抽象工廠中宣告的建立產品的方法,生成一組具體產品,這些產品構成了一個產品族,每一個產品都位於某個產品等級結構中。
● Product(抽象產品):它為每種產品宣告介面,在抽象產品中聲明瞭產品所具有的業務方法。
● ConcreteProduct(具體產品):它定義具體工廠生產的具體產品物件,實現抽象產品介面中宣告的業務方法。
五、程式碼示例
先定義一個抽象原料工廠:
/**
*/
public
interface
FoodFactory
{
Egg
provideEgg
();
Fruit
provideFruit
();
}
每個蛋糕店對應的具體工廠:
/**
*/
public
class
CenterFoodFactory
implements
FoodFactory
{
@Override
public
Egg
provideEgg
() {
return
new
NormalEgg
();
}
@Override
public
Fruit
provideFruit
() {
return
new
StrawberryFruit
();
}
}
/**
*/
public
class
CollegeFoodFactory
implements
FoodFactory
{
@Override
public
Egg
provideEgg
() {
return
new
SpecialEgg
();
}
@Override
public
Fruit
provideFruit
() {
return
new
MongoFruit
();
}
}
具體的水果蛋糕類:
/**
* Created by w1992wishes on 2017/11/01。
*/
public
abstract
class
Cake
{
String
name
;
Fruit
fruit
;
Egg
egg
;
abstract
void
prepare
();
void
bake
(){
System
。
out
。
println
(
“bake”
);
}
void
box
(){
System
。
out
。
println
(
“box”
);
}
public
String
getName
() {
return
name
;
}
public
Fruit
getFruit
(){
return
fruit
;
}
public
Egg
getEgg
(){
return
egg
;
}
}
/**
*/
public
class
CenterFruitCake
extends
Cake
{
public
CenterFruitCake
(){
name
=
;
}
@Override
public
void
prepare
(){
FoodFactory
foodFactory
=
new
CenterFoodFactory
();
fruit
=
foodFactory
。
provideFruit
();
egg
=
foodFactory
。
provideEgg
();
}
}
/**
*/
public
class
CollegeFruitCake
extends
Cake
{
public
CollegeFruitCake
(){
name
=
;
}
@Override
public
void
prepare
(){
FoodFactory
foodFactory
=
new
CollegeFoodFactory
();
fruit
=
foodFactory
。
provideFruit
();
egg
=
foodFactory
。
provideEgg
();
}
}
蛋糕店的程式碼不變:
/**
* Created by w1992wishes on 2017/11/1。
*/
public
class
CenterCakeStore
extends
CakeStore
{
@
Override
protected
Cake
createCake
(
String
type
) {
Cake
cake
=
null
;
if
(
“cheese”
。
equals
(
type
)) {
cake
=
new
CenterCheeseCake
();
}
else
if
(
“fruit”
。
equals
(
type
)) {
cake
=
new
CenterFruitCake
();
}
else
if
(
“cream”
。
equals
(
type
)) {
cake
=
new
CenterCreamCake
();
}
return
cake
;
}
}
/**
* Created by w1992wishes on 2017/11/1。
*/
public
class
CollegeCakeStore
extends
CakeStore
{
@
Override
protected
Cake
createCake
(
String
type
) {
Cake
cake
=
null
;
if
(
“cheese”
。
equals
(
type
)) {
cake
=
new
CollegeCheeseCake
();
}
else
if
(
“fruit”
。
equals
(
type
)) {
cake
=
new
CollegeFruitCake
();
}
else
if
(
“cream”
。
equals
(
type
)) {
cake
=
new
CollegeCreamCake
();
}
return
cake
;
}
}
客戶端程式碼不變:
/**
* Created by w1992wishes on 2017/11/1。
*/
public
class
Client
{
public
static
void
main
(
String
[]
args
) {
CakeStore
cakeStore
=
new
CollegeCakeStore
();
//這裡可透過引入配置檔案更改
cakeStore
。
orderCake
(
“fruit”
);
}
}
看這段程式碼可能有點疑惑,建議不用管蛋糕店,直接看製作蛋糕的原料那塊,是抽象工廠模式的真正實現程式碼。
相對於工廠方法模式,抽象工廠方法,即沒有破壞“開閉原則”,同時又解決了前者引入大量工廠類的弊端,是前者更高層次的抽象。
六、優點和缺點
6。1、優點
抽象工廠模式隔離了具體類的生成,使得客戶並不需要知道什麼被建立。由於這種隔離,更換一個具體工廠就變得相對容易。所有的具體工廠都實現了抽象工廠中定義的那些公共介面,因此只需改變具體工廠的例項,就可以在某種程度上改變整個軟體系統的行為。另外,應用抽象工廠模式可以實現高內聚低耦合的設計目的,因此抽象工廠模式得到了廣泛的應用。
當一個產品族中的多個物件被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的物件。這對一些需要根據當前環境來決定其行為的軟體系統來說,是一種非常實用的設計模式。
增加新的具體工廠和產品族很方便,無須修改已有系統,符合“開閉原則”。
6。2、缺點
在新增新的產品物件時,難以擴充套件抽象工廠來生產新種類的產品,這是因為在抽象工廠角色中規定了所有可能被建立的產品集合,要支援新種類的產品就意味著要對該介面進行擴充套件,而這將涉及到對抽象工廠角色及其所有子類的修改,顯然會帶來較大的不便。
開閉原則的傾斜性。
(1) 增加產品族:對於增加新的產品族,抽象工廠模式很好地支援了“開閉原則”,只需要增加具體產品並對應增加一個新的具體工廠,對已有程式碼無須做任何修改。
(2) 增加新的產品等級結構:對於增加新的產品等級結構,需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生產新產品的方法,違背了“開閉原則”。
正因為抽象工廠模式存在“開閉原則”的傾斜性,它以一種傾斜的方式來滿足“開閉原則”,為增加新產品族提供方便,但不能為增加新產品結構提供這樣的方便,因此要求設計人員在設計之初就能夠全面考慮,不會在設計完成之後向系統中增加新的產品等級結構,也不會刪除已有的產品等級結構,否則將會導致系統出現較大的修改,為後續維護工作帶來諸多麻煩。
七、適用環境
在以下情況下可以考慮使用抽象工廠模式:
一個系統不應當依賴於產品類例項如何被建立、組合和表達的細節,這對於所有型別的工廠模式都是很重要的,使用者無須關心物件的建立過程,將物件的建立和使用解耦。
系統中有多於一個的產品族,而每次只使用其中某一產品族。可以透過配置檔案等方式來使得使用者可以動態改變產品族,也可以很方便地增加新的產品族。
屬於同一個產品族的產品將在一起使用,這一約束必須在系統的設計中體現出來。同一個產品族中的產品可以是沒有任何關係的物件,但是它們都具有一些共同的約束,如同一製作水果蛋糕用的水果——草莓和芒果,草莓和芒果之間沒有直接關係,但它們都是屬於水果。
產品等級結構穩定,設計完成之後,不會向系統中增加新的產品等級結構或者刪除已有的產品等級結構。
八、模式應用
在很多軟體系統中需要更換介面主題,要求介面中的按鈕、文字框、背景色等一起發生改變時,可以使用抽象工廠模式進行設計。
九、模式擴充套件
“開閉原則”的傾斜性
在介紹抽象工廠方法模式的缺點已經提到,這裡再重複囉嗦一下。
“開閉原則”要求系統對擴充套件開放,對修改封閉,透過擴充套件達到增強其功能的目的。對於涉及到多個產品族與多個產品等級結構的系統,其功能增強包括兩方面:
增加產品族:對於增加新的產品族,工廠方法模式很好的支援了“開閉原則”,對於新增加的產品族,只需要對應增加一個新的具體工廠即可,對已有程式碼無須做任何修改。
增加新的產品等級結構:對於增加新的產品等級結構,需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生產新產品的方法,不能很好地支援“開閉原則”。
工廠模式的退化
當抽象工廠模式中每一個具體工廠類只建立一個產品物件,也就是隻存在一個產品等級結構時,抽象工廠模式退化成工廠方法模式;當工廠方法模式中抽象工廠與具體工廠合併,提供一個統一的工廠來建立產品物件,並將建立物件的工廠方法設計為靜態方法時,工廠方法模式退化成簡單工廠模式。
十、總結
抽象工廠模式提供一個建立一系列相關或相互依賴物件的介面,而無須指定它們具體的類。抽象工廠模式又稱為Kit模式,屬於物件建立型模式。
抽象工廠模式包含四個角色:抽象工廠用於宣告生成抽象產品的方法;具體工廠實現了抽象工廠宣告的生成抽象產品的方法,生成一組具體產品,這些產品構成了一個產品族,每一個產品都位於某個產品等級結構中;抽象產品為每種產品宣告介面,在抽象產品中定義了產品的抽象業務方法;具體產品定義具體工廠生產的具體產品物件,實現抽象產品介面中定義的業務方法。
抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態。抽象工廠模式與工廠方法模式最大的區別在於,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式則需要面對多個產品等級結構。
抽象工廠模式的主要優點是隔離了具體類的生成,使得客戶並不需要知道什麼被建立,而且每次可以透過具體工廠類建立一個產品族中的多個物件,增加或者替換產品族比較方便,增加新的具體工廠和產品族很方便;主要缺點在於增加新的產品等級結構很複雜,需要修改抽象工廠和所有的具體工廠類,對“開閉原則”的支援呈現傾斜性。
抽象工廠模式適用情況包括:一個系統不應當依賴於產品類例項如何被建立、組合和表達的細節;系統中有多於一個的產品族,而每次只使用其中某一產品族;屬於同一個產品族的產品將在一起使用;系統提供一個產品類的庫,所有的產品以同樣的接口出現,從而使客戶端不依賴於具體實現。