007設(shè)計(jì)模式--抽象工廠模式(Abstract Factory Pattern)

一、什么是抽象工廠模式

抽象工廠模式為創(chuàng)建一組相關(guān)或相互依賴的對象提供一個接口,而且無需指定他們的具體類。

抽象工廠模式隸屬于設(shè)計(jì)模式中的創(chuàng)建型模式,用于產(chǎn)品族的構(gòu)建。抽象工廠是所有形態(tài)的工廠模式中最為抽象和最具一般性的一種形態(tài)。抽象工廠是指當(dāng)有多個抽象角色時使用的一種工廠模式。抽象工廠模式可以向客戶端提供一個接口,使客戶端在不必指定產(chǎn)品的具體情況下,創(chuàng)建多個產(chǎn)品族中的產(chǎn)品對象。

為什么出現(xiàn)抽象工廠模式?

雖然工廠方法模式引入工廠等級結(jié)構(gòu),解決了簡單工廠模式中工廠類職責(zé)過重的問題,但由于工廠方法模式中每個工廠只創(chuàng)建一類具體類的對象,如果需要的具體類很多時候,這將會導(dǎo)致系統(tǒng)當(dāng)中的工廠類過多,這勢必會增加系統(tǒng)的開銷。此時可以考慮將一些相關(guān)的具體類組成一個“具體類族”,由同一個工廠來統(tǒng)一生產(chǎn),這就是我們需要抽象工廠模式的原因。

實(shí)現(xiàn)原理

在抽象工廠模式中,客戶端不再負(fù)責(zé)對象的創(chuàng)建,而是把這個責(zé)任丟給了具體的工廠類,客戶端只負(fù)責(zé)對對象的調(diào)用,從而明確了各個類的職責(zé)。并且當(dāng)一系列相互關(guān)聯(lián)的產(chǎn)品被設(shè)計(jì)到一個工廠類里后,客戶端的調(diào)用將會變得非常簡單,而且,如果要更換這一系列的產(chǎn)品,則只需要更換一個工廠類即可。

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

角色 含義
抽象工廠角色 擔(dān)任這個角色的是工廠方法模式的核心,它是與應(yīng)用系統(tǒng)商業(yè)邏輯無關(guān)的
具體工廠角色 這個角色直接在客戶端的調(diào)用下創(chuàng)建產(chǎn)品的實(shí)例。這個角色含有選擇合適的產(chǎn)品對象的邏輯,而這個邏輯是與應(yīng)用系統(tǒng)的商業(yè)邏輯緊密相關(guān)的
抽象產(chǎn)品角色 擔(dān)任這個角色的類是工廠方法模式所創(chuàng)建的對象的父類,或它們共同擁有的接口
具體產(chǎn)品角色 抽象工廠模式所創(chuàng)建的任何產(chǎn)品對象都是某一個具體產(chǎn)品類的實(shí)例。這是客戶端最終需要的東西,其內(nèi)部一定充滿了應(yīng)用系統(tǒng)的商業(yè)邏輯

三、抽象工廠模式的代碼實(shí)現(xiàn)

python實(shí)現(xiàn)
from abc import ABCMeta, abstractmethod


class CPU(metaclass=ABCMeta):
    @abstractmethod
    def show_cpu(self):
        pass


class InterCPU(CPU):
    def show_cpu(self):
        print('this is inter cpu...')


class AmdCPU(CPU):
    def show_cpu(self):
        print('this is amd cpu...')


class OS(metaclass=ABCMeta):
    @abstractmethod
    def show_os(self):
        pass


class AppleOS(OS):
    def show_os(self):
        print('this is apple os..')


class WindowsOS(OS):
    def show_os(self):
        print('this is windows os')


class Factory(metaclass=ABCMeta):
    @abstractmethod
    def make_cpu(self):
        pass

    @abstractmethod
    def make_os(self):
        pass


class Mac(Factory):
    def make_cpu(self):
        return AmdCPU()

    def make_os(self):
        return AppleOS()


class MacComputer(object):
    def __init__(self, cpu, os):
        self.cpu = cpu
        self.os = os

    def show_info(self):
        print("電腦信息:")
        self.cpu.show_cpu()
        self.os.show_os()


def make_computer(factory):
    cpu = factory.make_cpu()
    os = factory.make_os()
    return MacComputer(cpu, os)


mac = make_computer(Mac())
mac.show_info()
golang實(shí)現(xiàn)
package main

import "fmt"

// Programmer 程序員總稱
type Programmer interface {
    Work()
}

// FrontProgrammer 前端程序員
type FrontProgrammer struct {
    Programmer
}
func (fp FrontProgrammer) Work() {
    fmt.Println("this is FrontProgrammer work.")
}

// BackEndProgrammer 后端程序員
type BackendProgrammer struct {
    Programmer
}
func (bp BackendProgrammer) Work() {
    fmt.Println("this is BackendProgrammer work.")
}

// Architect 架構(gòu)師總稱
type Architect interface {
    Design()
}

// FrontEndArchitect 前端架構(gòu)師
type FrontArchitect struct {
    Architect
}
func (fa FrontArchitect) Design() {
    fmt.Println("this is FrontArchitect design.")
}

// BackEndArchitect 后端架構(gòu)師
type BackendArchitect struct {
    Architect
}
func (ba BackendArchitect) Design() {
    fmt.Println("this is BackendArchitect design.")
}

// AbstractFactory 抽象工廠
type AbstractFactory interface {
    CreateProgrammer() Programmer
    CreateArchitect() Architect
}

// FrontEndFactory 前端工廠
type FrontFactory struct {
    AbstractFactory
}
func (f *FrontFactory) CreateProgrammer() Programmer {
    return FrontProgrammer{}
}
func (f *FrontFactory) CreateArchitect() Architect {
    return FrontArchitect{}
}

// BackEndFactory 后端工廠
type BackendFactory struct {
    AbstractFactory
}
func (b *BackendFactory) CreateProgrammer() Programmer {
    return BackendProgrammer{}
}
func (b *BackendFactory) CreateArchitect() Architect {
    return BackendArchitect{}
}

func main3() {
    ff := FrontFactory{}        // 定義前端工廠
    fa := ff.CreateArchitect()  // 定義前端架構(gòu)師類
    fp := ff.CreateProgrammer() // 定義前端程序員類
    fmt.Println("前端組接到任務(wù),開始工作...")
    fa.Design() // 架構(gòu)師開始工作
    fp.Work()   // 程序員開始工作
    fmt.Println("后端組招到了一個程序員和一個架構(gòu)師")
    bf := BackendFactory{}
    ba := bf.CreateArchitect()
    bp := bf.CreateProgrammer()
    fmt.Println("后端組接到任務(wù),開始工作...")
    ba.Design()
    bp.Work()
}

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

優(yōu)點(diǎn)
  • 抽象工廠模式隔離了具體產(chǎn)品/類的生成, 使得客戶并不需要知道什么被創(chuàng)建。 由于這種隔離,更換或增加一個具體工廠就變得相對容易, 所有的具體工廠都實(shí)現(xiàn)了抽象工廠中定義的那些公共接口, 因此只需改變具體工廠的實(shí)例, 就可以在某種程度上改變整個軟件系統(tǒng)的行為。
  • 當(dāng)一個族中的多個對象被設(shè)計(jì)成一起工作時, 它能夠保證客戶端始終只使用同一個族中的對象
  • 增加新的族很方便, 無須修改已有系統(tǒng), 符合“開閉原則”
缺點(diǎn)
  • 難以支持新種類的產(chǎn)品, 最大缺點(diǎn)就是產(chǎn)品族本身的擴(kuò)展非常困難。如果在產(chǎn)品族中增加一個新的產(chǎn)品類型,則需要修改多個接口,并影響現(xiàn)已有的工廠類(打個比方說,你要在這個工廠創(chuàng)建三個對象,原本只是創(chuàng)建兩個對象的,那么你就要在抽象方法中添加一個創(chuàng)建對象的方法,那么所有實(shí)現(xiàn)了這個接口的類都是要重新添加這個創(chuàng)建對象的方法,這就是對之前的工廠有影響的原因。)

五、抽象工廠模式的應(yīng)用場景

  • 當(dāng)用戶端需要創(chuàng)建的對象是一系列相互關(guān)聯(lián)或相互依賴的產(chǎn)品族時,抽象工廠模式很適合應(yīng)用這種情況
  • 當(dāng)系統(tǒng)中有多個產(chǎn)品族,但用戶端每次只需要其中的某一族產(chǎn)品時,抽象工廠模式也非常適合這種情況
    • 當(dāng)系統(tǒng)中提供了產(chǎn)品的類庫,且所有產(chǎn)品的接口相同時,要求客戶端類不依賴產(chǎn)品實(shí)例的創(chuàng)建細(xì)節(jié)和內(nèi)部結(jié)構(gòu)時,抽象工廠也適合這種情況

六、對比

抽象工廠模式和工廠方法模式對比

總體而言:

  • 工廠模式中的每一個形態(tài)都是針對一定問題的解決方案,工廠方法針對的是多個產(chǎn)品系列結(jié)構(gòu)
  • 抽象工廠模式針對的是多個產(chǎn)品族結(jié)構(gòu),一個產(chǎn)品族內(nèi)有多個產(chǎn)品系列。

七、總結(jié)

抽象工廠模式是為用戶端 提供一個創(chuàng)建一系列相關(guān)或相互依賴對象的接口,用戶端無需知道他們需要的產(chǎn)品怎么做的,它只需要調(diào)用接口方法就行。

抽象工廠模式,不僅可以實(shí)現(xiàn)多個接口,而且每個工廠也可以生產(chǎn)多種產(chǎn)品類,和工廠方法模式一樣,抽象工廠模式同樣實(shí)現(xiàn)了開發(fā)封閉原則。

注意:當(dāng)抽象工廠模式中的每個工廠類都只生產(chǎn)一種具體的產(chǎn)品時,這時候的抽象工廠模式相當(dāng)于工廠方法模式。

通過接口規(guī)定每個具體工廠需要實(shí)現(xiàn)的方法,不需要考慮具體產(chǎn)品如何生成,將生成具體產(chǎn)品的細(xì)節(jié)放到具體工廠中取實(shí)現(xiàn)。當(dāng)工廠生成產(chǎn)品需要更多的配置信息,將所有產(chǎn)品所需的配置信息都放在簡單工廠中太過雜亂,也不便于代碼維護(hù)。將生成某個產(chǎn)品的信息抽取出來,單獨(dú)放到一個類中,每個具體工廠對應(yīng)某個具體產(chǎn)品,使得具體工廠的職能更加單一,代碼簡潔。使信息局部化,降低了類的復(fù)雜性,變更引起的風(fēng)險(xiǎn)降低。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,762評論 2 372

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