再看示例代碼:
現在有兩個類型的產品族,他們的抽象基類分別是ProductA,ProductB。
//抽象工廠接口
public interface ICreator
{
ProductA factoryA();//創建A族產品
ProductB factoryB();//創建B族產品
}
//具體工廠類1
public class ConcreteCreator1 implements ICreator
{
@Override
public ProductA factoryA()
{
return new ConcreteProductA1();
}
@Override
public ProductB factoryB()
{
return new ConcreteProductB1();
}
}
//具體工廠類2
public class ConcreteCreator2 implements ICreator
{
@Override
public ProductA factoryA()
{
return new ConcreteProductA2();
}
@Override
public ProductB factoryB()
{
return new ConcreteProductB2();
}
}
//調用類
public class Client
{
public static void main(String[] args)
{
ICreator creator1=new ConcreteCreator1();
ICreator creator2=new ConcreteCreator2();
ProductA proa1=creator1.factoryA();
ProductB prob1=creator1.factoryB();
ProductA proa2=creator2.factoryA();
ProductB prob2=creator2.factoryB();
//....
}
}
這裏我要提出的問題是具體工廠類的實現中,每個工廠方法返回類型都是抽象類,而具體要返回哪個實現類並未規定,可以返回任何子類。 這裏返回的實際子類對調用者是透明的。如ConcreteCreator1的factoryA方法可以返回ConcretProductA2,客戶端本來想要調用ConcreteCreator1得到ConcretProductA1,結果可能是ConcretProductA2。
問題出現的原因是一個具體工廠類中所生產的不同類型的產品之間沒有關聯,以至程序員們可以隨意搭配,這樣抽象工廠已經毫無意義了。而抽象工廠的意義正是在於它要生產的是一組有關聯的產品族,從代碼的角度來說工廠實現類各工廠方法所生產的實際產品必須是有關聯的。下面我們以C#的ado.net抽象工廠DbProviderFactory爲例說明,先看一段普通的數據庫查詢代碼:
string connStr = "Persist Security Info=False;Initial Catalog=mydb;server=(local);User ID=sa;Pwd=xxx";
string sql = "select * from userinfo where id=@id";
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql,conn);
SqlParameter param = new SqlParameter();
param.ParameterName = "id";
param.DbType = DbType.Int32;
param.Value = 4;
cmd.Parameters.Add(param);
SqlDataReader dataReader = cmd.ExecuteReader();
if (dataReader.Read())
{
Console.WriteLine(dataReader.GetString(0));
}
}
這段使用System.Data.SqlClient查詢數據的代碼很常見,但按依賴倒轉原則來說它與System.Data.SqlClient耦合太深了。.net中數據提供程序還有System.Data.Odbc、System.Data.OleDb、 System.Data.OracleClient等,如果換了其他數據提供程序那我們整個代碼都得被翻新一遍了。現在我們要改造這段代碼使它與sqlclient的解耦,那就必須找他們的抽象基類了。當然微軟已經爲我們準備好了,在System.Data.Common命令空間有DbConnection、DbCommand、DbParameter、DbDataReader等等一系列相關的抽象類。我們可以把這些類看成一個產品族,顯然它們都是相關聯的,數據庫連接對象DbConnection要執行命令,就得有sql命令對象DbCommand,命令對象又要使用參數對象DbParameter。
而抽象工廠DbProviderFactory類定義了生產這些產品方法CreateConnection、CreateCommand......。具體工廠實現由各數據提供程序提供,譬如SqlClient的是SqlClientFactory。再看非完整的結構圖:
這是一個標準的抽象工廠模式實現。以下是改造後代碼:
DbProviderFactory DbProvider = SqlClientFactory.Instance;
using (DbConnection conn = DbProvider.CreateConnection())
{
conn.Open();
DbCommand cmd = DbProvider.CreateCommand();
cmd.Connection = conn;
cmd.CommandText = sql;
DbParameter param = DbProvider.CreateParameter();
param.ParameterName = "id";
param.DbType = DbType.Int32;
param.Value = 4;
cmd.Parameters.Add(param);
DbDataReader dataReader = cmd.ExecuteReader();
if (dataReader.Read())
{
Console.WriteLine(dataReader.GetString(0));
}
}
現在只需修改具體工廠即可實現數據提供程序的轉換,程序與數據提供器的解耦正是通過抽象工廠模式完美實現。