工廠模式

工廠模式是我們最常用的實(shí)例化對(duì)象模式了,是用工廠方法代替new操作的一種模式。通常我們所說(shuō)的工廠模式是指工廠方法模式,它也是使用頻率最高的工廠模式。工廠模式在Java程序系統(tǒng)可以說(shuō)是隨處可見。因?yàn)楣S模式就相當(dāng)于創(chuàng)建實(shí)例對(duì)象的new,我們經(jīng)常要根據(jù)類Class生成實(shí)例對(duì)象,如A a=new A() 工廠模式也是用來(lái)創(chuàng)建實(shí)例對(duì)象的,所以以后new時(shí)就要多個(gè)心眼,是否可以考慮使用工廠模式,雖然這樣做,可能多做一些工作,但會(huì)給你系統(tǒng)帶來(lái)更大的可擴(kuò)展性和盡量少的修改量。

工廠模式的作用

工廠模式(包括簡(jiǎn)單工廠模式、工廠方法模式和抽象工廠模式)到底有什么用,很多時(shí)候通過(guò)反射機(jī)制就可以很靈活地創(chuàng)建對(duì)象,為什么還要工廠?

與一個(gè)對(duì)象有關(guān)的原則有三類:

  1. 對(duì)象本身所具有的職責(zé)
  2. 創(chuàng)建對(duì)象的職責(zé)
  3. 使用對(duì)象的職責(zé)

對(duì)象本身的職責(zé)很好理解,就是對(duì)象自身所具有的一些數(shù)據(jù)和行為,可通過(guò)一些公開的方法來(lái)實(shí)現(xiàn)它的職責(zé)。在本文中,我們將簡(jiǎn)單討論一下對(duì)象的創(chuàng)建職責(zé)和使用職責(zé)。

在Java語(yǔ)言中,我們通常有以下幾種創(chuàng)建對(duì)象的方式:

  1. 使用new關(guān)鍵字直接創(chuàng)建對(duì)象;
  2. 通過(guò)反射機(jī)制創(chuàng)建對(duì)象;
  3. 通過(guò)clone()方法創(chuàng)建對(duì)象;
  4. 通過(guò)工廠類創(chuàng)建對(duì)象。

毫無(wú)疑問(wèn),在客戶端代碼中直接使用new關(guān)鍵字是最簡(jiǎn)單的一種創(chuàng)建對(duì)象的方式,但是它的靈活性較差,下面通過(guò)一個(gè)簡(jiǎn)單的示例來(lái)加以說(shuō)明:

class LoginAction {
    private UserDAO udao;
    
    public LoginAction() {
        udao = new JDBCUserDAO(); //創(chuàng)建對(duì)象
    }
    
    public void execute() {
        //其他代碼
        udao.findUserById(); //使用對(duì)象
        //其他代碼
    }
}

在LoginAction類中定義了一個(gè)UserDAO類型的對(duì)象udao,在LoginAction的構(gòu)造函數(shù)中創(chuàng)建了JDBCUserDAO類型的udao對(duì)象,并在execute()方法中調(diào)用了udao對(duì)象的findUserById()方法,這段代碼看上去并沒(méi)有什么問(wèn)題。下面我們來(lái)分析一下LoginAction和UserDAO之間的關(guān)系,LoginAction類負(fù)責(zé)創(chuàng)建了一個(gè)UserDAO子類的對(duì)象并使用UserDAO的方法來(lái)完成相應(yīng)的業(yè)務(wù)處理,也就是說(shuō)LoginAction即負(fù)責(zé)udao的創(chuàng)建又負(fù)責(zé)udao的使用,創(chuàng)建對(duì)象和使用對(duì)象的職責(zé)耦合在一起,這樣的設(shè)計(jì)會(huì)導(dǎo)致一個(gè)很嚴(yán)重的問(wèn)題:如果在LoginAction中希望能夠使用UserDAO的另一個(gè)子類如HibernateUserDAO類型的對(duì)象,必須修改LoginAction類的源代碼,違反了“開閉原則”。如何解決該問(wèn)題?

最常用的一種解決方法是將udao對(duì)象的創(chuàng)建職責(zé)從LoginAction類中移除,在LoginAction類之外創(chuàng)建對(duì)象,那么誰(shuí)來(lái)負(fù)責(zé)創(chuàng)建UserDAO對(duì)象呢?答案是:工廠類。通過(guò)引入工廠類,客戶類(如LoginAction)不涉及對(duì)象的創(chuàng)建,對(duì)象的創(chuàng)建者也不會(huì)涉及對(duì)象的使用。引入工廠類UserDAOFactory之后的結(jié)構(gòu)如圖1所示:

enter image description here

工廠類的引入將降低因?yàn)楫a(chǎn)品或工廠類改變所造成的維護(hù)工作量。如果UserDAO的某個(gè)子類的構(gòu)造函數(shù)發(fā)生改變或者要需要添加或移除不同的子類,只要維護(hù)UserDAOFactory的代碼,而不會(huì)影響到LoginAction;如果UserDAO的接口發(fā)生改變,例如添加、移除方法或改變方法名,只需要修改LoginAction,不會(huì)給UserDAOFactory帶來(lái)任何影響。
在所有的工廠模式中,我們都強(qiáng)調(diào)一點(diǎn):兩個(gè)類A和B之間的關(guān)系應(yīng)該僅僅是A創(chuàng)建B或是A使用B,而不能兩種關(guān)系都有。將對(duì)象的創(chuàng)建和使用分離,也使得系統(tǒng)更加符合“單一職責(zé)原則”,有利于對(duì)功能的復(fù)用和系統(tǒng)的維護(hù)。

此外,將對(duì)象的創(chuàng)建和使用分離還有一個(gè)好處:防止用來(lái)實(shí)例化一個(gè)類的數(shù)據(jù)和代碼在多個(gè)類中到處都是,可以將有關(guān)創(chuàng)建的知識(shí)搬移到一個(gè)工廠類中,這在Joshua Kerievsky的《重構(gòu)與模式》一書中有專門的一節(jié)來(lái)進(jìn)行介紹。因?yàn)橛袝r(shí)候我們創(chuàng)建一個(gè)對(duì)象不只是簡(jiǎn)單調(diào)用其構(gòu)造函數(shù),還需要設(shè)置一些參數(shù),可能還需要配置環(huán)境,如果將這些代碼散落在每一個(gè)創(chuàng)建對(duì)象的客戶類中,勢(shì)必會(huì)出現(xiàn)代碼重復(fù)、創(chuàng)建蔓延的問(wèn)題,而這些客戶類其實(shí)無(wú)須承擔(dān)對(duì)象的創(chuàng)建工作,它們只需使用已創(chuàng)建好的對(duì)象就可以了。此時(shí),可以引入工廠類來(lái)封裝對(duì)象的創(chuàng)建邏輯和客戶代碼的實(shí)例化/配置選項(xiàng)。

使用工廠類還有一個(gè)“不是特別明顯的”優(yōu)點(diǎn),一個(gè)類可能擁有多個(gè)構(gòu)造函數(shù),而在Java、C#等語(yǔ)言中構(gòu)造函數(shù)名字都與類名相同,客戶端只能通過(guò)傳入不同的參數(shù)來(lái)調(diào)用不同的構(gòu)造函數(shù)創(chuàng)建對(duì)象,從構(gòu)造函數(shù)和參數(shù)列表中也許大家根本不了解不同構(gòu)造函數(shù)所構(gòu)造的產(chǎn)品的差異。但如果將對(duì)象的創(chuàng)建過(guò)程封裝在工廠類中,我們可以提供一系列名字完全不同的工廠方法,每一個(gè)工廠方法對(duì)應(yīng)一個(gè)構(gòu)造函數(shù),客戶端可以以一種更加可讀、易懂的方式來(lái)創(chuàng)建對(duì)象,而且,從一組工廠方法中選擇一個(gè)意義明確的工廠方法,比從一組名稱相同參數(shù)不同的構(gòu)造函數(shù)中選擇一個(gè)構(gòu)造函數(shù)要方便很多。

那么,有人可能會(huì)問(wèn),是否需要為設(shè)計(jì)中的每一個(gè)類都配備一個(gè)工廠類?答案是:具體情況具體分析。如果產(chǎn)品類很簡(jiǎn)單,而且不存在太多變數(shù),其構(gòu)造過(guò)程也很簡(jiǎn)單,此時(shí)無(wú)須為其提供工廠類,直接在使用之前實(shí)例化即可,例如Java語(yǔ)言中的String類,我們就無(wú)須為它專門提供一個(gè)StringFactory,這樣做反而有點(diǎn)像殺雞用牛刀,大材小用,而且會(huì)導(dǎo)致工廠泛濫,增加系統(tǒng)的復(fù)雜度。

工廠模式是為了將對(duì)象的創(chuàng)建和使用分離,也使得系統(tǒng)更加符合“單一職責(zé)原則”,有利于對(duì)功能的復(fù)用和系統(tǒng)的維護(hù),防止用來(lái)實(shí)例化一個(gè)類的數(shù)據(jù)和代碼在多個(gè)類中到處都是,可以將有關(guān)創(chuàng)建的知識(shí)搬移到一個(gè)工廠類中。

轉(zhuǎn)載:創(chuàng)建對(duì)象與使用對(duì)象——談?wù)劰S的作用

簡(jiǎn)單工廠模式

簡(jiǎn)單工廠模式(Simple Factory Pattern):定義一個(gè)工廠類,它可以根據(jù)參數(shù)的不同返回不同類的實(shí)例,被創(chuàng)建的實(shí)例通常都具有共同的父類。因?yàn)樵诤?jiǎn)單工廠模式中用于創(chuàng)建實(shí)例的方法是靜態(tài)(static)方法,因此簡(jiǎn)單工廠模式又被稱為靜態(tài)工廠方法(Static Factory Method)模式,它屬于類創(chuàng)建型模式。

簡(jiǎn)單工廠模式的要點(diǎn)在于:當(dāng)你需要什么,只需要傳入一個(gè)正確的參數(shù),就可以獲取你所需要的對(duì)象,而無(wú)須知道其創(chuàng)建細(xì)節(jié)。簡(jiǎn)單工廠模式結(jié)構(gòu)比較簡(jiǎn)單,其核心是工廠類的設(shè)計(jì), 其結(jié)構(gòu)如下圖所示。

image.png

在簡(jiǎn)單工廠模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:

  • Factory(工廠角色):工廠角色即工廠類,它是簡(jiǎn)單工廠模式的核心,負(fù)責(zé)實(shí)現(xiàn)創(chuàng)建所有產(chǎn)品實(shí)例的內(nèi)部邏輯;工廠類可以被外界直接調(diào)用,創(chuàng)建所需的產(chǎn)品對(duì)象;在工廠類中提供了靜態(tài)的工廠方法factoryMethod(),它的返回類型為抽象產(chǎn)品類型Product。
  • Product(抽象產(chǎn)品角色):它是工廠類所創(chuàng)建的所有對(duì)象的父類,封裝了各種產(chǎn)品對(duì)象的公有方法,它的引入將提高系統(tǒng)的靈活性,使得在工廠類中只需定義一個(gè)通用的工廠方法,因?yàn)樗袆?chuàng)建的具體產(chǎn)品對(duì)象都是其子類對(duì)象。
  • ConcreteProduct(具體產(chǎn)品角色):它是簡(jiǎn)單工廠模式的創(chuàng)建目標(biāo),所有被創(chuàng)建的對(duì)象都充當(dāng)這個(gè)角色的某個(gè)具體類的實(shí)例。每一個(gè)具體產(chǎn)品角色都繼承了抽象產(chǎn)品角色,需要實(shí)現(xiàn)在抽象產(chǎn)品中聲明的抽象方法。

在簡(jiǎn)單工廠模式中,客戶端通過(guò)工廠類來(lái)創(chuàng)建一個(gè)產(chǎn)品類的實(shí)例,而無(wú)須直接使用new關(guān)鍵字來(lái)創(chuàng)建對(duì)象,它是工廠模式家族中最簡(jiǎn)單的一員。
在使用簡(jiǎn)單工廠模式時(shí),首先需要對(duì)產(chǎn)品類進(jìn)行重構(gòu),不能設(shè)計(jì)一個(gè)包羅萬(wàn)象的產(chǎn)品類,而需根據(jù)實(shí)際情況設(shè)計(jì)一個(gè)產(chǎn)品層次結(jié)構(gòu),將所有產(chǎn)品類公共的代碼移至抽象產(chǎn)品類,并在抽象產(chǎn)品類中聲明一些抽象方法,以供不同的具體產(chǎn)品類來(lái)實(shí)現(xiàn),典型的抽象產(chǎn)品類代碼如下所示:

abstract class Product {
    //所有產(chǎn)品類的公共業(yè)務(wù)方法
    public void methodSame() {
        //公共方法的實(shí)現(xiàn)
    }

    //聲明抽象業(yè)務(wù)方法
    public abstract void methodDiff();
}

在具體產(chǎn)品類中實(shí)現(xiàn)了抽象產(chǎn)品類中聲明的抽象業(yè)務(wù)方法,不同的具體產(chǎn)品類可以提供不同的實(shí)現(xiàn),典型的具體產(chǎn)品類代碼如下所示:

class ConcreteProduct extends Product {
    //實(shí)現(xiàn)業(yè)務(wù)方法
    public void methodDiff() {
        //業(yè)務(wù)方法的實(shí)現(xiàn)
    }
}

簡(jiǎn)單工廠模式的核心是工廠類,在沒(méi)有工廠類之前,客戶端一般會(huì)使用new關(guān)鍵字來(lái)直接創(chuàng)建產(chǎn)品對(duì)象,而在引入工廠類之后,客戶端可以通過(guò)工廠類來(lái)創(chuàng)建產(chǎn)品,在簡(jiǎn)單工廠模式中,工廠類提供了一個(gè)靜態(tài)工廠方法供客戶端使用,根據(jù)所傳入的參數(shù)不同可以創(chuàng)建不同的產(chǎn)品對(duì)象,典型的工廠類代碼如下所示:

class Factory {
    //靜態(tài)工廠方法
    public static Product getProduct(String arg) {
        Product product = null;
        if (arg.equalsIgnoreCase("A")) {
            product = new ConcreteProductA();
            //初始化設(shè)置product
        }
        else if (arg.equalsIgnoreCase("B")) {
            product = new ConcreteProductB();
            //初始化設(shè)置product
        }
        return product;
    }
}

在客戶端代碼中,我們通過(guò)調(diào)用工廠類的工廠方法即可得到產(chǎn)品對(duì)象,典型代碼如下所示:

class Client {
    public static void main(String args[]) {
        Product product; 
        product = Factory.getProduct("A"); //通過(guò)工廠類創(chuàng)建產(chǎn)品對(duì)象
        product.methodSame();
        product.methodDiff();
    }
}

簡(jiǎn)單工廠模式總結(jié):

簡(jiǎn)單工廠模式提供了專門的工廠類用于創(chuàng)建對(duì)象,將對(duì)象的創(chuàng)建和對(duì)象的使用分離開,它作為一種最簡(jiǎn)單的工廠模式在軟件開發(fā)中得到了較為廣泛的應(yīng)用。

  • 優(yōu)點(diǎn)

    • 工廠類包含必要的判斷邏輯,可以決定在什么時(shí)候創(chuàng)建哪一個(gè)產(chǎn)品類的實(shí)例,客戶端可以免除直接創(chuàng)建產(chǎn)品對(duì)象的職責(zé),而僅僅“消費(fèi)”產(chǎn)品,簡(jiǎn)單工廠模式實(shí)現(xiàn)了對(duì)象創(chuàng)建和使用的分離。
    • 客戶端無(wú)須知道所創(chuàng)建的具體產(chǎn)品類的類名,只需要知道具體產(chǎn)品類所對(duì)應(yīng)的參數(shù)即可,對(duì)于一些復(fù)雜的類名,通過(guò)簡(jiǎn)單工廠模式可以在一定程度減少使用者的記憶量。
    • 通過(guò)引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產(chǎn)品類,在一定程度上提高了系統(tǒng)的靈活性。
  • 缺點(diǎn)

    • 由于工廠類集中了所有產(chǎn)品的創(chuàng)建邏輯,職責(zé)過(guò)重,一旦不能正常工作,整個(gè)系統(tǒng)都要受到影響。
    • 使用簡(jiǎn)單工廠模式勢(shì)必會(huì)增加系統(tǒng)中類的個(gè)數(shù)(引入了新的工廠類),增加了系統(tǒng)的復(fù)雜度和理解難度。
    • 系統(tǒng)擴(kuò)展困難,一旦添加新產(chǎn)品就不得不修改工廠邏輯,在產(chǎn)品類型較多時(shí),有可能造成工廠邏輯過(guò)于復(fù)雜,不利于系統(tǒng)的擴(kuò)展和維護(hù)。
    • 系統(tǒng)擴(kuò)展困難,一旦添加新產(chǎn)品就不得不修改工廠邏輯,在產(chǎn)品類型較多時(shí),有可能造成工廠邏輯過(guò)于復(fù)雜,不利于系統(tǒng)的擴(kuò)展和維護(hù)。
  • 適用場(chǎng)景

    • 工廠類負(fù)責(zé)創(chuàng)建的對(duì)象比較少,由于創(chuàng)建的對(duì)象較少,不會(huì)造成工廠方法中的業(yè)務(wù)邏輯太過(guò)復(fù)雜。
    • 工廠類負(fù)責(zé)創(chuàng)建的對(duì)象比較少,由于創(chuàng)建的對(duì)象較少,不會(huì)造成工廠方法中的業(yè)務(wù)邏輯太過(guò)復(fù)雜。

轉(zhuǎn)載:工廠三兄弟之簡(jiǎn)單工廠模式(二)

工廠方法模式

簡(jiǎn)單工廠模式雖然簡(jiǎn)單,但存在一個(gè)很嚴(yán)重的問(wèn)題。當(dāng)系統(tǒng)中需要引入新產(chǎn)品時(shí),由于靜態(tài)工廠方法通過(guò)所傳入?yún)?shù)的不同來(lái)創(chuàng)建不同的產(chǎn)品,這必定要修改工廠類的源代碼,將違背“開閉原則”,如何實(shí)現(xiàn)增加新產(chǎn)品而不影響已有代碼?工廠方法模式應(yīng)運(yùn)而生.

在工廠方法模式中,我們不再提供一個(gè)統(tǒng)一的工廠類來(lái)創(chuàng)建所有的產(chǎn)品對(duì)象,而是針對(duì)不同的產(chǎn)品提供不同的工廠,系統(tǒng)提供一個(gè)與產(chǎn)品等級(jí)結(jié)構(gòu)對(duì)應(yīng)的工廠等級(jí)結(jié)構(gòu)。

工廠方法模式(Factory Method Pattern):定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定將哪一個(gè)類實(shí)例化。工廠方法模式讓一個(gè)類的實(shí)例化延遲到其子類。工廠方法模式又簡(jiǎn)稱為工廠模式(Factory Pattern),又可稱作虛擬構(gòu)造器模式(Virtual Constructor Pattern)或多態(tài)工廠模式(Polymorphic Factory Pattern)。工廠方法模式是一種類創(chuàng)建型模式。

工廠方法模式提供一個(gè)抽象工廠接口來(lái)聲明抽象工廠方法,而由其子類來(lái)具體實(shí)現(xiàn)工廠方法,創(chuàng)建具體的產(chǎn)品對(duì)象。工廠方法模式結(jié)構(gòu)如圖所示:

image.png

在工廠方法模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:

  • Product(抽象產(chǎn)品):它是定義產(chǎn)品的接口,是工廠方法模式所創(chuàng)建對(duì)象的超類型,也就是產(chǎn)品對(duì)象的公共父類。
  • ConcreteProduct(具體產(chǎn)品):它實(shí)現(xiàn)了抽象產(chǎn)品接口,某種類型的具體產(chǎn)品由專門的具體工廠創(chuàng)建,具體工廠和具體產(chǎn)品之間一一對(duì)應(yīng)。
  • Factory(抽象工廠):在抽象工廠類中,聲明了工廠方法(Factory Method),用于返回一個(gè)產(chǎn)品。抽象工廠是工廠方法模式的核心,所有創(chuàng)建對(duì)象的工廠類都必須實(shí)現(xiàn)該接口。
  • ConcreteFactory(具體工廠):它是抽象工廠類的子類,實(shí)現(xiàn)了抽象工廠中定義的工廠方法,并可由客戶端調(diào)用,返回一個(gè)具體產(chǎn)品類的實(shí)例。

與簡(jiǎn)單工廠模式相比,工廠方法模式最重要的區(qū)別是引入了抽象工廠角色,抽象工廠可以是接口,也可以是抽象類或者具體類,其典型代碼如下所示:


interface Factory {
    public Product factoryMethod();
}

在實(shí)際使用時(shí),具體工廠類在實(shí)現(xiàn)工廠方法時(shí)除了創(chuàng)建具體產(chǎn)品對(duì)象之外,還可以負(fù)責(zé)產(chǎn)品對(duì)象的初始化工作以及一些資源和環(huán)境配置工作,例如連接數(shù)據(jù)庫(kù)、創(chuàng)建文件等。
在客戶端代碼中,只需關(guān)心工廠類即可,不同的具體工廠可以創(chuàng)建不同的產(chǎn)品,典型的客戶端類代碼片段如下所示:

Factory factory;
factory = new ConcreteFactory(); //可以結(jié)合配置文件和反射機(jī)制來(lái)實(shí)現(xiàn)
Product product;
product = factory.factoryMethod();

可以通過(guò)配置文件來(lái)存儲(chǔ)具體工廠類ConcreteFactory的類名,更換新的具體工廠時(shí)無(wú)須修改源代碼,系統(tǒng)擴(kuò)展更為方便。

工廠方法模式總結(jié)

  • 優(yōu)點(diǎn)
    • 在工廠方法模式中,工廠方法用來(lái)創(chuàng)建客戶所需要的產(chǎn)品,同時(shí)還向客戶隱藏了哪種具體產(chǎn)品類將被實(shí)例化這一細(xì)節(jié),用戶只需要關(guān)心所需產(chǎn)品對(duì)應(yīng)的工廠,無(wú)須關(guān)心創(chuàng)建細(xì)節(jié),甚至無(wú)須知道具體產(chǎn)品類的類名。
    • 基于工廠角色和產(chǎn)品角色的多態(tài)性設(shè)計(jì)是工廠方法模式的關(guān)鍵。它能夠讓工廠可以自主確定創(chuàng)建何種產(chǎn)品對(duì)象,而如何創(chuàng)建這個(gè)對(duì)象的細(xì)節(jié)則完全封裝在具體工廠內(nèi)部。工廠方法模式之所以又被稱為多態(tài)工廠模式,就正是因?yàn)樗械木唧w工廠類都具有同一抽象父類。
    • 使用工廠方法模式的另一個(gè)優(yōu)點(diǎn)是在系統(tǒng)中加入新產(chǎn)品時(shí),無(wú)須修改抽象工廠和抽象產(chǎn)品提供的接口,無(wú)須修改客戶端,也無(wú)須修改其他的具體工廠和具體產(chǎn)品,而只要添加一個(gè)具體工廠和具體產(chǎn)品就可以了,這樣,系統(tǒng)的可擴(kuò)展性也就變得非常好,完全符合“開閉原則”。
  • 缺點(diǎn)
    • 在添加新產(chǎn)品時(shí),需要編寫新的具體產(chǎn)品類,而且還要提供與之對(duì)應(yīng)的具體工廠類,系統(tǒng)中類的個(gè)數(shù)將成對(duì)增加,在一定程度上增加了系統(tǒng)的復(fù)雜度,有更多的類需要編譯和運(yùn)行,會(huì)給系統(tǒng)帶來(lái)一些額外的開銷。
    • 由于考慮到系統(tǒng)的可擴(kuò)展性,需要引入抽象層,在客戶端代碼中均使用抽象層進(jìn)行定義,增加了系統(tǒng)的抽象性和理解難度,且在實(shí)現(xiàn)時(shí)可能需要用到DOM、反射等技術(shù),增加了系統(tǒng)的實(shí)現(xiàn)難度。
  • 適用場(chǎng)景
    • 客戶端不知道它所需要的對(duì)象的類。在工廠方法模式中,客戶端不需要知道具體產(chǎn)品類的類名,只需要知道所對(duì)應(yīng)的工廠即可,具體的產(chǎn)品對(duì)象由具體工廠類創(chuàng)建,可將具體工廠類的類名存儲(chǔ)在配置文件或數(shù)據(jù)庫(kù)中。
    • 抽象工廠類通過(guò)其子類來(lái)指定創(chuàng)建哪個(gè)對(duì)象。在工廠方法模式中,對(duì)于抽象工廠類只需要提供一個(gè)創(chuàng)建產(chǎn)品的接口,而由其子類來(lái)確定具體要?jiǎng)?chuàng)建的對(duì)象,利用面向?qū)ο蟮亩鄳B(tài)性和里氏代換原則,在程序運(yùn)行時(shí),子類對(duì)象將覆蓋父類對(duì)象,從而使得系統(tǒng)更容易擴(kuò)展。

參考:工廠三兄弟之工廠方法模式(三)

抽象工廠模式

抽象工廠模式(Abstract Factory Pattern):提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口,而無(wú)須指定它們具體的類。抽象工廠模式又稱為Kit模式,它是一種對(duì)象創(chuàng)建型模式。

在抽象工廠模式中,每一個(gè)具體工廠都提供了多個(gè)工廠方法用于產(chǎn)生多種不同類型的產(chǎn)品,這些產(chǎn)品構(gòu)成了一個(gè)產(chǎn)品族,抽象工廠模式結(jié)構(gòu)如圖所示:

image.png

在抽象工廠模式結(jié)構(gòu)圖中包含如下幾個(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ù)方法。

在抽象工廠中聲明了多個(gè)工廠方法,用于創(chuàng)建不同類型的產(chǎn)品,抽象工廠可以是接口,也可以是抽象類或者具體類,其典型代碼如下所示:

class ConcreteFactory1 extends AbstractFactory {
        //工廠方法一
        public AbstractProductA createProductA() {
            return new ConcreteProductA1();
        }

        //工廠方法二
        public AbstractProductB createProductB() {
            return new ConcreteProductB1();
        }

        ……
}

與工廠方法模式一樣,抽象工廠模式也可為每一種產(chǎn)品提供一組重載的工廠方法,以不同的方式對(duì)產(chǎn)品對(duì)象進(jìn)行創(chuàng)建。

具體工廠實(shí)現(xiàn)了抽象工廠,每一個(gè)具體的工廠方法可以返回一個(gè)特定的產(chǎn)品對(duì)象,而同一個(gè)具體工廠所創(chuàng)建的產(chǎn)品對(duì)象構(gòu)成了一個(gè)產(chǎn)品族。

開閉原則”的傾斜性

在抽象工廠模式中,增加新的產(chǎn)品族很方便,但是增加新的產(chǎn)品等級(jí)結(jié)構(gòu)很麻煩,抽象工廠模式的這種性質(zhì)稱為“開閉原則”的傾斜性。“開閉原則”要求系統(tǒng)對(duì)擴(kuò)展開放,對(duì)修改封閉,通過(guò)擴(kuò)展達(dá)到增強(qiáng)其功能的目的,對(duì)于涉及到多個(gè)產(chǎn)品族與多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)的系統(tǒng),其功能增強(qiáng)包括兩方面:

  • 增加產(chǎn)品族:對(duì)于增加新的產(chǎn)品族,抽象工廠模式很好地支持了“開閉原則”,只需要增加具體產(chǎn)品并對(duì)應(yīng)增加一個(gè)新的具體工廠,對(duì)已有代碼無(wú)須做任何修改。
  • 加新的產(chǎn)品等級(jí)結(jié)構(gòu):對(duì)于增加新的產(chǎn)品等級(jí)結(jié)構(gòu),需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生產(chǎn)新產(chǎn)品的方法,違背了“開閉原則”。

正因?yàn)槌橄蠊S模式存在“開閉原則”的傾斜性,它以一種傾斜的方式來(lái)滿足“開閉原則”,為增加新產(chǎn)品族提供方便,但不能為增加新產(chǎn)品結(jié)構(gòu)提供這樣的方便,因此要求設(shè)計(jì)人員在設(shè)計(jì)之初就能夠全面考慮,不會(huì)在設(shè)計(jì)完成之后向系統(tǒng)中增加新的產(chǎn)品等級(jí)結(jié)構(gòu),也不會(huì)刪除已有的產(chǎn)品等級(jí)結(jié)構(gòu),否則將會(huì)導(dǎo)致系統(tǒng)出現(xiàn)較大的修改,為后續(xù)維護(hù)工作帶來(lái)諸多麻煩。

抽象工廠模式總結(jié)

  • 優(yōu)點(diǎn)
    • 抽象工廠模式隔離了具體類的生成,使得客戶并不需要知道什么被創(chuàng)建。由于這種隔離,更換一個(gè)具體工廠就變得相對(duì)容易,所有的具體工廠都實(shí)現(xiàn)了抽象工廠中定義的那些公共接口,因此只需改變具體工廠的實(shí)例,就可以在某種程度上改變整個(gè)軟件系統(tǒng)的行為。
    • 當(dāng)一個(gè)產(chǎn)品族中的多個(gè)對(duì)象被設(shè)計(jì)成一起工作時(shí),它能夠保證客戶端始終只使用同一個(gè)產(chǎn)品族中的對(duì)象。
    • 增加新的產(chǎn)品族很方便,無(wú)須修改已有系統(tǒng),符合“開閉原則”。
  • 缺點(diǎn)
    • 增加新的產(chǎn)品等級(jí)結(jié)構(gòu)麻煩,需要對(duì)原有系統(tǒng)進(jìn)行較大的修改,甚至需要修改抽象層代碼,這顯然會(huì)帶來(lái)較大的不便,違背了“開閉原則”。
  • 適用場(chǎng)景
    • 一個(gè)系統(tǒng)不應(yīng)當(dā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)品族,而每次只使用其中某一產(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)或者刪除已有的產(chǎn)品等級(jí)結(jié)構(gòu)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容