一、模式描述
我的程序中有需要一系列的对象,比如我们要吃一碗米饭(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("命名空间.类名称");
六、抽象工厂的简单实现
Entity Code
1namespace DesignPattern.AbstractFactory
2{
3 public class User
4 {
5 private int _id;
6 public int Id
7 {
8 get { return _id; }
9 set { _id = value; }
10 }
11
12 private string _name;
13 public string Name
14 {
15 get { return _name; }
16 set { _name = value; }
17 }
18
19 private string _age;
20 public string Age
21 {
22 get { return _age; }
23 set { _age = value; }
24 }
25
26 public User()
27 { }
28
29 public User(int id, string name, string age)
30 {
31 _id = id;
32 _name = name;
33 _age = age;
34 }
35 }
36}
37----------------------------------------------------------------------
38namespace DesignPattern.AbstractFactory
39{
40 public class News
41 {
42 private int _id;
43 public int Id
44 {
45 get { return _id; }
46 set { _id = value; }
47 }
48
49 private string _title;
50 public string Title
51 {
52 get { return _title; }
53 set { _title = value; }
54 }
55
56 private string _context;
57 public string Context
58 {
59 get { return _context; }
60 set { _context = value; }
61 }
62
63 private string _author;
64 public string Author
65 {
66 get { return _author; }
67 set { _author = value; }
68 }
69
70 public News()
71 { }
72
73 public News(int id, string title, string context, string author)
74 {
75 _id = id;
76 _title = title;
77 _context = context;
78 _author = author;
79 }
80 }
81}
82
1namespace DesignPattern.AbstractFactory
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}
1namespace DesignPattern.AbstractFactory
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}
1namespace DesignPattern.AbstractFactory
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}
1namespace DesignPattern.AbstractFactory
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来完成具体实现对象的创建,这里就是抽象工厂模式的一个应用。示意性代码:
1public IDbConnection CreateConnection()
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接口的具体实现类型,如下:
private static Type[] _connectionTypes = new Type[] { typeof(OleDbConnection), typeof(SqlConnection) };
由于Command,DataAdapter等对象的代码都和上面很相似,这里就不作过多解释,我把代码贴到下面,有兴趣的看看:
ProviderFactory
1using System;
2using System.Data;
3using System.Configuration;
4using System.Web;
5using System.Web.Security;
6using System.Web.UI;
7using System.Web.UI.WebControls;
8using System.Web.UI.WebControls.WebParts;
9using System.Web.UI.HtmlControls;
10using System.Reflection;
11using System.Data.OleDb;
12using System.Data.SqlClient;
13
14/**//// <summary>
15/// ProviderFactory 的摘要说明
16/// </summary>
17public class ProviderFactory
18{
19 /**//// <summary>
20 /// 私有构造器,伪单例。
21 /// 一个具体的工厂通常是一个单件(Singleton)。
22 /// </summary>
23 private ProviderFactory()
24 {
25
26 }
27
28 public ProviderFactory(ProviderType provider)
29 {
30 _provider = provider;
31 }
32
33 private static Type[] _connectionTypes = new Type[] { typeof(OleDbConnection), typeof(SqlConnection) };
34 private static Type[] _commandTypes = new Type[] { typeof(OleDbCommand), typeof(SqlCommand) };
35 private static Type[] _dataAdapterTypes = new Type[] { typeof(OleDbDataAdapter), typeof(SqlDataAdapter) };
36 private static Type[] _dataParameterTypes = new Type[] { typeof(OleDbParameter), typeof(SqlParameter) };
37
38 private ProviderType _provider;
39 public ProviderType Provider
40 {
41 get { return _provider; }
42 set { _provider = value; }
43 }
44
45 public IDbConnection CreateConnection()
46 {
47 IDbConnection conn = null;
48 try
49 {
50 conn = (IDbConnection)Activator.CreateInstance(_connectionTypes[(int)_provider]);
51 }
52 catch(TargetInvocationException e)
53 {
54 throw new Exception(e.Message);
55 }
56 return conn;
57 }
58
59 public IDbConnection CreateConnection(string connectionString)
60 {
61 IDbConnection conn = null;
62 object[] param ={ connectionString };
63 try
64 {
65 conn = (IDbConnection)Activator.CreateInstance(_connectionTypes[(int)_provider], param);
66 }
67 catch (TargetInvocationException e)
68 {
69 throw new Exception(e.Message);
70 }
71 return conn;
72 }
73
74 public IDbCommand CreateCommand()
75 {
76 IDbCommand cmd = null;
77 try
78 {
79 cmd = (IDbCommand)Activator.CreateInstance(_commandTypes[(int)_provider]);
80 }
81 catch (TargetInvocationException e)
82 {
83 throw new Exception(e.Message);
84 }
85 return cmd;
86 }
87
88 public IDbCommand CreateCommand(string cmdText)
89 {
90 IDbCommand cmd = null;
91 object[] args = { cmdText };
92 try
93 {
94 cmd = (IDbCommand)Activator.CreateInstance(_commandTypes[(int)_provider], args);
95 }
96 catch (TargetInvocationException e)
97 {
98 throw new Exception(e.Message);
99 }
100 return cmd;
101 }
102
103 public IDbCommand CreateCommand(string cmdText, IDbConnection connection)
104 {
105 IDbCommand cmd = null;
106 object[] args = { cmdText, connection };
107 try
108 {
109 cmd = (IDbCommand)Activator.CreateInstance(_commandTypes[(int)_provider], args);
110 }
111 catch (TargetInvocationException e)
112 {
113 throw new Exception(e.Message);
114 }
115 return cmd;
116 }
117
118 public IDbCommand CreateCommand(string cmdText, IDbConnection connection, IDbTransaction transaction)
119 {
120 IDbCommand cmd = null;
121 object[] args = { cmdText, connection, transaction };
122 try
123 {
124 cmd = (IDbCommand)Activator.CreateInstance(_commandTypes[(int)_provider], args);
125 }
126 catch (TargetInvocationException e)
127 {
128 throw new Exception(e.Message);
129 }
130 return cmd;
131 }
132
133 public IDbDataAdapter CreateDataAdapter()
134 {
135 IDbDataAdapter da = null;
136 try
137 {
138 da = (IDbDataAdapter)Activator.CreateInstance(_dataAdapterTypes[(int)_provider]);
139 }
140 catch (TargetInvocationException e)
141 {
142 throw new SystemException(e.InnerException.Message, e.InnerException);
143 }
144 return da;
145 }
146
147 public IDbDataAdapter CreateDataAdapter(IDbCommand selectCommand)
148 {
149 IDbDataAdapter da = null;
150 object[] args = { selectCommand };
151 try
152 {
153 da = (IDbDataAdapter)Activator.CreateInstance(_dataAdapterTypes[(int)_provider], args);
154 }
155 catch (TargetInvocationException e)
156 {
157 throw new SystemException(e.InnerException.Message, e.InnerException);
158 }
159 return da;
160 }
161
162 public IDbDataAdapter CreateDataAdapter(string selectCommandText, IDbConnection selectConnection)
163 {
164 IDbDataAdapter da = null;
165 object[] args = { selectCommandText, selectConnection };
166 try
167 {
168 da = (IDbDataAdapter)Activator.CreateInstance(_dataAdapterTypes[(int)_provider], args);
169 }
170 catch (TargetInvocationException e)
171 {
172 throw new SystemException(e.InnerException.Message, e.InnerException);
173 }
174 return da;
175 }
176
177 public IDbDataAdapter CreateDataAdapter(string selectCommandText, string selectConnectionString)
178 {
179 IDbDataAdapter da = null;
180 object[] args = { selectCommandText, selectConnectionString };
181 try
182 {
183 da = (IDbDataAdapter)Activator.CreateInstance(_dataAdapterTypes[(int)_provider], args);
184 }
185 catch (TargetInvocationException e)
186 {
187 throw new SystemException(e.InnerException.Message, e.InnerException);
188 }
189 return da;
190 }
191
192 public IDbDataParameter CreateDataParameter()
193 {
194 IDbDataParameter param = null;
195 try
196 {
197 param = (IDbDataParameter)Activator.CreateInstance(_dataParameterTypes[(int)_provider]);
198 }
199 catch (TargetInvocationException e)
200 {
201 throw new SystemException(e.InnerException.Message, e.InnerException);
202 }
203 return param;
204 }
205
206 public IDbDataParameter CreateDataParameter(string parameterName, object value)
207 {
208 IDbDataParameter param = null;
209 object[] args = { parameterName, value };
210 try
211 {
212 param = (IDbDataParameter)Activator.CreateInstance(_dataParameterTypes[(int)_provider], args);
213 }
214 catch (TargetInvocationException e)
215 {
216 throw new SystemException(e.InnerException.Message, e.InnerException);
217 }
218 return param;
219 }
220
221 public IDbDataParameter CreateDataParameter(string parameterName, DbType dataType)
222 {
223 IDbDataParameter param = CreateDataParameter();
224 if (param != null)
225 {
226 param.ParameterName = parameterName;
227 param.DbType = dataType;
228 }
229 return param;
230 }
231
232 public IDbDataParameter CreateDataParameter(string parameterName, DbType dataType, int size)
233 {
234 IDbDataParameter param = CreateDataParameter();
235 if (param != null)
236 {
237 param.ParameterName = parameterName;
238 param.DbType = dataType;
239 param.Size = size;
240 }
241 return param;
242 }
243
244 public IDbDataParameter CreateDataParameter(string parameterName, DbType dataType, int size, string sourceColumn)
245 {
246 IDbDataParameter param = CreateDataParameter();
247 if (param != null)
248 {
249 param.ParameterName = parameterName;
250 param.DbType = dataType;
251 param.Size = size;
252 param.SourceColumn = sourceColumn;
253 }
254 return param;
255 }
256}
257
关于.NET 2.0的这一知识点不了解的朋友可以下载Web Cast课程进行学习。本文就简单的介绍这些。
七、.NET 2.0中的新ADO.NET操作接口应用示例
建立一ASP.NET网站项目,在默认的Default.aspx里放置一个GridView控件便OK。这里以MSSQL 2000里的Northwind数据库作为示例数据库,查询出订单表的数据呈现在aspx页面上,进入后台代码文件(.cs文件):
1public partial class _Default : System.Web.UI.Page
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
由于最近生病精神不振,文章介绍的不是很清楚,望大家体谅。