定義
為創建一組相關或相互依賴的對象提供一個接口,而且無需指定他們的具體類。
簡介
當每個抽象產品都有多於一個的具體子類的時候,工廠角色怎么知道實例化哪一個子類呢?比如每個抽象產品 角色都有兩個具體產品。抽象工廠模式提供兩個具體工廠角色,分別對應於這兩個具體產品角色,每一個具體工廠角色只負責某一個產品角色的實例化。每一個具體工廠類只負責創建抽象產品的某一個具體子類的實例。
每一個模式都是針對一定問題的解決方案,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式針對的是多個產品等級結構。
產品族
是指位於不同產品等級結構中,功能相關聯的產品組成的家族。一般是位於不同的等級結構中的相同位置上。顯然,每一個產品族中含有產品的數目,與產品等級結構的數目是相等的,形成一個二維的坐標系,水平坐標是產品等級結構,縱坐標是產品族。叫做相圖。
當有多個不同的等級結構的產品時,如果使用工廠方法模式就勢必要使用多個獨立的工廠等級結構來對付這些產品的等級結構。如果這些產品等級結構是平行的,會導致多個平行的工廠等級結構。
抽象工廠模式使用同一個 工廠等級結構負責這些不同產品等級結構產品對象的創建。
對於每一個產品族,都有一個具體工廠。而每一個具體工廠創建屬於同一個產品族,但是分屬於不同等級結構的產品。
通過引進抽象工廠模式,可以處理具有相同(或者相似)等級結構的多個產品族中的產品對象的創建問題。
由於每個具體工廠角色都需要負責兩個不同等級結構的產品對象的創建,因此每個工廠角色都需要提供兩個工廠方法,分別用於創建兩個等級結構的產品。既然每個具體工廠角色都需要實現這兩個工廠方法,所以具有一般性,不妨抽象出來,移動到抽象工廠角色中加以聲明。
類型
public interface Creator{
public ProductA factoryA();
public ProductB factoryB();
}
具體工廠
類別一:
public class ConcreteCreator1 implements Creator{
public ProductA factoryA(){
return new ProductA1();
}
public ProductB factoryB(){
return new ProductB1();
}
}
類別二:
public class ConcreteCreator2 implements Creator{
public ProductA factoryA(){
return new ProductA2();
}
public ProductB factoryB(){
return new ProductB2();
}
}
一般而言,有多少個產品等級結構,就會在工廠角色中發現多少個工廠方法。每一個產品等級結構中有多少個具體的產品,就有多少個產品族,也就會在工廠等級結構中發現多少個具體工廠。
抽象產品
類別A
public interface ProductA
{
}
類別B
public interface ProductB
{
}
具體產品類
ProdcutA1:
public class ProductA1 implements ProductA
{
public ProductA1()
{
}
}
ProdcutA2:
public class ProductA2 implements ProductA
{
public ProductA2()
{
}
}:
ProdcutA1:
public class ProductB1 implements ProductB
{
public ProductB1()
{
}
}
public class ProductB2 implements ProductB
{
public ProductB2()
{
}
}
在真實的系統中,產品等級結構的數目與每個產品等級結構中產品的數目(產品族)一般是不相等的。
使用情況
1.系統不依賴於產品類實例如何被創建,組合和表達的細節。
2.系統的產品有多於一個的產品族,而系統只消費其中某一族的產品(抽象工廠模式的原始用意Unix&Windows)
Button--->UnixButton/WinButton
Text----->UnixText/WinText
Unix產品族和Windows產品族,不會同時使用。
Factory--->UnixFactory/WinFactory
3.同屬於同一個產品族是在一起使用的。這一約束必須在系統的設計中體現出來。
4.系統提供一個產品類的庫,所有產品以同樣的接口出現,從而使客戶端不依賴於實現。
農場系統
在農場系統的實現
//兩種抽象產品:水果、蔬菜
public interface Fruit
{
}
public interface Veggie
{
}
//四種具體產品:北方水果,熱帶水果,北方蔬菜,熱帶蔬菜
//Northern Fruit
public class NorthernFruit implements Fruit
{
private String name;
public NorthernFruit(String name)
{
}
public String getName()
{
return name;
}
public void setName(String name)
{
this. name = name;
}
}
//TropicalFruit
public class TropicalFruit implements Fruit
{
private String name;
public TropicalFruit(String name)
{
}
public String getName()
{
return name;
}
public void setName(String name)
{
this. name = name;
}
}
//NorthernVeggie
public class NorthernVeggie implements Veggie
{
private String name;
public NorthernVeggie(String name)
{
}
public String getName()
{
return name;
}
public void setName(String name)
{
this. name = name;
}
}
//TropicalVeggie
public class TropicalVeggie implements Veggie
{
private String name;
public TropicalVeggie(String name)
{
}
public String getName()
{
return name;
}
public void setName(String name)
{
this. name = name;
}
}
//抽象工廠角色
public interface Gardener
{
public Fruit createFruit(String name);
public Veggie createVeggie(String name);
}
//具體工廠角色:北方工廠,熱帶角色
public class NorthernGardener implements Gardener
{
public Fruit createFruit(String name)
{
return new NorthernFruit(name);
}
public Veggie createVeggie(String name)
{
return new NorthernVeggie(name);
}
}
public class TropicalGardener implements Gardener
{
public Fruit createFruit(String name)
{
return new TropicalFruit(name);
}
public Veggie createVeggie(String name)
{
return new TropicalVeggie(name);
}
}
這樣客戶端只需要創建具體工廠的實例,然後調用工廠對象的工廠方法就可以得到所需要的產品對象。
第二個例子:C++實現
例子:資料庫訪問程式設計,不同的資料庫訪問方式可能不一樣,
為了抽象對不同資料庫的訪問,可以將資料庫隱藏起來,提供統一的訪問方式,用多態進行實現
#include <iostream>
using namespace std;
//用戶表接口
class IUser
{
public:
virtual void Insert() = 0;
virtual void GetUser() = 0;
};
//SqlServer資料庫訪問User表
class SqlServerUser : public IUser
{
public:
void Insert()
{
cout<<"在SQL Server中給User表增加一條記錄"<<endl;
}
void GetUser()
{
cout<<"在SQL Server中給User表獲取一條記錄"<<endl;
}
};
//Access資料庫訪問User表
class AccessUser : public IUser
{
public:
void Insert()
{
cout<<"在Access中給User表增加一條記錄"<<endl;
}
void GetUser()
{
cout<<"在Access中User表獲取一條記錄"<<endl;
}
};
//Department表接口
class IDepartment
{
public:
virtual void Insert() = 0;
virtual void GetDepartment() = 0;
};
//SqlServer資料庫訪問Department表
class SqlServerDepartment : public IDepartment
{
public:
void Insert()
{
cout<<"在SQL Server中給Department表增加一條記錄"<<endl;
}
void GetDepartment()
{
cout<<"在SQL Server中Department獲取一條記錄"<<endl;
};
};
//Access資料庫訪問Department表
class AccessDepartment : public IDepartment
{
public:
void Insert()
{
cout<<"在Access中給Department表增加一條記錄"<<endl;
}
void GetDepartment()
{
cout<<"在Access中Department獲取一條記錄"<<endl;
};
};
//抽象工廠接口
class IFactory
{
public:
virtual IUser* CreateUser() = 0;
virtual IDepartment* CreateDepartment() = 0;
};
//SqlServer工廠實現
class SqlServerFactory : public IFactory
{
IUser* CreateUser()
{
return new SqlServerUser();
}
IDepartment* CreateDepartment()
{
return new SqlServerDepartment();
}
};
//Access工廠實現
class AccessFactory : public IFactory
{
IUser* CreateUser()
{
return new AccessUser();
}
IDepartment* CreateDepartment()
{
return new AccessDepartment();
}
};
int main()
{
//創建工廠
IFactory * pFactory = NULL;
IUser * pUser = NULL;
IDepartment * pDepartment = NULL;
int choise;
cout<<"選擇資料庫: ";
cin>>choise;
switch(choise)
{
case 1:
pFactory= new SqlServerFactory(); //創建SqlServer訪問的工廠
break;
case 2:
pFactory = new AccessFactory(); //創建Access訪問的工廠
break;
}
//一致的操作
pUser = pFactory->CreateUser();
pDepartment= pFactory->CreateDepartment();
pUser->Insert();
pUser->GetUser();
pDepartment->Insert();
pDepartment->GetDepartment();
return 0;
}
模式
優點:
1.它分離了具體的類
2.它使得易於交換產品系列
3.它有利於產品的一致性
缺點:
難以支持新種類的產品
微型計算機配件,這個系統所需要的產品族有兩個,一個系列是PC系列,另一個系列是MAC系列。
產品等級結構也有兩個,一個是RAM,一個是CPU。
//兩個抽象產品
public interface Cpu
{
}
public interface Ram
{
}
//四個具體產品
public class PcCpu implements Cpu
{
}
public class MacCpu implements Cpu
{
}
public class PcRam implements Ram
{
}
public class MacRam implements Ram
{
}
//抽象工廠角色
public interface ComputerProducer
{
Cpu createCpu();
Ram createRam();
}
//兩個具體工廠角色
public class PcProducer implements ComputerProducer
{
public Cpu createCpu()
{
return new PcCpu();
}
public Ram createRam()
{
return new PcRam();
}
}
public class MacProducer implements ComputerProducer
{
public Cpu createCpu()
{
return new MacCpu();
}
public Ram createRam()
{
return new MacRam();
}
}
一般情況下,有多少個抽象產品,就有多少個工廠方法。(比如再增加一個PC與MAC不同的其他計算機配件,例如顯示卡)。
OCP(開放-封閉原則(The Open-Close Principle,簡稱OCP))
增加產品族。
增加產品等級結構。
在不改變產品等級結構的情況下,增加產品族就是意味著向每一個產品等級結構中增加一個或者多個新的具體產品角色,這時只需要向工廠等級結構中增加新的元素就可以了,具體的說,只需要增加新的具體工廠類就可以了。
在產品族數目不變的情況下,增加產品等級結構,相當於增加一個與現有產品等級結構平行的一個新的產品等級結構,這時需要向修改所有的工廠角色,增加一個新的工廠方法,這是不支持OCP的。
Producer
PcProducer MacProducer
CPU
PcCPU MacCPU
RAM
PcRAM MacCPU
在上面的結構中,增加產品族相當於增加一個新的廠商,比如Sun的CPU和RAM,這時,只需要增加一個SunProducer即可。
而增加一個新的產品等級結構相當於增加一個顯示卡,而顯示卡也有Pc和Mac之分,那么對於所有的Producer,都需要增加一個方法:createCard()
其他關係
與其他設計模式
單例模式:具體工廠類可以設計成單例類,一個單例類只有一個實例,它自己向外界提供自己的實例。很顯然,在農場系統中,只需要NorthernGardener和TropicalGardener的一個實例就可以了。而在計算機生產的例子中,PcProducer和RamProducer也分別只需要一個實例。
工廠的工廠:工廠角色與抽象產品角色合併(簡單工廠模式java.util.DateFormat),在抽象工廠模式中,抽象工廠類可以有靜態方法,這個方法根據參數的值,返回對應的具體工廠類實例,但是其返回值類型是抽象工廠類型,這樣可以在多態性的保證之下,允許靜態工廠方法自行決定哪一個具體工廠符合要求。
//計算機生產抽象工廠角色
abstract public class ComputerProducer
{
public static ComputerProducer getProducer(String which)
{
if (which.equalsIgnoreCase("PC"))
{
return new PcProducer();
}
else if (which.equalsIgnoreCase("Mac"))
{
return new MacProducer();
}
else
{
return null;
}
}
}
工廠的工廠:工廠角色可以和具體產品角色合併(簡單工廠模式),在抽象工廠模式中,每一個具體工廠類可以有一個靜態方法,其返回值類型是該具體工廠類自己。
public class MacProducer extends ComputerProducer
{
private static MacProducer producer = new MacProducer();
private MacProducer() {
}
public Cpu createCpu()
{
return new MacCpu();
}
public Ram createRam()
{
return new PcRam();
}
public static MacProducer getInstance()
{
return producer;
}
}
女媧造萬物的故事:
神繩(+舉繩造物())
陰繩 陽繩
人
女人 男人
動物
雌性 雄性
在JAVA語言的AWT庫中,使用了抽象工廠模式創建分水域不同作業系統的Peer構件(與本地系統相關的GUI組件)。