设计模式之抽象工厂

工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。

简单工厂 VS 抽象工厂:关于这两个模式的区别,我思考了下。简单工厂只所以简单,是因为它只是把创建一个类的过程进行了封装(这些类一般来说有一个公共的基类),通过Switch或者其他的判断形式创建需要的类(当然也是可以使用反射);而抽象工厂创建的类更为复杂,通常来说创建的类(子类)有同一基类,而在不同情形下选择对应的子类。

情形重现:

说到抽象工厂,必定想到对于不同数据库怎么快速切换类对象,也就是说可以很方便简单创建自己想到的类对象,废话不多说。

实体类接口:

    interface IUser
    {
         void Add(IUser user);
         List<IUser> GetUsers();
    }

一个IUser类型的接口,作为所有User类型的接口(不管是SqlServer还是Oracle或者Access)。

实现类:

SqlServer:

    class SqlServerUser:IUser
    {
        public void Add(IUser user)
        {
           
        }

        public List<IUser> GetUsers()
        {
            return new List<IUser>();
        }
    }

Oracle:

    class OracleUser:IUser
    {
        public void Add(IUser user)
        {

        }

        public List<IUser> GetUsers()
        {
            return new List<IUser>();
        }
    }

根据模式的定义,会有一个工厂的接口,以及工厂的子类,如下:

工厂接口:

    interface IDataFactory
    {
        IUser CreateUser();
    }

SqlServer工厂:

    class SqlServerDataFactory:IDataFactory
    {
        public IUser CreateUser()
        {
            return new SqlServerUser();
        }
    }

Oracle工厂:

    class OracleDataFactory:IDataFactory
    {
        public IUser CreateUser()
        {
            return new  OracleUser();
        }
    }

代码的过程还是比较简单,创建实体类接口,创建接口的实现类(具体实现类有几个根据自己的需要);创建工厂的接口,创建工厂的实现类(具体实现类有几个同样根据自己的需要),在实现类中将工厂的方法进行完善(创建对应的实体类型)。

使用喽:

            IDataFactory sqlDataFactory = new SqlServerDataFactory();
            IDataFactory oracleDataFactory = new OracleDataFactory();
            IUser sqlUser = sqlDataFactory.CreateUser();
            IUser oracleUser = oracleDataFactory.CreateUser();

创建对应的工厂类(SqlServer或者Oracle),使用工厂类创建对应的实体(SqlServer或者Oracle)。

当然,我们的数据库中,肯定不止一张User表,可能还有Role表或者是RoleAndUser等等很多张表,这样的话我们就要创建对应类的接口和接口的对应实现类,同时还要修改工厂的接口(添加对应的创建实体方法),还有工厂接口的实现类不能忘了哦。好复杂,好繁琐。

终极武器:

刚才已经分析了,如果增加了一个类或者删除了一个类,需要修改四个地方,所以我们拿出杀手锏"反射"。

第一步,先来个大裁员,删掉工厂(工厂和工厂的接口都不要);

第二步,添加一个操作工厂(其实就是一个类,放了之前抽象工厂中的方法);

第三步,添加两个变量,一个代表实体类的程序集,一个代表当前创建哪个类型的实体(SqlServer还是Oracle)。

如下所示:

    class DataFactory
    {
        private static readonly string AssemblyName = "程序集(或者包含命名空间)";
        private static readonly string db = "SqlServer";
        public static IUser CreateUser()
        {
            string typeName = AssemblyName + "." + db + "." + db + "User";
            return (IUser)Assembly.Load(AssemblyName).CreateInstance(typeName);
        }
    }

其实AssemblyName是否包含命名空间自己可以决定,当前的实没有包含的,当然如果包含了就无法使用Assembly.Load了哦。

代码修改之后,如果我们要增加其他的实体类,只需增加一个类的接口和匹配的实现类即可。细心的你会发现,这样如果要创建Oracle会很麻烦,因为要修改db的值,这一点可以通过配置的方式进行实现,只需动态读取配置文件即可,但是感觉这样还是有一点不太好的,毕竟在程序的运行中我们可能随时访问任意一个类型的,所以这一点来说我们的第一种方式就没有这种问题。在这里我倒是推荐大家可以适当修改下创建方法,可以给其添加一个参数,用于决定创建哪个类型,当然最好是一个常量,或者是一个私有的全局变量,甚至是一个枚举也都是ok的,关键看大家个人的喜好了。

好了,这次的抽象工厂就说这么多,希望大家多提意见和建议,代码在这里下载

posted @ 2013-07-27 21:11  wangyafei_it  阅读(230)  评论(0编辑  收藏  举报