解讀設計模式----抽象工廠模式(AbstractFactory Pattern)
一、模式描述
我的程序中有需要一系列的對象,比如我們要吃一碗米飯(Rice),要喝一杯咖啡(Coffee)......,要想利用他們,我們就必須在程序中根據用戶要求,然後一個個調用 new 操作符來生成他們,這樣客戶程序就要知道相應的類的信息,生成的代碼顯然不夠靈活。那麼我們可以在代碼中不利用具體的類,而只是說明我們需要什麼,然後就能夠得到我們想要的對象呢?
這當然是可以的,根據GOF在《設計模式》一書裏介紹,要創建對象這樣的工作應該是屬於創建型模式完成的。熟悉各種設計模式意圖的朋友就會很快得出結論:“提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類”,至少“無需指定它們具體的類”符合我們的要求。OK,這就是抽象工廠模式的意圖。
二、模式意圖
提供一個創建一系列相關或相互依賴對象的接口,而不需指定他們具體的類。
三、模式UML圖:
四、模式參與者
抽象工廠(Abstract Factory)角色:擔任這個角色的是工廠方法模式的核心,它是與應用系統商業邏輯無關的。
具體工廠(Concrete Factory)角色:這個角色直接在客戶端的調用下創建產品的實例。這個角色含有選擇合適的產品對象的邏輯,而這個邏輯是與應用系統的商業邏輯緊密相關的。
抽象產品(Abstract Product)角色:擔任這個角色的類是工廠方法模式所創建的對象的父類,或它們共同擁有的接口。
具體產品(Concrete Product)角色:抽象工廠模式所創建的任何產品對象都是某一個具體產品類的實例。這是客戶端最終需要的東西,其內部一定充滿了應用系統的商業邏輯。
五、模式與反射
利用設計模式可以使我們的代碼更靈活,更容易擴展,更容易維護。各種面向對象的程序設計語言都提供了基本相同的機制:比如類、繼承、派生、多態等等。但是又有各自的特色,C# 中的反射機制便是一個很重要的工具,好好地利用就可以在實際中發揮很大的作用。
反射是.NET Framework中的一個非常重要的特性。相信絕大多數的朋友都對其有所瞭解或是已經熟練的應用這項技術。我們需要根據需求去動態的創建一對象的實例,在程序設計中,通常我們會爲了解耦合,把接口的實現對象寫入配置文件,讓工廠自己去一個特定的地方(配置文件)找他應該要實例化的對象(接口的實現對象),通過這樣來實現“依賴注入(Dependency Injection)”。
本來“依賴注入”需要專門的IOC容器提供,比如Spring.net,Castle這類似的框架產品。而在抽象工廠模式的應用中顯然沒有這麼麻煩,通常的實現就是使用.NET技術‘反射’就可以了。下面是反射的兩種常見應用:
應用一:
Activator.CreateInstance("類型");
應用二:
Assembly.Load("程序集名稱").CreateInstance("命名空間.類名稱");
六、抽象工廠的簡單實現
2{
3 /// <summary>
4 /// 抽象產品角色
5 /// </summary>
6 public interface INews
7 {
8 void Insert(News news);
9 News QueryById(int newsId);
10 }
11
12 /// <summary>
13 /// 具體產品角色
14 /// </summary>
15 public class NewsSql:INews
16 {
17 public void Insert(News news)
18 {
19 Console.WriteLine("插入新聞到SQL數據庫");
20 }
21
22 public News QueryById(int newsId)
23 {
24 return new News(1, "Hello", " Hello C#!", "beniao");
25 }
26 }
27
28 /// <summary>
29 /// 具體產品角色
30 /// </summary>
31 public class NewsAccess : INews
32 {
33 public void Insert(News news)
34 {
35 Console.WriteLine("插入新聞到Access數據庫");
36 }
37
38 public News QueryById(int newsId)
39 {
40 return new News(1, "Hello", " Hello C#!", "beniao");
41 }
42 }
43}
2{
3 /// <summary>
4 /// 抽象產品角色
5 /// </summary>
6 public interface IUser
7 {
8 void Insert(User user);
9 User QueryById(int userId);
10 }
11
12 /// <summary>
13 /// 具體產品角色
14 /// </summary>
15 public class UserSql:IUser
16 {
17 public void Insert(User user)
18 {
19 Console.WriteLine("Insert SQL OK!");
20 }
21
22 public User QueryById(int userId)
23 {
24 return new User(1, "beniao", "22");
25 }
26 }
27
28 /// <summary>
29 /// 具體產品角色
30 /// </summary>
31 public class UserAccess : IUser
32 {
33 public void Insert(User user)
34 {
35 Console.WriteLine("Insert Access OK!");
36 }
37
38 public User QueryById(int userId)
39 {
40 return new User(2, "beniao", "23");
41 }
42 }
43}
2{
3 /// <summary>
4 /// 工廠角色(根據配置文件來確定創建何種對象)
5 /// </summary>
6 public class DataAccess
7 {
8 public static IUser CreateUser()
9 {
10 string obj = ConfigurationManager.AppSettings["usersql"];
11 return (IUser)Assembly.Load("DesignPattern.AbstractFactory").CreateInstance(obj);
12 }
13
14 public static INews CreateNews()
15 {
16 string obj = ConfigurationManager.AppSettings["newssql"];
17 return (INews)Assembly.Load("DesignPattern.AbstractFactory").CreateInstance(obj);
18 }
19 }
20***********************************************************************************
21 /// <summary>
22 /// 如果根據GOF的定義及UML圖,此爲抽象工廠角色
23 /// </summary>
24 public class Factory
25 {
26 public virtual IUser CreateUser()
27 {
28 return null;
29 }
30
31 public virtual INews CreateNews()
32 {
33 return null;
34 }
35 }
36
37 /// <summary>
38 /// 具體的工廠角色
39 /// </summary>
40 public class SqlFactory:Factory
41 {
42 public override IUser CreateUser()
43 {
44 string obj = ConfigurationManager.AppSettings["usersql"];
45 return (IUser)Assembly.Load("DesignPattern.AbstractFactory").CreateInstance(obj);
46 }
47
48 public override INews CreateNews()
49 {
50 string obj = ConfigurationManager.AppSettings["newssql"];
51 return (INews)Assembly.Load("DesignPattern.AbstractFactory").CreateInstance(obj);
52 }
53 }
54
55 /// <summary>
56 /// 具體的工廠角色
57 /// </summary>
58 public class AccessFactory : Factory
59 {
60 public override IUser CreateUser()
61 {
62 string obj = ConfigurationManager.AppSettings["useracc"];
63 return (IUser)Assembly.Load("DesignPattern.AbstractFactory").CreateInstance(obj);
64 }
65
66 public override INews CreateNews()
67 {
68 string obj = ConfigurationManager.AppSettings["newsacc"];
69 return (INews)Assembly.Load("DesignPattern.AbstractFactory").CreateInstance(obj);
70 }
71 }
72}
2{
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 IUser user = DataAccess.CreateUser();
8 user.Insert(null);
9
10 INews news = DataAccess.CreateNews();
11 news.Insert(null);
12
13 //******************GOF************************
14
15 Factory factory = new SqlFactory();
16 factory.CreateNews().Insert(null);
17 factory.CreateUser().Insert(null);
18
19 factory = new AccessFactory();
20 factory.CreateNews().Insert(null);
21 factory.CreateUser().Insert(null);
22 }
23 }
24}
七、.NET 2.0中的抽象工廠模式
.NET 2.0相比.NET 1.1有很大的改進,就在ADO.NET上來說吧,提供了一套新的操作接口。下面我就簡單的介紹下這套接口的設計,在System.Date下提供了IDbConnection 、IDbCommand、IDbDataAdapter以及IDbTransaction這樣一系列接口,通過ProviderFactory來完成具體實現對象的創建,這裏就是抽象工廠模式的一個應用。示意性代碼:
2{
3 IDbConnection conn = null;
4 try
5 {
6 conn = (IDbConnection)Activator.CreateInstance(_connectionTypes[(int)_provider]);
7 }
8 catch(TargetInvocationException e)
9 {
10 throw new Exception(e.Message);
11 }
12 return conn;
13}
14
15public IDbConnection CreateConnection(string connectionString)
16{
17 IDbConnection conn = null;
18 object[] param ={ connectionString };
19 try
20 {
21 conn = (IDbConnection)Activator.CreateInstance(_connectionTypes[(int)_provider], param);
22 }
23 catch (TargetInvocationException e)
24 {
25 throw new Exception(e.Message);
26 }
27 return conn;
28}
在_connectionTypes數組裏存放的是IDbConnection接口的具體實現類型,如下:
由於Command,DataAdapter等對象的代碼都和上面很相似,這裏就不作過多解釋,我把代碼貼到下面,有興趣的看看:
關於.NET 2.0的這一知識點不瞭解的朋友可以下載Web Cast課程進行學習。本文就簡單的介紹這些。
七、.NET 2.0中的新ADO.NET操作接口應用示例
建立一ASP.NET網站項目,在默認的Default.aspx裏放置一個GridView控件便OK。這裏以MSSQL 2000裏的Northwind數據庫作爲示例數據庫,查詢出訂單表的數據呈現在aspx頁面上,進入後臺代碼文件(.cs文件):
2{
3 protected void Page_Load(object sender, EventArgs e)
4 {
5 string connectionString="Data Source=.;Initial Catalog=Northwind;user id=sa;password=;";
6 string cmdText = "select * from orders";
7
8 ProviderFactory factory = new ProviderFactory(ProviderType.SqlClient);
9 IDbConnection conn = factory.CreateConnection(connectionString);
10 IDbDataAdapter sda = factory.CreateDataAdapter(cmdText, conn);
11 DataSet ds = new DataSet();
12 sda.Fill(ds);
13 this.GridView1.DataSource = ds.Tables[0];
14 this.GridView1.DataBind();
15 }
16}
ProviderFactory擔任着工廠的角色,負責創建如IDbConnection、IDbCommand等一系列的產品。如上,我們拿到了工廠角色,通過工廠角色的CreateConnection就創建到了一個基於抽象產品角色IDbConnection接口的實現對象(具體是什麼實現對象我們暫不管)。
.NET 2.0提供了這一套操作接口,對於程序實現上就更加靈活了,更是強調了使用“依賴接口/抽象編程”的思想。
示例程序代碼下載
注:轉載請註明出處:http://beniao.cnblogs.com/ 或 http://www.cnblogs.com