設計模式 – 抽象工廠

f1uLove發表於2019-02-20

在講解抽象工廠前我們首先需要了解兩個概念:

  • 產品等級結構:產品的等級結構即產品的繼承結構。如果一個抽象類是電視機,其子類有海爾電視機、海信電視機、創維電視機,則抽象電視機與具體品牌的電視機之間構成了一個產品等級結構,抽象電視機是父類,而具體品牌的電視機是其子類。

  • **產品族:**在抽象工廠模式中,產品族是指由一個工廠生產的,位於不同產品等級結構中的一組產品,如海爾電器生產的海爾電視機、海爾冰箱,海爾電視機位於電視機產品等級結構中,海爾冰箱位於冰箱產品等級結構中。海爾電視機,海爾冰箱構成了一個產品族。

    產品結構&產品族

圖中一共有四個產品族,分佈於三個不同的產品等級結構中。只要指明一個產品所處的產品族以及它所屬的等級結構,就可以唯一的確定這個產品。

引進抽象工廠模式

所謂的抽象工廠是指一個工廠等級結構可以建立出分屬於不同產品等級結構的一個產品族中的所有物件。如果用圖來描述的話,如下圖:

抽象工廠

定義

抽象工廠模式Abstract Factory),提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們的具體類。

結構

抽象工廠

抽象工廠包含如下角色:

  • IFactory:抽象工廠
    抽象工廠定義了一個介面,所有的具體工廠都必須實現此介面,這個介面包含一組方法用來生產產品。
  • ConCreteFactory:具體工廠
    具體工廠實現生成不同的具體產品家族,要建立一個產品,客戶端主要使用其中一個工廠而不需要例項化任何產品物件。
  • IProduct:抽象產品
    不同的抽象產品為不同的產品家族,每一個具體的工廠都能實現一整套的產品
  • ConcreteProduct:具體產品
    對抽象產品的具體分類的實現

場景示例

說起抽象工廠不得不讓人想起位於各大城市中魚龍混雜的電子城。只有你想不到的,沒有他們生產不出來的。 500塊的Iphone你買到過嗎?你見到過裝有安卓系統、小米攝像頭的蘋果手機嗎?下面讓我們一起來見識一下裝有安卓手機系統的蘋果手機是怎麼在電子城生產出來的。
手機賣家,也就是我們的手機工廠,使用主機板、攝像頭、手機外殼就能組裝出一臺手機:

public interface IFactory {
    MainBoard createMainBoard();

    CameraLens createCameraLens();

    Case createCase();

}
複製程式碼

然後無良賣家根據組裝流程給組裝了三種型別的手機

/**
 * 蘋果手機
 */
public class AppleFactory implements IFactory {
    @Override
    public MainBoard createMainBoard() {
        return new IosMainBoard();
    }

    @Override
    public CameraLens createCameraLens() {
        return new IosCameraLens();
    }

    @Override
    public Case createCase() {
        return new IosCase();
    }
}

/**
 * 組裝Android手機
 */
public class AndroidFactory implements IFactory {
    @Override
    public MainBoard createMainBoard() {
        return new AndroidMainBoard();
    }

    @Override
    public CameraLens createCameraLens() {
        return new AndroidCameraLens();
    }

    @Override
    public Case createCase() {
        return new AndroidCase();
    }
}

/**
 * 組裝裝有安卓作業系統、安卓攝像頭的蘋果手機
 */
public class AndroidAppleFactory implements IFactory {
    @Override
    public MainBoard createMainBoard() {
        return new AndroidMainBoard();
    }

    @Override
    public CameraLens createCameraLens() {
        return new AndroidCameraLens();
    }

    @Override
    public Case createCase() {
        return new IosCase();
    }
}
複製程式碼

不同的配件產品

public class AndroidCameraLens implements CameraLens {
    @Override
    public String getCameraLens() {
        return "安卓攝像頭";
    }
}

public class AndroidCase implements Case {
    @Override
    public String getCase() {
        return "安卓手機外殼";
    }
}

public class AndroidMainBoard implements MainBoard {
    @Override
    public String getMainBoard() {
        return "安卓主機板";
    }
}

public class IosCameraLens implements CameraLens {
    @Override
    public String getCameraLens() {
        return "蘋果攝像頭";
    }
}

public class IosCase implements Case {
    @Override
    public String getCase() {
        return "蘋果手機外殼";
    }
}

public class IosMainBoard implements MainBoard {
    @Override
    public String getMainBoard() {
        return "蘋果手機主機板";
    }
}
複製程式碼

然後顧客來買手機

public class App {
    public static void main(String[] args) {
        IFactory factory = new AppleFactory();
        String mainBoard = factory.createMainBoard().getMainBoard();
        String cameraLens = factory.createCameraLens().getCameraLens();
        String aCase = factory.createCase().getCase();
        System.out.println(String.format("手機配件:%s、%s、%s,售價:12888", mainBoard, cameraLens, aCase));
    }
}
複製程式碼

騷年,新出的Apple XRS 手機配件:裝有蘋果主機板、蘋果攝像頭、蘋果手機外殼,售價:12888,要不要來一臺?
嫌貴啊,沒關係,我這有新出的安卓機:

public class App {
    public static void main(String[] args) {
        IFactory factory = new AndroidFactory();
        String mainBoard = factory.createMainBoard().getMainBoard();
        String cameraLens = factory.createCameraLens().getCameraLens();
        String aCase = factory.createCase().getCase();
        System.out.println(String.format("手機配件:%s、%s、%s,售價:4000", mainBoard, cameraLens, aCase));
    }
}
複製程式碼

手機配件:裝有安卓主機板、安卓攝像頭、安卓手機外殼,售價:4000,超高價效比,只要4000,考不考慮入手?
這樣吧,騷年,你我相遇也是有緣,我這新弄了一臺二手Apple,便宜出你,你看怎麼樣

public class App {
    public static void main(String[] args) {
        IFactory factory = new AndroidAppleFactory();
        String mainBoard = factory.createMainBoard().getMainBoard();
        String cameraLens = factory.createCameraLens().getCameraLens();
        String aCase = factory.createCase().getCase();
        System.out.println(String.format("手機配件:%s、%s、%s,售價:4000", mainBoard, cameraLens, aCase));
    }
}
複製程式碼

手機配件:安卓主機板、安卓攝像頭、蘋果手機外殼,售價:500 只要500,95成新,怎麼樣。

勿佔小便宜啊!!

優點

  • 抽象工廠模式隔離了具體類的生成,使得客戶並不需要知道什麼被建立。由於這種隔離,更換一個具體工廠就變得相對容易。所有的具體工廠都實現了抽象工廠中定義的那些公共介面,因此只需改變具體工廠的例項,就可以在某種程度上改變整個軟體系統的行為。另外,應用抽象工廠模式可以實現高內聚低耦合的設計目的,因此抽象工廠模式得到了廣泛的應用。

  • 當一個產品族中的多個物件被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的物件。這對一些需要根據當前環境來決定其行為的軟體系統來說,是一種非常實用的設計模式。

  • 增加新的具體工廠和產品族很方便,無須修改已有系統,符合開閉原則

缺點

  • 在新增新的產品物件時,難以擴充套件抽象工廠來生產新種類的產品,這是因為在抽象工廠角色中規定了所有可能被建立的產品集合,要支援新種類的產品就意味著要對該介面進行擴充套件,而這將涉及到對抽象工廠角色及其所有子類的修改,顯然會帶來較大的不便。

  • 開閉原則的傾斜性(增加新的工廠和產品族容易,增加新的產品等級結構麻煩)。

應用場景

  • 當需要建立的物件是一系列相互關聯或相互依賴的產品族時,便可以使用抽象工廠模式。
  • 系統中有多於一個的產品族,而每次只使用其中某一產品族。
  • 屬於同一個產品族的產品將在一起使用,這一約束必須在系統的設計中體現出來。
  • 系統提供一個產品類的庫,所有的產品以同樣的介面出現,從而使客戶端不依賴於具體實現。

簡單來講,就是一個繼承體系中,如果存在著多個等級結構(即存在著多個抽象類),並且分屬各個等級結構中的實現類之間存在著一定的關聯或者約束,就可以使用抽象工廠模式。假如各個等級結構中的實現類之間不存在關聯或約束,則使用多個獨立的工廠來對產品進行建立,則更合適一點。

“開閉原則”的傾斜性

  • “開閉原則”要求系統對擴充套件開放,對修改封閉,通過擴充套件達到增強其功能的目的。對於涉及到多個產品族與多個產品等級結構的系統,其功能增強包括兩方面:

    1. 增加產品族:對於增加新的產品族,工廠方法模式很好的支援了“開閉原則”,對於新增加的產品族,只需要對應增加一個新的具體工廠即可,對已有程式碼無須做任何修改。

    2. 增加新的產品等級結構:對於增加新的產品等級結構,需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生產新產品的方法,不能很好地支援“開閉原則”。

  • 抽象工廠模式的這種性質稱為“開閉原則”的傾斜性,抽象工廠模式以一種傾斜的方式支援增加新的產品,它為新產品族的增加提供方便,但不能為新的產品等級結構的增加提供這樣的方便。

工廠模式的退化

  • 當抽象工廠模式中每一個具體工廠類只建立一個產品物件,也就是隻存在一個產品等級結構時,抽象工廠模式退化成工廠方法模式;當工廠方法模式中抽象工廠與具體工廠合併,提供一個統一的工廠來建立產品物件,並將建立物件的工廠方法設計為靜態方法時,工廠方法模式退化成簡單工廠模式。

總結

  • 產品等級結構即產品的繼承結構關係。產品族即由一個共產生產的,位於不同產品等級結構中的一組產品。

  • 抽象工廠模式提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們的具體類。抽象工廠模式又稱為Kit模式,屬於物件建立型模式。

  • 抽象工廠包含四個角色:抽象工廠、具體工廠、抽象產品、具體產品。

  • 抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態。抽象工廠模式與工廠方法模式最大的區別在於,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式則需要面對多個產品等級結構。抽象工廠中利用工廠方法實現生產方法是很普遍的做法。

  • 抽象工廠模式的主要優點是隔離了具體類的生成,使得客戶並不需要知道什麼被建立,而且每次可以通過具體工廠類建立一個產品族中的多個物件,增加或者替換產品族比較方便,增加新的具體工廠和產品族很方便;主要缺點在於增加新的產品等級結構很複雜,需要修改抽象工廠和所有的具體工廠類,對“開閉原則”的支援呈現傾斜性。

  • 抽象工廠模式適用情況包括:一個系統不應當依賴於產品類例項如何被建立、組合和表達的細節;系統中有多於一個的產品族,而每次只使用其中某一產品族;屬於同一個產品族的產品將在一起使用;系統提供一個產品類的庫,所有的產品以同樣的介面出現,從而使客戶端不依賴於具體實現。

相關文章