设计模式之-抽象工厂模式

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
简介: 前言 我们先来看一段基本的数据访问代码,以‘新增用户’和得到用户为例,假设只有ID和Name两个字段,其余省略。 class User { private int _id; public int ID { ...

前言

我们先来看一段基本的数据访问代码,以‘新增用户’和得到用户为例,假设只有ID和Name两个字段,其余省略。

 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; }
        }
    }

SqlserverUser类-用于操作User表

 public class SqlserverUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Sql Server中给User表增加一条记录");
        }
        public User GetUser(int user)
        {
            Console.WriteLine("在Sql Server中根据ID得到User表一条记录");
            return null;
        }
    }

客户端代码

 static void Main(string[] args)
        {
            User user = new User();
            SqlserverUser su = new SqlserverUser();//此处与SQL Server耦合
            su.Insert(user);//插入用户
            su.GetUser(1);//得到ID为1的用户
            Console.Read();
        }

这里和Sql Server数据库耦合,不能做到灵活的更换数据库,如果下次要换成Mysql或者其他数据库,就非常麻烦了。这里我们可以改进下程序,使用工厂方法模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。

 

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

   interface IUser
    {
        void Insert(User user);
        User GetUser(int id);
    }

SqlserverUser类,用于访问SQL Server的User

 public class SqlserverUser:IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Sql Server中给User表增加一条记录");
        }
        public User GetUser(int user)
        {
            Console.WriteLine("在Sql Server中根据ID得到User表一条记录");
            return null;
        }
    }

AccessUser类,用于访问Access的User

 class AccessUser : IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Sql Server中给User表增加一条记录");
        }
        public User GetUser(int user)
        {
            Console.WriteLine("在Sql Server中根据ID得到User表一条记录");
            return null;
        }
    }

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

 /// <summary>
    /// 创建访问User表对象的抽象工厂接口
    /// </summary>
    interface IFactory
    {
        IUser CreateUser();
    }

SqlServerFactory类,实现IFactory接口,实例化SqlserverUser

 /// <summary>
    /// 实现IFactory接口,实例化SqlserverUser
    /// </summary>
    class SqlServerFactory : IFactory
    {
        public IUser CreateUser()
        {
            return new SqlserverUser();
        }
    }

AccessFactory类,实现IFactory接口,实例化AccessUser

    /// <summary>
    /// 实现IFactory接口,实例化AccessUser
    /// </summary>
    /// <returns></returns>
    class AccessFactory : IFactory
    {
        public IUser CreateUser()
        {
            return new AccessUser();
        }
    }

 客户端代码

       static void Main(string[] args)
        {
            User user = new User();
            //若要改成Access数据库,只需要将本剧改成  IFactory factory = new AccessFactory();
            IFactory factory = new SqlServerFactory();
            IUser iu = factory.CreateUser();
            iu.Insert(user);
            iu.GetUser(1);
            Console.Read();
        }

程序到这里依然还存在问题,虽然我们把业务逻辑和数据访问解耦了,但是如果我们数据此时新增其他的表,比如部门表(Department),此时程序应该怎样才会更灵活呢?思考五秒。。。。。

代码结构图如下

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

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

SqlserverDepartment类,用于访问SQL server 的Department.

 class SqlserverDepartment:IDepartment
    {
        public void Insert(Department user)
        {
            Console.WriteLine("在Sql Server中给Department表增加一条记录");
        }
        public Department GetUser(int user)
        {
            Console.WriteLine("在Sql Server中根据ID得到Department表一条记录");
            return null;
        }

    }

AccessDepartment类,用于访问Access的Department

class AccessDepartment : IDepartment
    {
        public void Insert(Department user)
        {
            Console.WriteLine("在Sql Server中给Department表增加一条记录");
        }
        public Department GetUser(int user)
        {
            Console.WriteLine("在Sql Server中根据ID得到Department表一条记录");
            return null;
        }
    }

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

 /// <summary>
    /// 创建访问表对象的抽象工厂接口
    /// </summary>
    interface IFactory
    {
        IUser CreateUser();
        IDepartment CreateDepartment();//增加接口方法
    }

SqlServerFactory类,实现IFactory接口,实例化SqlServerDepartment和SqlServerUser

/// <summary>
    /// 实现IFactory接口,实例化SqlserverUser
    /// </summary>
    class SqlServerFactory : IFactory
    {
        public IUser CreateUser()
        {
            return new SqlserverUser();
        }

        /// <summary>
        /// 新增SqlserverDepartment工厂
        /// </summary>
        /// <returns></returns>
        public IDepartment CreateDepartment()
        {
            return new SqlserverDepartment();
        }
    }

AccessFactory类,实现了IFactory接口,实例化User和Department

 /// <summary>
    /// 实现IFactory接口,实例化AccessUser
    /// </summary>
    /// <returns></returns>
    class AccessFactory : IFactory
    {
        public IUser CreateUser()
        {
            return new AccessUser();
        }
        /// <summary>
        /// 新增OleDBDepartment工厂
        /// </summary>
        /// <returns></returns>
        public IDepartment CreateDepartment()
        {
            return new AccessDepartment();
        }
    }

客户端代码

 static void Main(string[] args)
        {
            User user = new User();
            Department dept = new Department();
            //只需确定实例化哪一个数据库访问对象给factory
            //IFactory factory = new SqlServerFactory();
            IFactory factory = new AccessFactory();
            //则此时已于具体的数据库访问接触了依赖
            IUser iu = factory.CreateUser();
            iu.Insert(user);
            iu.GetUser(1);
            //则此时已于具体的数据库访问接触了依赖
            IDepartment id = factory.CreateDepartment();
            id.Insert(dept);
            id.GetDepartment(1);
            Console.Read();
        }

此时,我们会发现数据库中有很多个表,和SQL Server和Access又是两大不同的分类,所以解决这种涉及到多产品系列的问题,我们可以使用抽象工厂模式。

抽象工厂模式

 

 

AbstractProductA和AbstractProductB是两个抽象产品,之所以为抽象,是因为他们都有可能有两种不同的实现。就上面的例子来说就是User和Department,而ProductA1,ProductA2和ProductB1,ProductB2就是对两个抽象产品的具体分类实现 ,比如ProductA1可以理解是SqlserverUser,而ProductB1是AccessUser。

IFactory是一个抽象工厂接口,它里面应该包含所有的产品创建的抽象方法,而ConcreateFactory1和ConcreateFactory2就是具体的工厂了,就像 SqlserverFactory和AccessFactory一样。

这样做优点和缺点?

好处:易于交换产品系列,由于是具体工厂类,例如IFactory factory=new AccessFactory,再一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂边的非常容易,它只需要改变具体工厂即可使用不同的产品配置。

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

缺点:很明显,我们新增一个表改动的地方很多,接口、工厂类,具体实现,这太糟糕了

 

用简单工厂来改进抽象工厂

去除IFactory/SqlserverFactory和AccessFactory三个类,取而代之的是DataAccess类,用一个简单工厂模式来实现

 class DataAccess
    {
        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 CreateUser()
        {
            IDepartment result = null;
            switch (db)
            {
                case "Sqlserver":
                    result = new SqlserverDepartment();
                    break;
                case "Access":
                    result = new AccessDepartment();
                    break;
            }
            return result;
        }
    }

由于db的实现设置,所以swtich中可以根据选择实例化出相应的对象

 static void Main(string[] args)
        {
            User user = new User();
            Department dept = new Department();
            //直接得到实际的数据库访问实例,而不存在任何依赖
            IUser iu = DataAccess.CreateUser();
            iu.Insert(user);
            iu.GetUser(1);

            IDepartment id = DataAccess.CreateDepartment();
            id.Insert(dept);
            id.GetDepartment(1);

            Console.Read();
        }

这里用简单工厂来实现了,我们抛弃了IFactory、SqlserverFactory和AccessFactory三个工厂类,取而代之的是DataAccess类,客户端没有出现任何一个Sqlserver 和Access的字样,达到了解耦的目的。

不过此时还不是最完美的,因为我们需要增加Oracle的话,现在需要在DataAccess类中每个方法的swicth中加case了。

反射+抽象工厂

上述问题的关键在于我们如何去解决switch的问题,可以使用依赖注入(Dependency Injection),本来依赖注入需要专门的IoC容器提供,比如:Spring.NET,显然这个程序不需要这么麻烦。

程序引用:using System.Reflection 就可以使用反射帮我们克服抽象工厂模式的先天不足了。

DataAccess类,用反射技术,取代IFactory、SqlserverFactory和AccessFactory.

class DataAccess
    {
        private static readonly string AssemblyName = "程序集名称";
        private static readonly string db = "Sqlserver";//数据库名称,可以替换成Access

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

用反射+配置文件实现数据库访问

 

最后得到执行结果:

 

总结:所有简单工厂的地方,都可以考虑用反射技术取出swtich或if,接触分支判断带来的耦合。

 

  • 感谢你的阅读。如果你觉得这篇文章对你有帮助或者有启发,就请推荐一下吧~你的精神支持是博主强大的写作动力。欢迎转载!
  • 博主的文章没有高度、深度和广度,只是凑字数。由于博主的水平不高(其实是个菜B),不足和错误之处在所难免,希望大家能够批评指出。
  • 欢迎加入.NET 从入门到精通技术讨论群→523490820 期待你的加入
  • 不舍得打乱,就永远学不会复原。被人嘲笑的梦想,才更有实现的价值。
  • 我的博客:http://www.cnblogs.com/zhangxiaoyong/
相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
6月前
|
设计模式 C++
设计模式之抽象工厂模式(C++)
设计模式之抽象工厂模式(C++)
|
6月前
|
设计模式
设计模式~抽象工厂模式-04
抽象工厂 优点 缺点 使用场景 为创建一组相
29 0
|
4月前
|
设计模式 Java
Java设计模式【三】:抽象工厂模式
Java设计模式【三】:抽象工厂模式
18 0
|
7月前
|
设计模式
设计模式~~~抽象工厂模式
设计模式~~~抽象工厂模式
28 0
|
15天前
|
设计模式 Java Windows
23种设计模式,抽象工厂模式的概念优缺点以及JAVA代码举例
【4月更文挑战第10天】抽象工厂模式是一种创建型设计模式,它提供了一个接口用于创建相关或依赖对象的家族,而不需要指定具体类。该模式允许客户端在不知道具体类的情况下,通过其共同的接口来创建一组产品。
27 7
|
3月前
|
设计模式 Oracle 关系型数据库
设计模式 | 抽象工厂模式
设计模式 | 抽象工厂模式
22 0
|
3月前
|
设计模式 Go 开发工具
Golang设计模式——02抽象工厂模式
Golang设计模式——02抽象工厂模式
21 0
|
8月前
|
设计模式 Java 数据库连接
Java设计模式之抽象工厂模式:创造多个相关对象
在软件开发领域,设计模式是一种用于解决常见问题的实践方法,它提供了一些被验证过的指导原则。抽象工厂模式是创建型设计模式中的佼佼者,可以帮助我们在处理多个相关对象的创建时保持一致性。让我们深入了解抽象工厂模式的内涵以及它如何在Java中应用。
61 0
|
3月前
|
设计模式 前端开发
【设计模式】之抽象工厂模式
抽象工厂模式是一种创建型设计模式,适用于需要创建一系列相关或相互依赖对象的场景。在前端开发中,抽象工厂模式可以帮助我们更好地组织和管理代码,提高代码的可维护性和可扩展性。它通过封装对象的创建过程,使得客户端代码与具体类解耦,并且可以通过切换具体工厂类来改变整个系统的行为。然而,它也增加了系统的复杂度,并且当产品族较多时会导致大量的具体工厂类。因此,在使用抽象工厂模式时需要权衡利弊,并根据实际情况进行选择。
53 0
【设计模式】之抽象工厂模式
|
3月前
|
设计模式 NoSQL 中间件
设计模式 - 创建型模式_抽象工厂模式
设计模式 - 创建型模式_抽象工厂模式
21 0