抽象工厂模式 - 设计模式学习

  抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。

  以下给出抽象工厂方法模式的UML图:

    抽象工厂模式 - 设计模式学习_第1张图片

  回到《大话设计模式》里面的双数据库访问的例子:

namespace ConsoleApplication1
{
    class User
    {
        private int _id;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
    }

    class Department
    {
        private int _id;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        private string _deptNmae
        {
            get { return _deptNmae; }
            set { _deptNmae = value; }
        }
    }

    //IUser接口,用于客户端访问,解除与具体数据库访问的耦合
    interface IUser
    {
        void Insert(User user);
        User GetUser(int id);
    }

    class SqlserverUser : IUser 
    {
        public void Insert(User user)
        {
            Console.WriteLine("Sql Server添加一条记录");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("Sql Server根据ID得到User表的一条记录");
            return null;
        }
    }

    class AccessUser : IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Access中给User表增加一条记录");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("在Access中根据ID得到User表的一条记录");
            return null;
        }
    }

    interface IDepartment
    {
        void Insert(Department department);
        Department GetDepartment(int id);
    }

    class SqlserverDepartment : IDepartment
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Sqlserver中给Department表插入一条记录");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Sqlserver中根据ID得到Department表的一条记录");
            return null;
        }
    }

    class AccessDepartment : IDepartment 
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Access中给Department表插入一条记录");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Access中根据ID得到Department表的一条记录");
            return null;
        }
    }

    interface IFactory
    {
        IUser CreateUser();

        IDepartment CreateDepartment();
    }

    //SQLServer实例化工厂,负责实例化SqlserverUser和SqlserverDepartment
    class SqlServerFactory : IFactory
    {
        public IUser CreateUser()
        {
            return new SqlserverUser();
        }

        public IDepartment CreateDepartment()
        {
            return new SqlserverDepartment();
        }
    }

    //Access实例化工厂,负责实例化AccessUser和AccessDepartment
    class AccessFactory : IFactory
    {
        public IUser CreateUser()
        {
            return new AccessUser();
        }

        public IDepartment CreateDepartment()
        {
            return new AccessDepartment();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IFactory factory = new SqlServerFactory();

            IUser iu = factory.CreateUser();    //获得数据库User访问类的对象
            iu.Insert(new User());
            iu.GetUser(1);

            IDepartment idepartment = factory.CreateDepartment();   //获得数据库Department访问类的对象
            idepartment.Insert(new Department());
            idepartment.GetDepartment(1);

            Console.ReadKey();
        }
    }  
}

  其实以上代码就叫做抽象工厂模式了,当只有一个User表的时候,就叫工厂方法模式。当有了多个表了,就叫抽象工厂模式。但是以上代码是非常不好的,为什么?例如当你还要增加一个Project表的时候,就需要增加三个类,IProject、SqlserverProject、AccessProject,同时还需要更改IFactory、SqlserverFactory、AccessFactory。增加一张表就要更改三个类,这个是比较悲剧的。

  因此,以下给出一个用简单工厂模式来优化抽象工厂模式的代码:

namespace ConsoleApplication1
{
    class User
    {
        private int _id;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
    }

    class Department
    {
        private int _id;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        private string _deptNmae
        {
            get { return _deptNmae; }
            set { _deptNmae = value; }
        }
    }

    //IUser接口,用于客户端访问,解除与具体数据库访问的耦合
    interface IUser
    {
        void Insert(User user);
        User GetUser(int id);
    }

    class SqlserverUser : IUser 
    {
        public void Insert(User user)
        {
            Console.WriteLine("Sql Server添加一条记录");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("Sql Server根据ID得到User表的一条记录");
            return null;
        }
    }

    class AccessUser : IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Access中给User表增加一条记录");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("在Access中根据ID得到User表的一条记录");
            return null;
        }
    }

    interface IDepartment
    {
        void Insert(Department department);
        Department GetDepartment(int id);
    }

    class SqlserverDepartment : IDepartment
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Sqlserver中给Department表插入一条记录");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Sqlserver中根据ID得到Department表的一条记录");
            return null;
        }
    }

    class AccessDepartment : IDepartment 
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Access中给Department表插入一条记录");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Access中根据ID得到Department表的一条记录");
            return null;
        }
    }

    class DataAccess
    {
        private static readonly string db = "SqlServer";    //事先就设置好的数据库
        //private static readonly string db = "Access";     //也就是说,更改数据库改的是这里了 

        public static IUser CreateUser()    //这是第一个User的简单工厂
        {
            IUser result = null;
            switch (db)         //根据db的设置实例化不同的User数据库访问对象
            {
                case "SqlServer":
                    result = new SqlserverUser();
                    break;
                case "Access":
                    result = new AccessUser();
                    break;
            }
            return result;
        }

        public static IDepartment CreateDepartment()    //这是Department的简单工厂
        {   
            IDepartment result = null;
            switch (db)
            {
                case "Sqlserver":
                    result = new SqlserverDepartment();
                    break;
                case "Access":
                    result = new AccessDepartment();
                    break;
            }
            return result;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IUser iu = DataAccess.CreateUser();    //获得数据库User访问类的对象,要改数据库就要改DataAccess里面的设置
            iu.Insert(new User());
            iu.GetUser(1);

            IDepartment idepartment = DataAccess.CreateDepartment();   //获得数据库Department访问类的对象,这里没有依赖SqlserverFactory或AccessFactory了
            idepartment.Insert(new Department());
            idepartment.GetDepartment(1);

            Console.ReadKey();
        }
    }  
}

  经过简单工厂模式优化之后,客户端就不再依赖于SqlserverFactory或AccessFactory了,带到了解耦的目的。如果要添加一个Project,只需要添加响应类,但改就只需要修改DataAccess就可以了(在DataAccess里面在添加一个简单工厂)。很明显者不是最终方案,因为简单工厂方法还是要修改,switch case。

  以下还有通过反射实现的更加好的方案:

namespace ConsoleApplication1
{
    class User
    {
        private int _id;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
    }

    class Department
    {
        private int _id;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        private string _deptNmae
        {
            get { return _deptNmae; }
            set { _deptNmae = value; }
        }
    }

    //IUser接口,用于客户端访问,解除与具体数据库访问的耦合
    interface IUser
    {
        void Insert(User user);
        User GetUser(int id);
    }

    class SqlserverUser : IUser 
    {
        public void Insert(User user)
        {
            Console.WriteLine("Sql Server添加一条记录");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("Sql Server根据ID得到User表的一条记录");
            return null;
        }
    }

    class AccessUser : IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Access中给User表增加一条记录");
        }

        public User GetUser(int id)
        {
            Console.WriteLine("在Access中根据ID得到User表的一条记录");
            return null;
        }
    }

    interface IDepartment
    {
        void Insert(Department department);
        Department GetDepartment(int id);
    }

    class SqlserverDepartment : IDepartment
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Sqlserver中给Department表插入一条记录");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Sqlserver中根据ID得到Department表的一条记录");
            return null;
        }
    }

    class AccessDepartment : IDepartment 
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Access中给Department表插入一条记录");
        }

        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Access中根据ID得到Department表的一条记录");
            return null;
        }
    }

    class DataAccess
    {
        private static readonly string AssemblyName = "ConsoleApplication1";    //字符串为程序集的名字
        private static readonly string db = ConfigurationManager.AppSettings["DB"];

        public static IUser CreateUser()
        {
            string className = AssemblyName + "." + db +"User"; //拼接SqlserverUser类或AccessUser类所在的位置
            return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);    //反射创建实例
        }

        public static IDepartment CreateDepartment()
        {
            string className = AssemblyName + "." + "SqlserverDepartment";
            return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IUser iu = DataAccess.CreateUser();    //获得数据库User访问类的对象,要改数据库就要改DataAccess里面的设置
            iu.Insert(new User());
            iu.GetUser(1);

            IDepartment idepartment = DataAccess.CreateDepartment();   //获得数据库Department访问类的对象
            idepartment.Insert(new Department());
            idepartment.GetDepartment(1);

            Console.ReadKey();
        }
    }  
}

  配置文件App.config代码:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="DB" value="Access" />
  </appSettings>
</configuration>

  以上就是反射+抽象工厂的方法, 还是非常强大的,强大到什么程度呢?例如上面的代码,要修改数据库访问对象例如将Sqlserver改为Access根本不需要改动程序重新编译,只需要改动App.config就可以了。其实这里的反射只是改进了简单工厂模式而已,与抽象工厂没太大的关系。要记住,所有在用到简单工厂的地方都可以考虑用反射技术来去除switch解除分支判断所带来的耦合。

  在以上的代码中,要增加一个Project表的话,只需要添加三个Project相关的类(扩展),再修改DataAccess,在其中增加一个CreateProject()方法就可以了。

你可能感兴趣的:(抽象工厂模式)