zoukankan      html  css  js  c++  java
  • 简单架构:反射实现抽象工厂+IDAL接口完全独立DAL

    一、普通架构中存在的问题 

    StudentDB数据库,包含一张StudentInfoTB表,结构如下:

    s_id int primary key identity(1,1),
    s_name Nvarchar(10) not null,
    s_age int check(s_age >10 and s_age<30),
    s_sex bit not null

    先来看一下普通的架构的问题所在:

    调用关系:

    dal层代码只是通过SqlHelper简单的操作一下数据库,就不展示了。

    StudentInfo的bll层代码,实例化了一个dal层对象,并且每个方法返回对应的方法:

    private StudentInfoADODal dal = new StudentInfoADODal();
    public List<StudentInfoModel> Select()
    {
        return dal.Select();
    }
    
    public int Update(StudentInfoModel siModel)
    {
        return dal.Update(siModel);
    }
    
    public int Delete(int id)
    {
        return dal.Delete(id);
    }
    
    public int Add(StudentInfoModel siModel)
    {
        return dal.Add(siModel);
    }
    
    public StudentInfoModel Get(int id)
    {
        return dal.Get(id);
    }
    View Code

    ui层调用:

    static void Main(string[] args)
    {
        Display();
        Console.ReadKey();
    }
    
    static void Display()
    {
        //实例化bll层对象
        StudentInfoBll bll = new StudentInfoBll();
        //接收返回值
        List<StudentInfoModel> lstModel = bll.Select();
        //输出标题
        Console.WriteLine($"编号	姓名	年龄	性别");
        //循环输出
        foreach (StudentInfoModel  item in lstModel)
        {
            Console.WriteLine($"{item.s_id}	{item.s_name}	{item.s_age}	{item.s_sex}");
        }
    }

    输出:

     

    一切看起来都没什么问题,可是如果后续逻辑复杂起来了,并且Dal层对象有变动的话,我们是不是需要在Bll层修改实例化的Dal层对象,而且一但更改,所有位置的Dal层对象都会修改,现在代码很少,东西也不复杂,可是如果代码复杂了,修改的话就不是太方便了。

    首先我们应该想到的是工厂模式,因为如果Dal层有变动的话,我们只需要修改工厂模式中返回的对象就行了,但是如果使用简单工厂模式的话,返回值是个问题,Dal层改变的话,返回值肯定也会发生改变,到时候还是要对bll层接收对象修改类型。

    二、抽象工厂+IDal

    所以我们应该考虑使用抽象工厂,既然要用抽象工厂,那么就必须有一个具体的父类或者接口,但是Dal层并没有继承任何父类或实现任何接口(Object除外),所以我们应该抽象出一个IDal层,使得所有的Dal层都要去实现IDal层,然后用IDal层的接口作为返回值,返回给Bll层,后续如果在要修改Dal层的话,只要Dal层实现了IDal层我们是不是就不用在对Bll层做任何修改了。

    接下来先创建IDal层:

    然后抽象出IStudentInfoDal接口:

    public interface IStudentInfoDal
    {
        List<StudentInfoModel> Select();
        int Update(StudentInfoModel siModel);
        int Delete(int id);
        int Add(StudentInfoModel siModel);
        StudentInfoModel Get(int id);
    }

    StudentInfoADODal层实现IStudentDal接口:

    下面就是使用工厂模式来进行创建对象,先创建工厂层和DalFactory类:

    public class DalFactory
    {
        public static IStudentInfoDal CreateStudentInfoInstance()
        {
            return new StudentInfoADODal();
        }
    }

    Bll层调用的话直接声明接口,然后通过工厂模式来获取Dal层对象:

    private IStudentInfoDal dal = DalFactory.CreateStudentInfoInstance();

    这下如果后续需要更改的话,直接就更改工厂模式这一个地方就行了,其他地方就不用做更改了。

    现在看一下调用关系,箭头是引用关系

    可以发现,Dal层并没有做到完全独立起来,Factory层还是在引用Dal层,更换数据库的话,还是要重新添加引用,重新修改工厂模式中的代码。小项目不分层都是可以的,如果项目很复杂,做任何事情之前都要考虑周到,任何细节都要处理好,所以就想办法把Dal层完全独立起来

    三、反射+App.config实现抽象工厂

     反射就是能够动态加载程序集,不需要添加对程序集的引用,就可以获取程序集内部的结构(属性、方法),可以实现动态创建对象,调用对象的方法,为属性赋值等操作。所以,在创建Dal层对象时,我们可以考虑使用反射来创建。

    反射所在命名空间:System.Reflection;  其实就是先将dll文件给加载到Assembly对象中,然后通过Assembly对象创建dll文件中的对象(反射还有其他的几个常用的对象Type、Activator、PropertyInfo...)

    使用反射创建dal层对象:

    public static IStudentInfoDal CreateStudentInfoInstance()
    {
        //使用Assembly来加载程序集 
        Assembly assembly = Assembly.Load("CKKA.ADODal");
        //通过assembly对象来创建一个StudentInfoADODal实例
        //必须是完整的类型名称         类型所在命名空间+“.”+类名
        Object siDal = assembly.CreateInstance("CKKA.ADODal.StudentInfoADODal");
        return siDal as IStudentInfoDal;
    }

    然后右键CKKA.ADODal-->属性-->最左侧生成-->下方输出路径改为Ui/bin/debug或者Ui/bin(具体可以自己打开文件夹下看那个目录下有dll文件),详情看这篇帖子https://www.cnblogs.com/ckka/p/11331037.html

    现在运行该程序是可以反射成功的

    我们把将抽象工厂再次改造一下,将CKKA.ADODal和后缀ADODal写在App.config的appSettings节点下(web项目写在web.config)

    <add key="AssemblyName" value="CKKA.ADODal"/>
    <add key="Suffix" value="ADODal"/>

    抽象工厂改进为(需要为Factory添加System.Configuration程序集和命名空间的引用):

    public class DalFactory
    {
        //从配置文件中读取AssemblyName(程序集名称)和Suffix(Dal层扩展名)
        private static String AssemblyName = ConfigurationManager.AppSettings["AssemblyName"];
        private static String Suffix = ConfigurationManager.AppSettings["Suffix"];
    
        //每个方法都调用此方法来创建对象
        private static Object CreateInstance(String TypeName)
        {
            return Assembly.Load(AssemblyName).CreateInstance(TypeName);
        }
        
        public static IStudentInfoDal CreateStudentInfoInstance()
        {   
            //拼接类型名称
            String TypeName = $"{AssemblyName}.StudentInfo{Suffix}";
            //创建实例
            return CreateInstance(TypeName) as IStudentInfoDal;
        }
        
    }

    以后我们如果需要更换数据库的话,只需要修改配置文件,操作数据库的Dal实现IDal接口,并且bin目录下有dll文件就行了,不需要更改任何代码。

    由于没有其他数据库,我们就用EF操作数据库来测试一下:

    将CKKA.EFDal的生成路径更改一下,然后修改配置文件为:

    然后运行:

    可以发现,完全不用改任何代码,就可以做到更换一整个Dal或者数据库,最终调用结构为:

    如果我哪里写的有问题或者我说的不够清楚或者你有疑问的话,欢迎留言

  • 相关阅读:
    类似-Xms、-Xmn这些参数的含义:
    类似-Xms、-Xmn这些参数的含义:
    类似-Xms、-Xmn这些参数的含义:
    类似-Xms、-Xmn这些参数的含义:
    Java 虚拟机是如何判定两个 Java 类是相同的?
    Java 虚拟机是如何判定两个 Java 类是相同的?
    Java 虚拟机是如何判定两个 Java 类是相同的?
    Java 虚拟机是如何判定两个 Java 类是相同的?
    互联网支付系统整体架构详解
    DTO
  • 原文地址:https://www.cnblogs.com/ckka/p/11411632.html
Copyright © 2011-2022 走看看