C語言中文網 目錄
首頁 > 設計模式 閱讀:2,113

抽象工廠模式(詳解版)

< 上一頁工廠方法模式 建造者模式下一頁 >

前面介紹的工廠方法模式中考慮的是一類產品的生產,如畜牧場只養動物、電視機廠只生產電視機、計算機軟件學院只培養計算機軟件專業的學生等。

同種類稱為同等級,也就是說:工廠方法模式只考慮生產同等級的產品,但是在現實生活中許多工廠是綜合型的工廠,能生產多等級(種類) 的產品,如農場里既養動物又種植物,電器廠既生產電視機又生產洗衣機或空調,大學既有軟件專業又有生物專業等。

本節要介紹的抽象工廠模式將考慮多等級產品的生產,將同一個具體工廠所生產的位于不同等級的一組產品稱為一個產品族,圖 1 所示的是海爾工廠和 TCL 工廠所生產的電視機與空調對應的關系圖。

電器工廠的產品等級與產品族
圖1 電器工廠的產品等級與產品族

模式的定義與特點

抽象工廠(AbstractFactory)模式的定義:是一種為訪問類提供一個創建一組相關或相互依賴對象的接口,且訪問類無須指定所要產品的具體類就能得到同族的不同等級的產品的模式結構。

抽象工廠模式是工廠方法模式的升級版本,工廠方法模式只生產一個等級的產品,而抽象工廠模式可生產多個等級的產品。

使用抽象工廠模式一般要滿足以下條件。
  • 系統中有多個產品族,每個具體工廠創建同一族但屬于不同等級結構的產品。
  • 系統一次只可能消費其中某一族產品,即同族的產品一起使用。

抽象工廠模式除了具有工廠方法模式的優點外,其他主要優點如下。
  • 可以在類的內部對產品族中相關聯的多等級產品共同管理,而不必專門引入多個新的類來進行管理。
  • 當增加一個新的產品族時不需要修改原代碼,滿足開閉原則。

其缺點是:當產品族中需要增加一個新的產品時,所有的工廠類都需要進行修改。

模式的結構與實現

抽象工廠模式同工廠方法模式一樣,也是由抽象工廠、具體工廠、抽象產品和具體產品等 4 個要素構成,但抽象工廠中方法個數不同,抽象產品的個數也不同?,F在我們來分析其基本結構和實現方法。

1. 模式的結構

抽象工廠模式的主要角色如下。
  1. 抽象工廠(Abstract Factory):提供了創建產品的接口,它包含多個創建產品的方法 newProduct(),可以創建多個不同等級的產品。
  2. 具體工廠(Concrete Factory):主要是實現抽象工廠中的多個抽象方法,完成具體產品的創建。
  3. 抽象產品(Product):定義了產品的規范,描述了產品的主要特性和功能,抽象工廠模式有多個抽象產品。
  4. 具體產品(ConcreteProduct):實現了抽象產品角色所定義的接口,由具體工廠來創建,它 同具體工廠之間是多對一的關系。

抽象工廠模式的結構圖如圖 2 所示。

抽象工廠模式的結構圖
圖2 抽象工廠模式的結構圖

2. 模式的實現

從圖 2 可以看出抽象工廠模式的結構同工廠方法模式的結構相似,不同的是其產品的種類不止一個,所以創建產品的方法也不止一個。下面給出抽象工廠和具體工廠的代碼。

(1) 抽象工廠:提供了產品的生成方法。
interface AbstractFactory
{
    public Product1 newProduct1();
    public Product2 newProduct2();
}

(2) 具體工廠:實現了產品的生成方法。
class ConcreteFactory1 implements AbstractFactory
{
    public Product1 newProduct1()
    {
        System.out.println("具體工廠 1 生成-->具體產品 11...");
        return new ConcreteProduct11();
    }
    public Product2 newProduct2()
    {
        System.out.println("具體工廠 1 生成-->具體產品 21...");
        return new ConcreteProduct21();
    }
}

模式的應用實例

【例1】用抽象工廠模式設計農場類。

分析:農場中除了像畜牧場一樣可以養動物,還可以培養植物,如養馬、養牛、種菜、種水果等,所以本實例比前面介紹的畜牧場類復雜,必須用抽象工廠模式來實現。

本例用抽象工廠模式來設計兩個農場,一個是韶關農場用于養牛和種菜,一個是上饒農場用于養馬和種水果,可以在以上兩個農場中定義一個生成動物的方法 newAnimal() 和一個培養植物的方法 newPlant()。

對馬類、牛類、蔬菜類和水果類等具體產品類,由于要顯示它們的圖像(點此下載圖片),所以它們的構造函數中用到了 JPanel、JLabel 和 ImageIcon 等組件,并定義一個 show() 方法來顯示它們。

客戶端程序通過對象生成器類 ReadXML 讀取 XML 配置文件中的數據來決定養什么動物和培養什么植物(點此下載 XML 文件)。其結構圖如圖 3 所示。

農場類的結構圖
圖3 農場類的結構圖

程序代碼如下:
package AbstractFactory;
import java.awt.*;
import javax.swing.*;
public class FarmTest
{
    public static void main(String[] args)
    {
        try
        {          
            Farm f;
            Animal a;
            Plant p;
            f=(Farm) ReadXML.getObject();
            a=f.newAnimal();
            p=f.newPlant();
            a.show();
            p.show();
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
    }
}
//抽象產品:動物類
interface Animal
{
    public void show();
}
//具體產品:馬類
class Horse implements Animal
{
    JScrollPane sp;
    JFrame jf=new JFrame("抽象工廠模式測試");
    public Horse()
    {
        Container contentPane=jf.getContentPane();
        JPanel p1=new JPanel();
        p1.setLayout(new GridLayout(1,1));
        p1.setBorder(BorderFactory.createTitledBorder("動物:馬"));
        sp=new JScrollPane(p1);
        contentPane.add(sp, BorderLayout.CENTER);
        JLabel l1=new JLabel(new ImageIcon("src/A_Horse.jpg"));
        p1.add(l1);       
        jf.pack();       
        jf.setVisible(false);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用戶點擊窗口關閉 
    }
    public void show()
    {
        jf.setVisible(true);
    }
}
//具體產品:牛類
class Cattle implements Animal
{
    JScrollPane sp;
    JFrame jf=new JFrame("抽象工廠模式測試");
    public Cattle() {
        Container contentPane=jf.getContentPane();
        JPanel p1=new JPanel();
        p1.setLayout(new GridLayout(1,1));
        p1.setBorder(BorderFactory.createTitledBorder("動物:牛"));
        sp=new JScrollPane(p1);
        contentPane.add(sp, BorderLayout.CENTER);
        JLabel l1=new JLabel(new ImageIcon("src/A_Cattle.jpg"));
        p1.add(l1);       
        jf.pack();       
        jf.setVisible(false);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用戶點擊窗口關閉 
    }
    public void show()
    {
        jf.setVisible(true);
    }
}
//抽象產品:植物類
interface Plant
{
    public void show();
}
//具體產品:水果類
class Fruitage implements Plant
{
    JScrollPane sp;
    JFrame jf=new JFrame("抽象工廠模式測試");
    public Fruitage()
    {
        Container contentPane=jf.getContentPane();
        JPanel p1=new JPanel();
        p1.setLayout(new GridLayout(1,1));
        p1.setBorder(BorderFactory.createTitledBorder("植物:水果"));
        sp=new JScrollPane(p1);
        contentPane.add(sp, BorderLayout.CENTER);
        JLabel l1=new JLabel(new ImageIcon("src/P_Fruitage.jpg"));
        p1.add(l1);       
        jf.pack();       
        jf.setVisible(false);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用戶點擊窗口關閉 
    }
    public void show()
    {
        jf.setVisible(true);
    }
}
//具體產品:蔬菜類
class Vegetables implements Plant
{
    JScrollPane sp;
    JFrame jf=new JFrame("抽象工廠模式測試");
    public Vegetables()
    {
        Container contentPane=jf.getContentPane();
        JPanel p1=new JPanel();
        p1.setLayout(new GridLayout(1,1));
        p1.setBorder(BorderFactory.createTitledBorder("植物:蔬菜"));
        sp=new JScrollPane(p1);
        contentPane.add(sp, BorderLayout.CENTER);
        JLabel l1=new JLabel(new ImageIcon("src/P_Vegetables.jpg"));
        p1.add(l1);       
        jf.pack();       
        jf.setVisible(false);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用戶點擊窗口關閉 
    }
    public void show()
    {
        jf.setVisible(true);
    }
}
//抽象工廠:農場類
interface Farm
{
    public Animal newAnimal();
    public Plant newPlant();
}
//具體工廠:韶關農場類
class SGfarm implements Farm
{
    public Animal newAnimal()
    {
        System.out.println("新牛出生!");
        return new Cattle();
    }
    public Plant newPlant()
    {
        System.out.println("蔬菜長成!");
        return new Vegetables();
    }
}
//具體工廠:上饒農場類
class SRfarm implements Farm
{
    public Animal newAnimal()
    {
        System.out.println("新馬出生!");
        return new Horse();
    }
    public Plant newPlant()
    {
        System.out.println("水果長成!");
        return new Fruitage();
    }
}
package AbstractFactory;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;
class ReadXML
{
    public static Object getObject()
    {
        try
        {
            DocumentBuilderFactory dFactory=DocumentBuilderFactory.newInstance();
            DocumentBuilder builder=dFactory.newDocumentBuilder();
            Document doc;                           
            doc=builder.parse(new File("src/AbstractFactory/config.xml"));
            NodeList nl=doc.getElementsByTagName("className");
            Node classNode=nl.item(0).getFirstChild();
            String cName="AbstractFactory."+classNode.getNodeValue();
            System.out.println("新類名:"+cName);
            Class<?> c=Class.forName(cName);
              Object obj=c.newInstance();
            return obj;
        }  
        catch(Exception e)
        {
               e.printStackTrace();
               return null;
        }
    }
}

程序運行結果如圖 4 所示。

農場養殖的運行結果
圖4 農場養殖的運行結果

模式的應用場景

抽象工廠模式最早的應用是用于創建屬于不同操作系統的視窗構件。如 java 的 AWT 中的 Button 和 Text 等構件在 Windows 和 UNIX 中的本地實現是不同的。

抽象工廠模式通常適用于以下場景:
  1. 當需要創建的對象是一系列相互關聯或相互依賴的產品族時,如電器工廠中的電視機、洗衣機、空調等。
  2. 系統中有多個產品族,但每次只使用其中的某一族產品。如有人只喜歡穿某一個品牌的衣服和鞋。
  3. 系統中提供了產品的類庫,且所有產品的接口相同,客戶端不依賴產品實例的創建細節和內部結構。

模式的擴展

抽象工廠模式的擴展有一定的“開閉原則”傾斜性:
  1. 當增加一個新的產品族時只需增加一個新的具體工廠,不需要修改原代碼,滿足開閉原則。
  2. 當產品族中需要增加一個新種類的產品時,則所有的工廠類都需要進行修改,不滿足開閉原則。

另一方面,當系統中只存在一個等級結構的產品時,抽象工廠模式將退化到工廠方法模式。
< 上一頁工廠方法模式 建造者模式下一頁 >

精美而實用的網站,提供C語言、C++、STL、Linux、Shell、Java、Go語言等教程,以及socket、GCC、vi、Swing、設計模式、JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo