#抽象工廠模式(Abstract factory Pattern)
##簡介
抽象工廠模式是一種軟件開發設計模式。抽象工廠模式提供了一種方式,可以將一組具有同一主題的單獨的工廠封裝起來。在正常使用中,客戶端程序需要創建抽象工廠的具體實現,然后使用抽象工廠作為接口來創建這一主題的具體對象。客戶端程序不需要知道(或關心)它從這些內部的工廠方法中獲得對象的具體類型,因為客戶端程序僅使用這些對象的通用接口。抽象工廠模式將一組對象的實現細節與他們的一般使用分離開來。
##簡例
有個項目原來是依賴于access數據庫,現在要用sqlserver,怎么更改呢(解耦)?
###用工廠方法模式的數據訪問程序
IUser接口,用于客戶端訪問,解除與具體數據庫訪問的耦合。
```
interface IUser
{
void Insert(User user);
User GetUser(int id);
}
```
Sqlserver類,用于訪問SQL Server的User。
```
class SqlserverUser : IUser
{
public void Insert(User user)
{
Console.WriteLine("在SQL Server中給User表增加一條記錄");
}
public User GetUser(int id)
{
Console.WriteLine("在SQL Server中給User表獲取一條記錄");
return null;
}
}
```
AccessUser類,用于訪問Access的User。
```
class AccessUser : IUser
{
public void Insert(User user)
{
Console.WriteLine("在access中給User表增加一條記錄");
}
public User GetUser(int id)
{
Console.WriteLine("在access中給User表獲取一條記錄");
return null;
}
}
```
IFactory接口,定義一個創建訪問User表對象的抽象的工廠接口。
```
interface Ifactory
{
IUser CreateUser();
}
```
sqlServerFactory類,實現IFactory接口,實例化SqlserverUser。
```
class sqlServerFactory: IFactory
{
public IUser CreateUser()
{
return new SqlserverUser();
}
}
```
AccessFactory類,實現IFactory接口,實例化AccessUser.
```
class AccessFactory: IFactory
{
public IUser CreateUser()
{
return new AccessUser();
}
}
```
客戶端代碼
```
static void main(string[] args)
{
User user = new User();
IFactory factory = new SqlServerFactory();
IUser iu = factory.CreateUser();
iu.Insert(user);
iu.GetUser(1);
Console.Read();
}
```
但是數據里面不可能只有一個User表,比如說增加部門表,此時怎么辦呢。
###抽象工廠類
IUser接口,用于客戶端訪問,解除與具體數據庫訪問的耦合。
```
interface IDepartment
{
void Insert(Department department);
Department GetDepartment(int id);
}
```
SqlserverDepartment類,用于訪問SQL Server的Department。
```
class SqlserverDepartment : IDepartment
{
public void Insert(Department department)
{
Console.WriteLine("在SQL Server中給Department表增加一條記錄");
}
public Department GetDepartment(int id)
{
Console.WriteLine("在SQL Server中給Department表獲取一條記錄");
return null;
}
}
```
AccessDepartment類,用于訪問Access的Department。
```
class AccessDepartment : IDepartment
{
public void Insert(Department department)
{
Console.WriteLine("在Access中給Department表增加一條記錄");
}
public Department GetDepartment(int id)
{
Console.WriteLine("在Access中給Department表獲取一條記錄");
return null;
}
}
```
***
```
interface IUser
{
void Insert(User user);
User GetUser(int id);
}
```
Sqlserver類,用于訪問SQL Server的User。
```
class SqlserverUser : IUser
{
public void Insert(User user)
{
Console.WriteLine("在SQL Server中給User表增加一條記錄");
}
public User GetUser(int id)
{
Console.WriteLine("在SQL Server中給User表獲取一條記錄");
return null;
}
}
```
AccessUser類,用于訪問Access的User。
```
class AccessUser : IUser
{
public void Insert(User user)
{
Console.WriteLine("在access中給User表增加一條記錄");
}
public User GetUser(int id)
{
Console.WriteLine("在access中給User表獲取一條記錄");
return null;
}
}
```
IFactory接口,定義一個創建訪問User表對象的抽象的工廠接口。
```
interface Ifactory
{
IUser CreateUser();
IDepartment CreateDepartment();
}
```
sqlServerFactory類,實現IFactory接口,實例化SqlserverUser。
```
class sqlServerFactory: IFactory
{
public IUser CreateUser()
{
return new SqlserverUser();
}
public IDepartment CreateDepartment()
{
return new SqlserverDepartment();
}
}
```
AccessFactory類,實現IFactory接口,實例化AccessUser.
```
class AccessFactory: IFactory
{
public IUser CreateUser()
{
return new AccessUser();
}
public IDepartment CreateDepartment()
{
return new AccessDepartment();
}
}
```
客戶端代碼
```
static void main(string[] args)
{
User user = new User();
IFactory factory = new SqlServerFactory();
IUser iu = factory.CreateUser();
iu.Insert(user);
iu.GetUser(1);
Console.Read();
}
```
抽象工廠模式:提供一個創建一系列相關或者相互依賴的對象的接口,而無需指定它們具體的類。
###用簡單工廠來改進抽象工廠
```
class DataAccess
{
private static readonly string db = "Sqlserver";
// private static readonly string db = "access";
public static IUser CreateUser()
{
IUser result = null;
switch (db)
{
case:"Sqlserver":
result = new SqlserverUser();
break;
case:"Acesss":
result = new AcesssUser();
break;
}
return result;
}
publci static IDepartment CreateDepartment()
{
IDepartment result = null;
switch (db)
{
case:"Sqlserver":
result = new SqlserverDepartment();
break;
case:"Acesss":
result = new AcesssDepartment();
break;
}
return result;
}
}
```
客戶端代碼
```
static void Main(string[] args)
{
User user = new User();
Department dept = new Department();
IUser iu = DataAccess.createUser();
iu.Insert(user);
iu.GetUser(1);
IDepartment id = DataAccess.createDepartment();
id.Inert(dept);
id.GetDepartment(1);
Console.Read();
}
```
利用反射或者配置文件都可以減少在所有簡單工廠的switch和if,解除分支判斷帶來的耦合。
“工廠”是創建產品(對象)的地方,其目的是將產品的創建與產品的使用分離。抽象工廠模式的目的,是將若干抽象產品的接口與不同主題產品的具體實現分離開。這樣就能在增加新的具體工廠的時候,不用修改引用抽象工廠的客戶端代碼。
使用抽象工廠模式,能夠在具體工廠變化的時候,不用修改使用工廠的客戶端代碼,甚至是在運行時。然而,使用這種模式或者相似的設計模式,可能給編寫代碼帶來不必要的復雜性和額外的工作。正確使用設計模式能夠抵消這樣的“額外工作”。
##從NSArray看類簇
Class Clusters(類簇)是抽象工廠模式在iOS下的一種實現,眾多常用類,如NSString,NSArray,NSDictionary,NSNumber都運作在這一模式下,它是接口簡單性和擴展性的權衡體現,在我們完全不知情的情況下,偷偷隱藏了很多具體的實現類,只暴露出簡單的接口。
雖然官方文檔中拿NSNumber說事兒,但Foundation并沒有像圖中描述的那樣為每個number都弄一個子類,于是研究下NSArray類簇的實現方式。
###__NSPlacehodlerArray
熟悉這個模式的同學很可能看過下面的測試代碼,將原有的alloc+init拆開寫:
```
id obj1 = [NSArray alloc]; // __NSPlacehodlerArray *
id obj2 = [NSMutableArray alloc]; // __NSPlacehodlerArray *
id obj3 = [obj1 init]; // __NSArrayI *
id obj4 = [obj2 init]; // __NSArrayM *
```
發現+ alloc后并非生成了我們期望的類實例,而是一個__NSPlacehodlerArray的中間對象,后面的- init或- initWithXXXXX消息都是發送給這個中間對象,再由它做工廠,生成真的對象。這里的__NSArrayI和__NSArrayM分別對應Immutable和Mutable(后面的I和M的意思)
于是順著思路猜實現,__NSPlacehodlerArray必定用某種方式存儲了它是由誰alloc出來的這個信息,才能在init的時候知道要創建的是可變數組還是不可變數組
經過研究發現,Foundation用了一個很賤的比較靜態實例地址方式來實現,偽代碼如下:
```
static __NSPlacehodlerArray *GetPlaceholderForNSArray() {
static __NSPlacehodlerArray *instanceForNSArray;
if (!instanceForNSArray) {
instanceForNSArray = [[__NSPlacehodlerArray alloc] init];
}
return instanceForNSArray;
}
static __NSPlacehodlerArray *GetPlaceholderForNSMutableArray() {
static __NSPlacehodlerArray *instanceForNSMutableArray;
if (!instanceForNSMutableArray) {
instanceForNSMutableArray = [[__NSPlacehodlerArray alloc] init];
}
return instanceForNSMutableArray;
}
// NSArray實現
+ (id)alloc {
if (self == [NSArray class]) {
return GetPlaceholderForNSArray()
}
}
// NSMutableArray實現
+ (id)alloc {
if (self == [NSMutableArray class]) {
return GetPlaceholderForNSMutableArray()
}
}
// __NSPlacehodlerArray實現
- (id)init {
if (self == GetPlaceholderForNSArray()) {
self = [[__NSArrayI alloc] init];
}
else if (self == GetPlaceholderForNSMutableArray()) {
self = [[__NSArrayM alloc] init];
}
return self;
}
```
Foundation不是開源的,所以上面的代碼是猜測的,思路大概就是這樣,可以這樣驗證下:
```
id obj1 = [NSArray alloc];
id obj2 = [NSArray alloc];
id obj3 = [NSMutableArray alloc];
id obj4 = [NSMutableArray alloc];
// 1和2地址相同,3和4地址相同,無論多少次都相同,且地址相差16位
```