抽象工厂模式

就拿数据库访问来讲:

    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; }
        }
    }
用户类User
    class SqlServerUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Sqlserver中给User表增加一条记录");
        }
        public User GetUser(int id)
        {
            Console.WriteLine("在Sqlserver中根据ID得到user表中一条记录");
            return null;
        }
    }
SqlServerUser
            User user = new User();
            SqlServerUser su = new SqlServerUser();
            su.Insert(user);
            su.GetUser(1);
客户端代码

这里我们发现不能更换数据库,因为SqlServerUser su = new SqlServerUser();将su框死在SqlServerUser上了,如果这里是灵活的,专业点的说法,这里是多态的,那么在执行Insert和GetUser的时候就不用考虑是Sql Server还是Access了。

可以使用工厂方法来封装new SqlServerUser();造成的变化【工厂方法模式定义一个创建对象的接口,让子类决定去实例化哪儿一个类】

工厂方法模式·使用:

    interface IUser
    {
        void Insert(User user);
        User GetUser(int id);
    }
IUser
    class SqlServerUser:IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Sqlserver中给User表增加一条记录");
        }
        public User GetUser(int id)
        {
            Console.WriteLine("在Sqlserver中根据ID得到user表中一条记录");
            return null;
        }
    }
SqlServerUser
    class AccessUser:IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Access中给User表增加一条记录");
        }
        public User GetUser(int id)
        {
            Console.WriteLine("在Access中根据ID得到user表中一条记录");
            return null;
        }
    }
AccessUser
    interface IFactory
    {
        IUser CreatUser();
    }
IFactory
    class SqlServerFactory:IFactory
    {
       public  IUser CreatUser()
        {
            return new SqlServerUser();
        }
    }
SqlServerFactory
    class AccessFactory:IFactory
    {
        public IUser CreatUser()
        {
            return new AccessUser();
        }
    }
AccessFactory
            User user = new User();
            IFactory factory = new SqlServerFactory();
            IUser iu = factory.CreatUser();
            iu.Insert(user);
            iu.GetUser(1);
客户端代码

现在如果要换数据库,只需要把new SqlServerFactory0改成new AccessFactory0,此时由于多态的关系,使得声明IUser接口的对象iu事先根本不知道是在访问哪个数据库,却可以在运行时很

好地完成工作,这就是所谓的业务逻辑与数据访问的解耦。”

但是数据库中总不能只有一个User表吧,肯定会有其他表,比如增加部门表。【Department表】

IDepartment接口,用于客户端访问,解除与具体数据库访问的耦合。

    interface IDepartment
    {
        void Insert(Department department);
        Department GetDepartment(int id);
    }
IDepartment
    class SqlServerDepartment : IDepartment
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Sqlserver中给Department表增加一条记录");
        }
    
        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Sqlserver中根据ID得到Department表中一条记录");
            return null;
        }
    }
SqlServerDepartment
    class AccessDepartment:IDepartment
    {
        public void Insert(Department department)
        {
            Console.WriteLine("在Access中给Department表增加一条记录");
        }
        public Department GetDepartment(int id)
        {
            Console.WriteLine("在Access中根据ID得到Department表中一条记录");
            return null;
        }
    }
AccessDepartment

IFactory接口,定义一个创建访问User表对象的抽象的工厂接口。

    interface IFactory
    {
        IUser CreatUser();
        IDepartment CreatDepartment();//新增
    }
IFactory

SqlServerFactory类,实现IFactory 接口,实例化SqlserverUser和SqlserverDepartment。

    class SqlServerFactory : IFactory
    {
        public IUser CreatUser()
        {
            return new SqlServerUser();
        }
        public IDepartment CreatDepartment()  //新增
        {
            return new SqlServerDepartment();
        }
    }
SqlServerFactory

AccessFactory类,实现IFactory接口,实例化AccessUser和AccessDepartment。

    class AccessFactory:IFactory
    {
        public IUser CreatUser()
        {
            return new AccessUser();
        }
        public IDepartment CreatDepartment()
        {
            return new AccessDepartment();
        }
    }
AccessFactory
            User user = new User();
            Department dept = new Department();
            IFactory factory = new AccessFactory();
            IUser iu = factory.CreatUser();
            iu.Insert(user);
            iu.GetUser(1);

            IDepartment id = factory.CreatDepartment();
            id.Insert(dept);
            id.GetDepartment(1);
客户端代码

这样就可以做到,只需更改IFactory factory = new AccessFactoryO为lIFactory factory = newSqlServerFactory0,就实现了数据库访问的切换了。”

此时重构出了一个非常重要的设计模式。” 

“只有一个User类和User操作类的时候,是只需要工厂方法模式的,但现在显然你数据库中有很多的表,而SQL Server 与Access又是两大不同的分类,所以解决这种涉及到多个产品系列的问题,有一

个专门的工厂模式叫抽象工厂模式。”

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

抽象工厂模式的优点和缺点:

优点:“最大的好处便是易于交换产品系列,由于具体工厂类,例如IFactory factory = new AccessFactory(),在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,

它只需要改变具体工厂即可使用不同的产品配置。我们的设计不能去防止需求的更改,那么我们的理想便是让改动变得最小,现在如果你要更改数据库访问,我们只需要更改具体工厂就可以做到。

第二大好处是,它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。

事实上,刚才写的例子,客户端所认识的只有IUser和IDepartment,至于它是用SQL Server来实现还是Access来实现就不知道了。”将开放-封闭原则,依赖倒转原则发挥到了极致

缺点:假如,再增加一个表IPoject,那么就要增加三个类IProject、 SqlserverProject、 AccessProject, 还需要更改IFactory、SqlserverFactory和AccessFactory才可以完全实现。要改三个类,这太糟糕了。”

而且客户端程序类又不会只有一个,有很多地方都在使用IUser和IDepartment这样的设计在每个类开始都要声明 IFactory factory = new AccessFactory();这样的代码才行,不能解决改变数据库访问只改变一处的要求。

用简单工厂改进抽象工厂

首先去掉IFactory,SqlServerFactory,AccessFactory,增加DateAccess类

    class DateAccess
    {
        private static readonly string db = "SqlServer";
        //private static readonly string db = "Access";
        public static IUser CreateUser()
        {
            IUser result = null;
            switch (db)
            {
                case "SqlServer":
                    result = new SqlServerUser();
                    break;
                case "Access":
                    result = new AccessUser();
                    break;
            }
            return result;
        }
        public static IDepartment CreateDepartment()
        {
            IDepartment result = null;
            switch (db)
            {
                case "SqlServer":
                    result = new SqlServerDepartment();
                    break;
                case "Access":
                    result = new AccessDepartment();
                    break;
            }
            return result;
        }
    }
DateAccess
            User user = new User();
            Department dept = new Department();
            IUser iu = DateAccess.CreateUser();
            iu.Insert(user);
            iu.GetUser(1);

            IDepartment id = DateAccess.CreateDepartment();
            id.Insert(dept);
            id.GetDepartment(1);
客户端代码

 

使用反射+抽象工厂的数据访问程序

“我们要考虑的就是可不可以不在程序里写明‘ 如果是Sqlserver 就去实例化SQL Server数据库相关类,如果是Access就去实例化Access相关类’这样的语句,而是根据字符串db的值去某个地方找应该要实例化的类是哪一一个。

这样,我们的switch就可以对它说再见了。” 什么叫‘ 去某个地方找应该要实例化的类是哪一一个 就是一 种编程方式:依赖注入(Dependency Injection), 从字面上不太好理解,我们也不去管它。

关键在于如何去用这种方法来解决我们的switch问题。本来依赖注入是需要专门的IoC容器提供,比如Spring.NET,显然当前这个程序不需要这么麻烦,你只需要再了解一一个简单的.NET技术‘反射’就可以了。”

 

posted @ 2022-05-16 14:27  C#工控菜鸟  阅读(9)  评论(0编辑  收藏  举报