抽象工厂的应用
本文是描述了自己对设计模式的工厂的了解.肯定有错误和不足的地方,希望大家能给予支持和建议.
1.问题的引出
在前面的Post中,我描述了.NET的反射在软件设计中的应用.当这篇Post发表之后,有人认为用工厂来实现更合理一些。
在这篇Post里,描述的是根据某些条件如何动态的创建一个类的实例(这些条件最常见的是从应用程序的配置文件里去读取,比如Web项目,你可以从.config文件里去读取),比如有一个类名为SqlDataProvider,我们就可以使用”SqlDataProvider”字符串来构造一个SqlDataProvider实例。这就用到了反射,具体实现方法,参看这篇Post。
为什么要这样去做呢?其中我提到了是为了更好的扩展应用程序,可以使应用程序更加灵活,比如哪天我得数据库不是 Mircrosoft SQL Server,而是Oracle,怎么办呢?很简单,你只需修改一下你的配置文件,写为“OracleDataProvider”即可,当然,你也必须在你的程序里实现这个类了。但是不管是SqlDataProvider,还是OracleDataProvider,都必这篇Post须继承自DataProvider这个抽象类。
这里用的是反射,那还有没有其他解决方案呢?
2 问题解决方案的提出
显然,解决方案是有都的,并且相当经典。 正如这篇Post的评论一样,可以采用工厂类解决。所以这里我们就用工厂来解决,并且我也更倾向于这一种方法。
首先,这种工厂真正称为Abstract Factory,它是一种设计模式(Design pattern),是创建模式(Creational Pattern)的一种。既然是创建模式,首先应该明白什么是创建模式?请看GOF的原文(原书英文版,第81页):
Creational design patterns abstract the instantiation process. They help make a system independent of how its objects are created, composed, or represented. A class creational pattern uses inheritance to vary the class that’s instantiated, whereas an object creational pattern will delegate instantiated to another object.
了解了创建模式后,让我们了解Abstract Factory,还是利用GOF的原文解释吧。第87页:
Intent
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
意思再清楚不过了。如果想得到更多信息,参考GOF的《Design Pattern》一书。
据上所说,我认为它提供了一个接口供创建相关的一组对象等。
结构如下:
下面是对应的实例:
很明显,结构里的AbstractFactory就是WidgetFactory,ConcreteFactory1就是MotifWidgetFactory,
ConcreteFactory2就是PMWidgetFactory。ConcreteFactory1负责构造Motif的Window,
ConcreteFactory2构造PM的window,所谓各司其职呀。
我想根据上面的图,我们对工厂模式有一个很好的了解了。
3 解决方案的一个实例
在我们学习.NET时,Microsoft提供了一个实例,那就是在Java世界和现在.NET阵营特有名的PetShop(版本是3.0以上)。在这个版本种,它提供了一DALFactory,不用看代码,就知道它干了一些什么事情。DAL(Data Access Layer,数据访问层),Factory,工厂。好了,我也不多说了。看看一下他的类图吧。
首先我们应该知道,PetShop的数据库既可以是SQLServer,也可以是Oracle,所以才有了SQLServerDAL和OracleDAL。对于Account,在BLL(Business Logic Layer,业务逻辑层)里的Account类调用了DAL工厂的Account,想想DAL工厂是如何工作的。假设我们现在用的是SQL Server,我们肯定会用SQLServerDAL里的Account,而不是Oracle里的。问题这样就来了,如何去创建这个对象呢?在前面提到,我们可以根据应用的程序的配置文件来创建。我们只需要说明我们现在使用的是SQL Server,其相应的是SQLServerDAL即可。仔细阅读一下源代码,发现它确实是这样做的。现在以BLL的Account的SignIn方法为例,给出PetShop的源代码:
BLL的Account的SignIn();
public AccountInfo SignIn(string userId, string password) {
// Validate input
if ((userId.Trim() == string.Empty) || (password.Trim() == string.Empty))
return null;
// Get an instance of the account DAL using the DALFactory
IAccount dal = PetShop.DALFactory.Account.Create();
// Try to sign in with the given credentials
AccountInfo account = dal.SignIn(userId, password);
// Return the account
return account;
}
DALFactory.Account.Create()的源代码:
public class Account
{
public static PetShop.IDAL.IAccount Create()
{
/// Look up the DAL implementation we should be using
string path = System.Configuration.ConfigurationSettings.AppSettings["WebDAL"];
string className = path + ".Account";
// Using the evidence given in the config file load the appropriate assembly and class
return (PetShop.IDAL.IAccount) Assembly.Load(path).CreateInstance(className);
}
}
注意
System.Configuration.ConfigurationSettings.AppSettings["WebDAL"];
这行代码,它从配置文件离地到WebDAL的内容,即“PetShop.SQLServerDAL”,所以最后className是PetShop.SQLServerDAL.Account.这样就创建了一个SQLServerDAL的实例,返回的确实IAccount对象,但是根据面向对象原理,最后调用的还是SQLServerDAL.Account.
好了,再给出另外一个实例:
与上面的类似,我就不再多说了。
4.Post的问题的解决
了解了上面的原理,再加上PetShop的实例,我想解决上一篇Post的问题就不算太难了。下面给出设计图.
5.需解决问题
1) 抽象类(Abstract Class)和接口(Interface)的区别
显然,在设计模式中,抽象是非常重要的,这样就需要用到抽象类和接口,那么这两者有什么联系和区别呢?希望大家可以提出意见和建议.
2) Abstract Facotry和Factory Method的区别
这两个设计模式之间既有相似之处又有不同之点,希望大家能说出其关系,使我们更加清楚的了解他们,了解设计模式的精髓.
真诚希望大家给出自己的看法.