Java與設計模式(十一)創建型--抽象工廠模式

工廠方法模式通過引入工廠等級結構,解決了簡單工廠模式中工廠類職責太重的問題,但由於工廠方法模式中的每個工廠只生產一類產品,可能會導致系統中存在大量的工廠類,勢必會增加系統的開銷。此時,我們可以考慮將一些相關的產品組成一個“產品族”,由同一個工廠來統一生產。

一、需求

比如開發一套界面皮膚庫,可以對Java桌面軟件進行界面美化。爲了保護版權,該皮膚庫源代碼不打算公開,而只向用戶提供已打包爲jar文件的class字節碼文件。用戶在使用時可以通過菜單來選擇皮膚,不同的皮膚將提供視覺效果不同的按鈕、文本框、組合框等界面元素,其結構示意圖如圖所示:

該皮膚庫需要具備良好的靈活性和可擴展性,用戶可以自由選擇不同的皮膚,開發人員可以在不修改既有代碼的基礎上增加新的皮膚。使用工廠方法模式進行系統的設計,爲了保證系統的靈活性和可擴展性,提供一系列具體工廠來創建按鈕、文本框、組合框等界面元素,客戶端針對抽象工廠編程,初始結構如圖所示:

在上圖中,提供了大量工廠來創建具體的界面組件,可以通過配置文件更換具體界面組件從而改變界面風格。但是,此設計方案存在如下問題:

  • 當需要增加新的皮膚時,雖然不要修改現有代碼,但是需要增加大量類,針對每一個新增具體組件都需要增加一個具體工廠,類的個數成對增加,這無疑會導致系統越來越龐大,增加系統的維護成本和運行開銷;
  • 由於同一種風格的具體界面組件通常要一起顯示,因此需要爲每個組件都選擇一個具體工廠,用戶在使用時必須逐個進行設置,如果某個具體工廠選擇失誤將會導致界面顯示混亂,雖然我們可以適當增加一些約束語句,但客戶端代碼和配置文件都較爲複雜。

如何減少系統中類的個數並保證客戶端每次始終只使用某一種風格的具體界面組件?這是Sunny公司開發人員所面臨的兩個問題,顯然,工廠方法模式無法解決這兩個問題,彆着急,抽象工廠模式可以讓這些問題迎刃而解。

二、產品等級結構與產品族

在工廠方法模式中具體工廠負責生產具體的產品,每一個具體工廠對應一種具體產品,工廠方法具有唯一性,一般情況下,一個具體工廠中只有一個或者一組重載的工廠方法。但是有時候我們希望一個工廠可以提供多個產品對象,而不是單一的產品對象,如一個電器工廠,它可以生產電視機、電冰箱、空調等多種電器,而不是隻生產某一種電器。爲了更好地理解抽象工廠模式,我們先引入兩個概念:

  • 產品等級結構:產品等級結構即產品的繼承結構,如一個抽象類是電視機,其子類有海爾電視機、海信電視機、TCL電視機,則抽象電視機與具體品牌的電視機之間構成了一個產品等級結構,抽象電視機是父類,而具體品牌的電視機是其子類。
  • 產品族:在抽象工廠模式中,產品族是指由同一個工廠生產的,位於不同產品等級結構中的一組產品,如海爾電器工廠生產的海爾電視機、海爾電冰箱,海爾電視機位於電視機產品等級結構中,海爾電冰箱位於電冰箱產品等級結構中,海爾電視機、海爾電冰箱構成了一個產品族。

產品等級結構與產品族示意圖如圖所示

在上圖中,不同顏色的多個正方形、圓形和橢圓形分別構成了三個不同的產品等級結構,而相同顏色的正方形、圓形和橢圓形構成了一個產品族,每一個形狀對象都位於某個產品族,並屬於某個產品等級結構。上圖中一共有五個產品族,分屬於三個不同的產品等級結構。我們只要指明一個產品所處的產品族以及它所屬的等級結構,就可以唯一確定這個產品。

當系統所提供的工廠生產的具體產品並不是一個簡單的對象,而是多個位於不同產品等級結構、屬於不同類型的具體產品時就可以使用抽象工廠模式。抽象工廠模式是所有形式的工廠模式中最爲抽象和最具一般性的一種形式。抽象工廠模式與工廠方法模式最大的區別在於,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式需要面對多個產品等級結構,一個工廠等級結構可以負責多個不同產品等級結構中的產品對象的創建。當一個工廠等級結構可以創建出分屬於不同產品等級結構的一個產品族中的所有對象時,抽象工廠模式比工廠方法模式更爲簡單、更有效率。抽象工廠模式示意圖如圖所示:

在上圖中,每一個具體工廠可以生產屬於一個產品族的所有產品,例如生產顏色相同的正方形、圓形和橢圓形,所生產的產品又位於不同的產品等級結構中。如果使用工廠方法模式,上圖所示結構需要提供15個具體工廠,而使用抽象工廠模式只需要提供5個具體工廠,極大減少了系統中類的個數。比如格力工廠生產冰箱(正方形)、洗衣機(圓形)、彩電(橢圓形)。

三、抽象工廠模式概述

抽象工廠模式爲創建一組對象提供了一種解決方案。與工廠方法模式相比,抽象工廠模式中的具體工廠不只是創建一種產品,它負責創建一族產品。抽象工廠模式定義如下:

抽象工廠模式定義

抽象工廠模式(Abstract Factory Pattern):提供一個創建一系列相關或相互依賴對象的接口,而無須指定它們具體的類。抽象工廠模式又稱爲Kit模式,它是一種對象創建型模式

在抽象工廠模式中,每一個具體工廠都提供了多個工廠方法用於產生多種不同類型的產品,這些產品構成了一個產品族,抽象工廠模式結構如圖所示:

抽象工廠模式中的角色

  • AbstractFactory(抽象工廠):它聲明瞭一組用於創建一族產品的方法,每一個方法對應一種產品。
  • ConcreteFactory(具體工廠):它實現了在抽象工廠中聲明的創建產品的方法,生成一組具體產品,這些產品構成了一個產品族,每一個產品都位於某個產品等級結構中。
  • AbstractProduct(抽象產品):它爲每種產品聲明接口,在抽象產品中聲明瞭產品所具有的業務方法。
  • ConcreteProduct(具體產品):它定義具體工廠生產的具體產品對象,實現抽象產品接口中聲明的業務方法。

抽象工廠模式代碼示例

先看一下類圖:

我們將創建 Shape 和 Color 接口和實現這些接口的實體類。下一步是創建抽象工廠類 AbstractFactory。接着定義工廠類 ShapeFactory 和 ColorFactory,這兩個工廠類都是擴展了 AbstractFactory。然後創建一個工廠創造器/生成器類 FactoryProducer。

AbstractFactoryPatternDemo,我們的演示類使用 FactoryProducer 來獲取 AbstractFactory 對象。它將向 AbstractFactory 傳遞形狀信息 Shape(CIRCLE / RECTANGLE / SQUARE),以便獲取它所需對象的類型。同時它還向 AbstractFactory 傳遞顏色信息 Color(RED / GREEN / BLUE),以便獲取它所需對象的類型。代碼如下:

形狀相關類

public interface Shape {
   void draw();
}

public class Rectangle implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

public class Square implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}

public class Circle implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}

顏色相關類

public interface Color {
   void fill();
}

public class Red implements Color {

   @Override
   public void fill() {
      System.out.println("Inside Red::fill() method.");
   }
}

public class Green implements Color {

   @Override
   public void fill() {
      System.out.println("Inside Green::fill() method.");
   }
}

public class Blue implements Color {

   @Override
   public void fill() {
      System.out.println("Inside Blue::fill() method.");
   }
}

爲 Color 和 Shape 對象創建抽象類來獲取工廠

public abstract class AbstractFactory {
   abstract Color getColor(String color);
   abstract Shape getShape(String shape) ;
}

創建擴展了 AbstractFactory 的工廠類,基於給定的信息生成實體類的對象。

public class ShapeFactory extends AbstractFactory {

   @Override
   public Shape getShape(String shapeType){
      if(shapeType == null){
         return null;
      }        
      if(shapeType.equalsIgnoreCase("CIRCLE")){
         return new Circle();
      } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }
      return null;
   }

   @Override
   Color getColor(String color) {
      return null;
   }
}

public class ColorFactory extends AbstractFactory {

   @Override
   public Shape getShape(String shapeType){
      return null;
   }

   @Override
   Color getColor(String color) {
      if(color == null){
         return null;
      }        
      if(color.equalsIgnoreCase("RED")){
         return new Red();
      } else if(color.equalsIgnoreCase("GREEN")){
         return new Green();
      } else if(color.equalsIgnoreCase("BLUE")){
         return new Blue();
      }
      return null;
   }
}

創建一個工廠創造器/生成器類,通過傳遞形狀或顏色信息來獲取工廠。

public class FactoryProducer {
   public static AbstractFactory getFactory(String choice){
      if(choice.equalsIgnoreCase("SHAPE")){
         return new ShapeFactory();
      } else if(choice.equalsIgnoreCase("COLOR")){
         return new ColorFactory();
      }
      return null;
   }
}

使用 FactoryProducer(客戶端) 來獲取 AbstractFactory,通過傳遞類型信息來獲取實體類的對象


public class AbstractFactoryPatternDemo {
   public static void main(String[] args) {

      //獲取形狀工廠
      AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");

      //獲取形狀爲 Circle 的對象
      Shape shape1 = shapeFactory.getShape("CIRCLE");

      //調用 Circle 的 draw 方法
      shape1.draw();
   }
}

四、抽象工廠模式總結

抽象工廠模式是工廠方法模式的進一步延伸,由於它提供了功能更爲強大的工廠類並且具備較好的可擴展性,在軟件開發中得以廣泛應用,尤其是在一些框架和API類庫的設計中,例如在Java語言的AWT(抽象窗口工具包)中就使用了抽象工廠模式,它使用抽象工廠模式來實現在不同的操作系統中應用程序呈現與所在操作系統一致的外觀界面。抽象工廠模式也是在軟件開發中最常用的設計模式之一。

主要優點

  • 抽象工廠模式隔離了具體類的生成,使得客戶並不需要知道什麼被創建。由於這種隔離,更換一個具體工廠就變得相對容易,所有的具體工廠都實現了抽象工廠中定義的那些公共接口,因此只需改變具體工廠的實例,就可以在某種程度上改變整個軟件系統的行爲。
  • 當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象。
  • 增加新的產品族很方便,無須修改已有系統,符合“開閉原則”。

主要缺點

增加新的產品等級結構麻煩,需要對原有系統進行較大的修改,甚至需要修改抽象層代碼,這顯然會帶來較大的不便,違背了“開閉原則”。

適用場景

  • 一個系統不應當依賴於產品類實例如何被創建、組合和表達的細節,這對於所有類型的工廠模式都是很重要的,用戶無須關心對象的創建過程,將對象的創建和使用解耦。
  • 系統中有多於一個的產品族,而每次只使用其中某一產品族。可以通過配置文件等方式來使得用戶可以動態改變產品族,也可以很方便地增加新的產品族。
  • 屬於同一個產品族的產品將在一起使用,這一約束必須在系統的設計中體現出來。同一個產品族中的產品可以是沒有任何關係的對象,但是它們都具有一些共同的約束,如同一操作系統下的按鈕和文本框,按鈕與文本框之間沒有直接關係,但它們都是屬於某一操作系統的,此時具有一個共同的約束條件:操作系統的類型。
  • 產品等級結構穩定,設計完成之後,不會向系統中增加新的產品等級結構或者刪除已有的產品等級結構。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章