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

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 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
目录
相关文章
|
4月前
|
设计模式 PHP
php设计模式--抽象工厂模式(二)
php设计模式--抽象工厂模式(二)
27 0
|
3月前
|
设计模式
**工厂模式与抽象工厂模式**都是创建型设计模式,用于封装对象创建,减少耦合
【6月更文挑战第23天】**工厂模式与抽象工厂模式**都是创建型设计模式,用于封装对象创建,减少耦合。工厂模式专注于单个对象,通过具体工厂创建具体产品,适用于简单对象创建;抽象工厂则关注一系列相关产品,提供创建一族对象的接口,适用于处理多个不兼容产品族。选择模式基于问题域的复杂性,单个产品需求时用工厂模式,多产品族时用抽象工厂模式。
29 5
|
4月前
|
设计模式 Java
【设计模式】JAVA Design Patterns——Abstract Factory(抽象工厂模式)
【设计模式】JAVA Design Patterns——Abstract Factory(抽象工厂模式)
|
4月前
|
设计模式 Java
Java一分钟之-设计模式:工厂模式与抽象工厂模式
【5月更文挑战第17天】本文探讨了软件工程中的两种创建型设计模式——工厂模式和抽象工厂模式。工厂模式提供了一个创建对象的接口,延迟实例化到子类决定。过度使用或违反单一职责原则可能导致问题。代码示例展示了如何创建形状的工厂。抽象工厂模式则用于创建一系列相关对象,而不指定具体类,但添加新产品可能需修改现有工厂。代码示例展示了创建颜色和形状的工厂。根据需求选择模式,注意灵活性和耦合度。理解并恰当运用这些模式能提升代码质量。
53 2
|
4月前
|
设计模式 Java
【设计模式系列笔记】抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是一种设计模式,属于创建型模式之一。它提供了一种方式来创建一系列相关或相互依赖的对象,而无需指定它们具体的类。抽象工厂模式通过引入抽象的工厂接口,使得客户端代码可以使用抽象的接口来创建一组相关的产品,而不关心这些产品的具体实现。
158 4
|
22天前
|
设计模式 Java
Java 设计模式之谜:工厂模式与抽象工厂模式究竟隐藏着怎样的神奇力量?
【8月更文挑战第30天】在Java编程中,设计模式为常见问题提供了高效解决方案。工厂模式与抽象工厂模式是常用的对象创建型设计模式,能显著提升代码的灵活性、可维护性和可扩展性。工厂模式通过定义创建对象的接口让子类决定实例化哪个类;而抽象工厂模式则进一步提供了一个创建一系列相关或相互依赖对象的接口,无需指定具体类。这种方式使得系统更易于扩展和维护。
30 1
|
1月前
|
设计模式 XML 存储
【三】设计模式~~~创建型模式~~~抽象工厂模式(Java)
文章详细介绍了抽象工厂模式,这是一种创建型设计模式,用于提供一个接口以创建一系列相关或相互依赖的对象,而不指定它们具体的类。通过代码示例和结构图,文章展示了抽象工厂模式的动机、定义、结构、优点、缺点以及适用场景,并探讨了如何通过配置文件和反射机制实现工厂的动态创建。
【三】设计模式~~~创建型模式~~~抽象工厂模式(Java)
|
1月前
|
设计模式 Java C语言
设计模式-----------工厂模式之抽象工厂模式(创建型)
抽象工厂模式是一种创建型设计模式,它提供了一个接口用于创建一系列相关或相互依赖的对象,而无需指定具体类,从而增强了程序的可扩展性并确保客户端只使用同一产品族的产品。
设计模式-----------工厂模式之抽象工厂模式(创建型)
|
1月前
|
设计模式 存储 XML
[设计模式]创建型模式-抽象工厂模式
[设计模式]创建型模式-抽象工厂模式
|
3月前
|
设计模式 SQL 关系型数据库
抽象工厂模式-大话设计模式
抽象工厂模式-大话设计模式