第六课 抽象工厂模式
第六课 抽象工厂模式
?
?????? 抽象工厂模式其实和工厂方法模式思路一样,可以说是工厂方法模式的一个特例,用于控制一个系列的产品的不同版本。简单举个例子(可能不适用于.net)我现在有几个界面UI控件产品,Button,Label,TextBox。大家应该知道,windows和Mac系统底层对UI的实现不同,所以创建控件的方式可能不同。这样我可能出2套产品,一套是为Windows实现的,另一套是基于Mac实现的。这样我为了方便的获得两套产品,就需要用抽象工厂模式。换句话说,抽象工厂在应用的时候,需要判断产品的横向和纵向。假设横向是一个系列的产品,纵向是不同的版本实现的话,就有类似下面这样的一个矩阵。
?
ButtonForWin? LabelForWin? TextBoxForWin ButtonForMac? LabelForMac? TextBoxForMac ButtonForLinux?? LabelForLinux?? TextBoxForLinux
?
其实就是3个产品,Button Label TextBox ,但是针对3个操作系统实现了3个版本而已。 这样大家有点印象了吧,下面开始看代码。Java版 首先系列产品的抽象 1.??????? public?interface?Button 2.??????? { 3.??????? ????... 4.??????? } 5.???????
?
6.??????? public?interface?Label 7.??????? { 8.??????? ????... 9.??????? } 这里我懒的打TextBox了,就用俩来看吧。
?
然后Windows实现: 1.??????? public?class?WinButton?implements?Button 2.??????? { 3.??????? ????... 4.??????? } 5.???????
?
6.??????? public?class?WinLabel?implements?Label 7.??????? { 8.??????? ????... 9.??????? }
?
再看Mac实现: 1.??????? public?class?MacButton?implements?Button 2.??????? { 3.??????? ????... 4.??????? } 5.???????
?
6.??????? public?class?MacLabel?implements?Label 7.??????? { 8.??????? ????... 9.??????? }
?
Windows和Mac的实现UI方式肯定不同,这里不描述了。
?
然后来看工厂: 1.???????
?
2.??????? public?abstract?class?UIProducer 3.??????? { 4.??????? ????public?abstract?Button?createButton(); 5.???????
?
6.??????? ????public?abstract?Label?createLabel(); 7.???????
?
8.??????? ????public?static?UIProducer?getInstance(String?which) 9.??????? ????{ 10.???? ????if?(which.equalsIgnoreCase("Win")) 11.???? ????????{ 12.???? ????????????return?WinProducer.getInstance(); 13.???? ????????} 14.???? ????????else?if?(which.equalsIgnoreCase("Mac")) 15.???? ????????{ 16.???? ????????????return?MacProducer.getInstance(); 17.???? ????????} 18.???? ????????return?null; 19.???? ????} 20.???? } 21.????
?
22.???? public?class?WinProducer?extends?UIProducer 23.???? { 24.???? ????private?static?WinProducer?producer?=?new?WinProducer?(); 25.????
?
26.???? ????private?WinProducer() 27.???? ????{ 28.???? ????} 29.????
?
30.???? ????public?Button?createButton() 31.???? ????{ 32.???? ????????return?new?WinButton(); 33.???? ????} 34.????
?
35.???? ????public?Label?createLabel() 36.???? ????{ 37.???? ????????return?new?WinLabel(); 38.???? ????} 39.????
?
40.???? ????public?static?WinProducer?getInstance() 41.???? ????{ 42.???? ????????return?producer; 43.???? ????} 44.????
?
45.????
?
46.???? } 47.????
?
48.???? public?class?MacProducer?extends?ComputerProducer 49.???? { 50.???? ????private?static?MacProducer?producer?=?new?MacProducer(); 51.????
?
52.???? ????private?MacProducer()?{ 53.???? ????} 54.????
?
55.???? ????public?Button?createButton() 56.???? ????{ 57.???? ????????return?new?MacButton(); 58.???? ????} 59.????
?
60.???? ????public?Label?createRam() 61.???? ????{ 62.???? ????????return?new?MacLabel(); 63.???? ????} 64.????
?
65.???? ????public?static?MacProducer?getInstance() 66.???? ????{ 67.???? ????????return?producer; 68.???? ????} 69.????
?
70.???? }
?
这里使用了类似工厂方法模式,使用了一个抽象工厂基类。但是实现方式不同,请仔细观察。这里没有用接口,使用了抽象类,同事没有定义抽象生产方法,采用了简单工厂模式的方式返回实现的工厂对象。这里如果我系统是针对Windows开发的,那么我可以使用: 1.??????? UIProducer?winUI?=?UIProducer.getInstance("Win"); 来获得生产Windows下产品的工厂,反之使用: 2.??????? UIProducer?winUI?=?UIProducer.getInstance("Mac"); 来获得生产Mac系统下产品的工厂。
?
现在大家能够体会到抽象工厂的用法了吧。 在你判断一套产品有可能要为不同的需求做几套不同的实现的产品的时候,其实简单说就是一套东西要做几个版本的实现的时候,就可以考虑抽象工厂。 再来看一个《大话设计模式》中的例子,这个是针对不同种类数据库实现的例子。 C#代码: 1.??????? using?System; 2.??????? using?System.Collections.Generic; 3.??????? using?System.Text; 4.??????? using?System.Reflection; 5.??????? using?System.Configuration; 6.???????
?
7.??????? namespace?抽象工厂模式 8.??????? { 9.??????? ????class?Program 10.???? ????{ 11.???? ????????static?void?
12.???? ????????{ 13.???? ????????????User?user?=?new?User(); 14.???? ????????????Department?dept?=?new?Department(); 15.????
?
16.???? ????????????IUser?iu?=?DataAccess.CreateUser(); 17.????
?
18.???? ????????????iu.Insert(user); 19.???? ????????????iu.GetUser(1); 20.????
?
21.???? ????????????IDepartment?id?=?DataAccess.CreateDepartment(); 22.???? ????????????id.Insert(dept); 23.???? ????????????id.GetDepartment(1); 24.????
?
25.???? ????????????Console.Read(); 26.???? ????????} 27.???? ????} 28.????
?
29.???? ????class?User 30.???? ????{ 31.???? ????????private?int?_id; 32.???? ????????public?int?ID 33.???? ????????{ 34.???? ????????????get?{?return?_id;?} 35.???? ????????????set?{?_id?=?value;?} 36.???? ????????} 37.????
?
38.???? ????????private?string?_name; 39.???? ????????public?string?Name 40.???? ????????{ 41.???? ????????????get?{?return?_name;?} 42.???? ????????????set?{?_name?=?value;?} 43.???? ????????} 44.???? ????} 45.????
?
46.???? ????class?Department 47.???? ????{ 48.???? ????????private?int?_id; 49.???? ????????public?int?ID 50.???? ????????{ 51.???? ????????????get?{?return?_id;?} 52.???? ????????????set?{?_id?=?value;?} 53.???? ????????} 54.????
?
55.???? ????????private?string?_deptName; 56.???? ????????public?string?DeptName 57.???? ????????{ 58.???? ????????????get?{?return?_deptName;?} 59.???? ????????????set?{?_deptName?=?value;?} 60.???? ????????} 61.???? ????} 62.????
?
63.???? ????interface?IUser 64.???? ????{ 65.???? ????????void?Insert(User?user); 66.????
?
67.???? ????????User?GetUser(int?id); 68.???? ????} 69.????
?
70.???? ????class?SqlserverUser?:?IUser 71.???? ????{ 72.???? ????????public?void?Insert(User?user) 73.???? ????????{ 74.???? ????????????Console.WriteLine("在Sqlserver中给User表增加一条记录"); 75.???? ????????} 76.????
?
77.???? ????????public?User?GetUser(int?id) 78.???? ????????{ 79.???? ????????????Console.WriteLine("在Sqlserver中根据ID得到User表一条记录"); 80.???? ????????????return?null; 81.???? ????????} 82.???? ????} 83.????
?
84.???? ????class?AccessUser?:?IUser 85.???? ????{ 86.???? ????????public?void?Insert(User?user) 87.???? ????????{ 88.???? ????????????Console.WriteLine("在Access中给User表增加一条记录"); 89.???? ????????} 90.????
?
91.???? ????????public?User?GetUser(int?id) 92.???? ????????{ 93.???? ????????????Console.WriteLine("在Access中根据ID得到User表一条记录"); 94.???? ????????????return?null; 95.???? ????????} 96.???? ????} 97.????
?
98.???? ????interface?IDepartment 99.???? ????{ 100.? ????????void?Insert(Department?department); 101.?
?
102.? ????????Department?GetDepartment(int?id); 103.? ????} 104.?
?
105.? ????class?SqlserverDepartment?:?IDepartment 106.? ????{ 107.? ????????public?void?Insert(Department?department) 108.? ????????{ 109.? ????????????Console.WriteLine("在Sqlserver中给Department表增加一条记录"); 110.? ????????} 111.?
?
112.? ????????public?Department?GetDepartment(int?id) 113.? ????????{ 114.? ????????????Console.WriteLine("在Sqlserver中根据ID得到Department表一条记录"); 115.? ????????????return?null; 116.? ????????} 117.? ????} 118.?
?
119.? ????class?AccessDepartment?:?IDepartment 120.? ????{ 121.? ????????public?void?Insert(Department?department) 122.? ????????{ 123.? ????????????Console.WriteLine("在Access中给Department表增加一条记录"); 124.? ????????} 125.?
?
126.? ????????public?Department?GetDepartment(int?id) 127.? ????????{ 128.? ????????????Console.WriteLine("在Access中根据ID得到Department表一条记录"); 129.? ????????????return?null; 130.? ????????} 131.? ????} 132.?
?
133.? ????class?DataAccess 134.? ????{ 135.? ????????private?static?readonly?string?AssemblyName?=?"抽象工厂模式"; 136.? ????????private?static?readonly?string?db?=?ConfigurationManager.AppSettings["DB"]; 137.? ???????? 138.? ????????public?static?IUser?CreateUser() 139.? ????????{ 140.? ????????????string?className?=?AssemblyName?+?"."?+?db?+?"User"; 141.? ????????????return?(IUser)Assembly.Load(AssemblyName).CreateInstance(className); 142.? ????????} 143.?
?
144.? ????????public?static?IDepartment?CreateDepartment() 145.? ????????{ 146.? ????????????string?className?=?AssemblyName?+?"."?+?db?+?"Department"; 147.? ????????????return?(IDepartment)Assembly.Load(AssemblyName).CreateInstance(className); 148.? ????????} 149.? ????} 150.?
?
151.? }
?
这里一共出现了2个产品对象,用户和部门。 针对两个产品做了2个版本实现 Access 和 SqlServer 。 这样的好处是什么呢,如果我现在要把系统安装在一个装有SqlServer数据库的机器上的时候,我就可以直接调用SqlServer工厂类,给我生产Sql版的产品来用,如果装在一个只有Access数据库的机器上时,我可以直接用Access工厂类,生产Access产品来使用。不用修改代码。你可能会问,为什么不用修改代码,调用的时候需要给参数啊。这里你看看 ” DataAccess” 这个类的实现方法。这里应用了一个动态加载类的方式 : Assembly.Load(AssemblyName).CreateInstance(className); 这个方式可以通过一串文本(指明类的目录及类名)来生成类对象。当然生成的是Object对象,但是可以通过强制转换成基类来调用吗。你又要问了,字符串也是后台写得啊,还是需要改代码啊。我回答,笨啊你,谁说字符串非要写在后台,我写到配置文件里去不行吗?你看: private?static?readonly?string?db?=?ConfigurationManager.AppSettings["DB"]; 这句是干什么用的啊。这不是读配置文件里的节点吗。
?
下面给出配置文件: 1.??????? <?xml?version="1.0"?encoding="utf-8"??> 2.??????? <configuration> 3.??????? ????<appSettings> 4.??????? ????????<add?key="DB"?value="Sqlserver"/> 5.??????? ????</appSettings> 6.??????? </configuration>
?
看到了没这里配置的是Sqlserver 所以后台肯定是生成SqlServer工厂了。 这样我的程序如果改用Access数据库的话,我只要修改下配置文件即可。完全避免的代码的修改。怎么样,有用吧。
?
好了,今天的课就到这里,明天讲什么呢,我回去想想吧。同志们期待吧。
?
作者:王文斌 转载请注明出处。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |