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

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

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

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

调用关系:

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

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

  1. private StudentInfoADODal dal = new StudentInfoADODal();
  2. public List<StudentInfoModel> Select()
  3. {
  4. return dal.Select();
  5. }
  6.  
  7. public int Update(StudentInfoModel siModel)
  8. {
  9. return dal.Update(siModel);
  10. }
  11.  
  12. public int Delete(int id)
  13. {
  14. return dal.Delete(id);
  15. }
  16.  
  17. public int Add(StudentInfoModel siModel)
  18. {
  19. return dal.Add(siModel);
  20. }
  21.  
  22. public StudentInfoModel Get(int id)
  23. {
  24. return dal.Get(id);
  25. }

ui层调用:

  1. static void Main(string[] args)
  2. {
  3. Display();
  4. Console.ReadKey();
  5. }
  6.  
  7. static void Display()
  8. {
  9. //实例化bll层对象
  10. StudentInfoBll bll = new StudentInfoBll();
  11. //接收返回值
  12. List<StudentInfoModel> lstModel = bll.Select();
  13. //输出标题
  14. Console.WriteLine($"编号\t姓名\t年龄\t性别");
  15. //循环输出
  16. foreach (StudentInfoModel item in lstModel)
  17. {
  18. Console.WriteLine($"{item.s_id}\t{item.s_name}\t{item.s_age}\t{item.s_sex}");
  19. }
  20. }

输出:

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

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

二、抽象工厂+IDal

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

接下来先创建IDal层:

然后抽象出IStudentInfoDal接口:

  1. public interface IStudentInfoDal
  2. {
  3. List<StudentInfoModel> Select();
  4. int Update(StudentInfoModel siModel);
  5. int Delete(int id);
  6. int Add(StudentInfoModel siModel);
  7. StudentInfoModel Get(int id);
  8. }

StudentInfoADODal层实现IStudentDal接口:

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

  1. public class DalFactory
  2. {
  3. public static IStudentInfoDal CreateStudentInfoInstance()
  4. {
  5. return new StudentInfoADODal();
  6. }
  7. }

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

  1. private IStudentInfoDal dal = DalFactory.CreateStudentInfoInstance();

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

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

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

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

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

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

使用反射创建dal层对象:

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

然后右键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)

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

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

  1. public class DalFactory
  2. {
  3. //从配置文件中读取AssemblyName(程序集名称)和Suffix(Dal层扩展名)
  4. private static String AssemblyName = ConfigurationManager.AppSettings["AssemblyName"];
  5. private static String Suffix = ConfigurationManager.AppSettings["Suffix"];
  6.  
  7. //每个方法都调用此方法来创建对象
  8. private static Object CreateInstance(String TypeName)
  9. {
  10. return Assembly.Load(AssemblyName).CreateInstance(TypeName);
  11. }
  12.  
  13. public static IStudentInfoDal CreateStudentInfoInstance()
  14. {
  15. //拼接类型名称
  16. String TypeName = $"{AssemblyName}.StudentInfo{Suffix}";
  17. //创建实例
  18. return CreateInstance(TypeName) as IStudentInfoDal;
  19. }
  20.  
  21. }

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

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

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

然后运行:

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

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

简单架构:反射实现抽象工厂+IDAL接口完全独立DAL的更多相关文章

  1. 泛型、反射和抽象工厂结合解决多DB问题

  2. Asp.Net 设计模式 之 “工厂方法”即利用 接口 实现的抽象工厂

    主要改动部分: /// <summary>    /// 6.创建工厂方法模式(抽象工厂:接口)    /// </summary>    interface IFactory ...

  3. java 抽象工厂模式简单实例

    抽象工厂模式:提供一个创建一系列的相关的或者依赖的对象的接口,无需指定它们的具体实现类,具体的时间分别在子类工厂中产生. 类似于工厂模式:隔离了具体类的生产实现,使得替换具体的工厂实现类很容易.包含有 ...

  4. Head First设计模式——简单工厂、工厂、抽象工厂

    前言:按照惯例我以Head First设计模式的工厂模式例子开始编码学习.并由简单工厂,工厂模式,抽象工厂模式依次演变,归纳他们的相同与不同. 话说Head First认为简单工厂并不是设计模式,而是 ...

  5. python 设计模式之工厂模式 Factory Pattern (简单工厂模式,工厂方法模式,抽象工厂模式)

    十一回了趟老家,十一前工作一大堆忙成了狗,十一回来后又积累了一大堆又 忙成了狗,今天刚好抽了一点空开始写工厂方法模式 我看了<Head First 设计模式>P109--P133 这25页 ...

  6. 设计模式(3)--抽象工厂模式(Absrtact Factory Pattern)

    定义 抽象工厂模式的实质就是提供接口来创建一系列相关或独立的对象而不指定这些对象的具体类. 理解 在软件系统中,经常面临着"一系列相互依赖的对象"的创建工作:同时由于需求的变化,往 ...

  7. 大话设计模式C++版——抽象工厂模式

    前面说过,简单工厂模式是最基础的一种设计模式,那以工厂命名的设计模式就是23种设计模式中最多的一种,他们一脉相承,一步一步进化而来,这里就是其中的最后一种——抽象工厂模式(Abstract Facto ...

  8. 深入浅出设计模式——抽象工厂模式(Abstract Factory)

    模式动机在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法.但是有时候我们需要一个工厂可 ...

  9. C#学习笔记-抽象工厂模式

    题目1:数据访问,通过数据库对用户表单的进行访问,数据库包含SQL Server,对用户表单进行“新增用户”和“查询用户”信息等操作. 分析: 首先,确认用户表单,里面包含两个ID和Name两个字段, ...

随机推荐

  1. 百度OCR文字识别API使用心得===com.baidu.ocr.sdk.exception.SDKError[283604]

    异常com.baidu.ocr.sdk.exception.SDKError[283604]App identifier unmatch.错误的packname或bundleId.logId::303 ...

  2. codewars[7]-python Friend or Foe?

    list中保留四字母的,然后return. 解 def friend(x): i = len(x) ii = [] a = 0 while a < i: if len(x[a]) == 4: i ...

  3. cocos creator 小游戏区域截图功能实现

    截图是游戏中非常常见的一个功能,在cocos中可以通过摄像机和 RenderTexture 可以快速实现一个截图功能,具体API可参考:https://docs.cocos.com/creator/m ...

  4. [Github]watch和star的区别

    1. Watch Issues 以及它们的评论 Pull Requests 及评论 对任何提交的评论 如果没有watch,只有在参与了讨论的情况下会接收到提醒 : 被 @ 提及 被分配 Issues ...

  5. C# Socket服务器端如何判断客户端断开求解

    Socket client //假如已经创建好了,连接到服务器端得Socket的客户端对象. 我们只要client.Poll(10,SelectMode.SelectRead)判断就行了.只要返回Tr ...

  6. 如何在Eclipse中查看Java类库的源代码

    你的JDK安装目录下%Java_home%/src.zip文件就是源码,解压缩找到对应包下面的类即可. 如果是Eclipse开发,ctr+鼠标左击,出现不了源码的话,在弹出的视图中点击attach s ...

  7. 什么是Java EE

    Java:是一门编程语言,可以用来编写各种应用程序. Java EE:是一个标准中间件体系结构,旨在简化和规范分布式多层企业应用系统的开发和部署. Java EE是一个标准中间件体系结构 Java E ...

  8. shiro-helloworld

    1.目录结构 2.log4j.properties # # Licensed to the Apache Software Foundation (ASF) under one # or more c ...

  9. lr参数化

    为什么做参数化? 数据库校验:注册用户时会看数据库有没有这个账号 应用程序校验:pc端qq登陆,一个账号只能登陆一台电脑 1.数据库或应用程序提交值的唯一性校验 数据库查询过程: 1.语法检查.语义检 ...

  10. SpringBoot(18)---通过Lua脚本批量插入数据到Redis布隆过滤器

    通过Lua脚本批量插入数据到布隆过滤器 有关布隆过滤器的原理之前写过一篇博客: 算法(3)---布隆过滤器原理 在实际开发过程中经常会做的一步操作,就是判断当前的key是否存在. 那这篇博客主要分为三 ...