zoukankan      html  css  js  c++  java
  • 抽象工厂模式学习笔记

    最基本的数据访问程序

    User类:表示数据库中字段,假设只有ID和Name两个字段 

    User

    SqlserverUser类:用于操作User表,假设只有“添加用户”和“得到用户”两个方法(这里为了简单,使用两句话代替O(∩_∩)O~)

    SqlserverUser

    客户端代码

    客户端代码

      在这个最基本数据访问程序中,如果要更换数据库,那么如果代码量非常大的情况下,修改数据库代码是非常痛苦的,甚至与数据库无关的代码页需要改。这里之所以不能更换数据库,原因在于

    SqlserverUser su = new SqlserverUser();

    使得su这个对象被框死在SQL Server上了,如果这里是多态的,那么在执行

                      su.Insert(user);
                      su.GetUser(1);

    时,就不用考虑是在用SQL Server还是在用Access或者Oracle了。


    使用工厂方法模式的数据访问程序,解除客户端与具体数据库访问的耦合

    这里我们添加一个IUser接口,用来解除客户端与具体数据库访问的耦合:

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

    这个时候我们就需要修改原先简单的数据访问代码了,因为我们的目的是解除与具体数据访问的耦合,所以需要让数据访问类来继承这个接口(不管是SQL Server还是Access),然后通过工厂模式来创建访问数据库表User的对象。

    首先修改SqlserverUser类,让之继承IUser接口;

    class SqlserverUser:IUser

    然后创建AccessUser类,用来访问Access数据库中的User类

    AccessUser

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

          interface IFactory
          {
                IUser CreateUser();
          }

    SqlserverFactory类,实现IFactory接口,用来实例化SqlserverUser

    复制代码
          class SqlServerFactory:IFactory
          {
                public IUser CreateUser()
                {
                      return new SqlserverUser();
                }
          }
    复制代码

    AccessFactory类,同样实现IFactory接口,用来实例化AccessUser

    复制代码
          class AccessFactory:IFactory
          {
                public IUser CreateUser()
                {
                      return new AccessUser();
                }
          }
    复制代码

    修改后的客户端代码:

    复制代码
                static void Main(string[] args)
                {
                      User user = new User();
                      //IFactory factory = new SqlServerFactory();
                      IFactory factory = new AccessFactory();        //如果需要修改数据库,只需要变更这句代码即可
                      IUser su = factory.CreateUser();
                      su.Insert(user);
                      su.GetUser(1);
                      Console.ReadKey();
                }
    复制代码

    此时由于多态的关系,使得声明IUser接口的对象su事先不知道是在访问哪个数据库,却可以再运行时很好地完成工作,这就解决了所谓的业务逻辑与数据访问的解耦

     

    这个时候问题还没有完全解决,数据库中不可能只有一张User表,如果增加一张Department表,那么依照上面的方法,需要增加一个IDepartment接口;

    然后由AccessDepartment和SqlServerDepartment两个类来实现这个接口,完成对数据库的操作。

    完成对数据库的操作之后,就需要在对应的数据库工厂中创建相应的对象,因为所有的数据库工厂都实现了IFactory接口,所以我们只需要在对应的数据库工厂中添加相应的方法。

    SqlServerFactory
    AccessFactory

    这样一来虽然解决了业务逻辑和数据访问的紧耦合问题,但是如果数据库中的数据量非常庞大的时候,我们也要一张表一个接口的这样做吗?当然不,当只有一个User表的时候,只需要一个User类和User操作类,这时使用工厂方法模式来解决问题;但是现在一般情况下显然数据库中有很多的表,而且SQL Server和Access又是两大不同的分类,所以解决涉及到多个产品系列的问题,就有一种专门的工厂模式:抽象工厂模式。

    抽象工厂模式

    Abstract Factory,提供一个创建一系列相关或相互依赖的接口,而无需指定它们具体的类。

    优点:

      1、便于交换产品系列,由于具体工厂类,比如IFactory factory = new SqlServerFactory(),在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变的非常容易,它只需要改变具体工厂即可使用不同的产品配置;

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

    缺点:

      1、如果有增加功能的需求,比如增加表Project,那么至少需要增加三个类:IProject、SqlServerProject、AccessProject,同时还需要更改IFactory、SqlServerFactory、AccessFactory才能完全实现。所以增加功能时是非常麻烦的。

      2、客户端程序多的情况下(在这里是Main里面的代码),在每一个类的开始都要声明IFactory factory = new SqlServerFactory(),如果有100个调用数据库访问的类,改为iAccess时就需要修改至少100处代码。

    为了解决上面缺点2的问题,我们试着用简单工厂来代替抽象工厂;

    不再使用IFactory、SqlServerFactory、AccessFactory,替换为DataAccess,用一个简单工厂来实现:

    DataAccess

    客户端代码:

    客户端代码

    通过上面的方法在客户端没有出现一个SQL Server或者Access的字样,达到了解耦的目的。

    但是如果需要使用Oracle数据库访问,就需要在每个switch中增加case了。

    用反射+配置文件+抽象工厂实现数据访问程序

    在增加数据库时,相关类的增加是不可避免的,这点目前为止还不能解决,不过开放-封闭原则告诉我们:对于扩展,我们开放;对于修改,我们应该尽量关闭。

    因此我们可以通过反射,来获得当前运行的程序集名称,再通过拼接字符串得到所需要创建的实例的类名,代码如下:

    复制代码
                private static readonly string db = ConfigurationManager.AppSettings["DB"];
                private static readonly string assemblyName = Assembly.GetExecutingAssembly().GetName().Name;
    
                public static IUser CreateUser()
                {
                      string className = assemblyName + "." + db + "User";
                      return (IUser)Assembly.Load(assemblyName).CreateInstance(className);
                }
    
                public static IDepartment CreateDepartment()
                {
                      string className = assemblyName + "." + db + "Department";
                      return (IDepartment)Assembly.Load(assemblyName).CreateInstance(className);
                }
    复制代码

    其中我把相关的数据库字符串放在了配置文件中:

      <appSettings>
        <add key="DB" value="Access"/>
      </appSettings>

    这样就解决了关于抽象工厂数据访问的可维护可扩展的问题。

    --------------------------------------------------------------------

    各位多多指导...Thank you!

  • 相关阅读:
    jupyter中使用熟悉的vim
    解决安装manjaro中安装ccs10.2的库缺失问题
    Markdown中公式
    诗就应该边读边品的,不要
    为neovim田间python3支持
    bilibili视频保存目录
    新工科教育--之我所见
    父母的爱 和汽车的后背箱
    解决manjaro中jupyter无法修改目录和默认浏览器的问题:
    解决jupyter的能打开python文件无法新建的问题
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2483588.html
Copyright © 2011-2022 走看看