企业级应用架构(一) 三层架构之解耦

前言

   前段时间朋友拿了个网站给我,让我帮忙添加几个小功能,我爽快的答应了,但是当我打开源码,我瞬间就奔溃了,整个项目连最基本的三层框架也没有搭建,仅仅是封装了一个sqlhelp作为数据库的操作接口,项目中的SQL查询语句无处不在,业务逻辑紧紧耦合在UI逻辑中,看到这样的代码,坦白来说,我什么兴致都没有了,但是碍着人情,我硬着头皮,把基本功能的完成交差,通过这件事情,我对软件分层进行了深入的思考。

三层架构

  说到三层架构,大伙都很熟悉,我也不再多啰嗦了,我们直接快速搭建一个。

     项目的引用关系是:StructWed->BLL,Model;BLL->DAL,Model;DAL->Model。下面我们来演示一下程序流程,假设我们的数据库有一张订单表Order表,我们的业务是针对Order表进行的增删改查,那么根据三层架构的编程模式,我们就需要建立起对应的Model层实体,数据访问层实体和业务层实体,我们分别用OrderModel,OrderDAL,OrderBLL表示,代码如下,由于仅仅是为了演示,所以我并未提供相应的实现。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace Mode
{public class OrderModel{public int ID { get; set; }public int productName { get; set; }public DateTime CreateTime { get; set; }}
}
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mode;namespace DaL
{public class OrderDAL{/// <summary>/// 向Order表插入数据/// </summary>/// <returns>成功:true,失败:false</returns>public bool Insert(){return true;}/// <summary>/// 修改Order表数据/// </summary>/// <param name="model">表数据对应实体</param>/// <returns>成功:true,失败:false</returns>public bool Update(OrderModel model){return true;}/// <summary>/// 删除Order表指定ID的记录/// </summary>/// <param name="id">表ID</param>/// <returns>成功:true,失败:false</returns>public bool Delete(int id){return true;}}
}
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DaL;
using Mode;namespace Bll
{public class OrderBLL{protected OrderDAL orderDal = new OrderDAL();public bool Insert(OrderModel model){//业务点1//业务点2return orderDal.Insert();}/// <summary>/// 修改Order表数据/// </summary>/// <param name="model">表数据对应实体</param>/// <returns>成功:true,失败:false</returns>public bool Update(OrderModel model){//业务点1//业务点2return orderDal.Update(model);}/// <summary>/// 删除Order表指定ID的记录/// </summary>/// <param name="id">表ID</param>/// <returns>成功:true,失败:false</returns>public bool Delete(int id){//业务点1//业务点2return orderDal.Delete(id);}}
}
View Code

  好了,现在让我们把目光聚焦到OrderBLL上面来。我们发现OrderBLL对数据库Order表的所有操作都是在调用其内部创建的orderDAL实体的同名方法,换句话来说OrderBLL指示其内部orderDAL实体去完成数据库的增删改查。

  好现在,我们思考一下。假设有一天我们发现我们数据访问层的orderDAL实体代码写的很不优雅,效率极差,我们想用一个更优雅的实体,比如OrderActiveDAL去替换掉它,那么这个时候我们OrderBLL的代码就必须做相应的改动,如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DaL;
using Mode;namespace Bll
{public class OrderBLL{protected OrderActive orderActive = new OrderActive();public bool Insert(OrderModel model){//业务点1//业务点2return orderActive.Insert();}/// <summary>/// 修改Order表数据/// </summary>/// <param name="model">表数据对应实体</param>/// <returns>成功:true,失败:false</returns>public bool Update(OrderModel model){//业务点1//业务点2return orderActive.Update(model);}/// <summary>/// 删除Order表指定ID的记录/// </summary>/// <param name="id">表ID</param>/// <returns>成功:true,失败:false</returns>public bool Delete(int id){//业务点1//业务点2return orderActive.Delete(id);}}
}
View Code

    这显然不是一种好的处理方式。我们追求的是在替换orderDal实体的同时,不修改OrderBLL的任何代码,换句话说就是解除BLL层与DAL层的耦合,为了达到这个目的,我们添加一个叫IDAL的接口类库,如图

      特别注意,我们在BLL层中添加了对IDA的引用,同时移除了对DAL的引用,这就意味着我们在BLL中无法创建(new)DAL层中的任何实体。下面我们在IDA项目中定义IOrder接口,如下

using Mode;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace IDal
{public interface IOrder{/// <summary>/// 向Order表插入数据/// </summary>/// <returns>成功:true,失败:false</returns>bool Insert();/// <summary>/// 修改Order表数据/// </summary>/// <param name="model">表数据对应实体</param>/// <returns>成功:true,失败:false</returns>bool Update(OrderModel model);/// <summary>/// 删除Order表指定ID的记录/// </summary>/// <param name="id">表ID</param>/// <returns>成功:true,失败:false</returns>bool Delete(int id);}
}
View Code

     我们让DAL引用IDAL,同时让OrderActiveDAL,OrderDal分别实现IOrder接口,如下

using IDal;
using Mode;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace DaL
{class OrderActiveDAL : IOrder{/// <summary>/// 向Order表插入数据/// </summary>/// <returns>成功:true,失败:false</returns>public bool Insert(){return true;}/// <summary>/// 修改Order表数据/// </summary>/// <param name="model">表数据对应实体</param>/// <returns>成功:true,失败:false</returns>public bool Update(OrderModel model){return true;}/// <summary>/// 删除Order表指定ID的记录/// </summary>/// <param name="id">表ID</param>/// <returns>成功:true,失败:false</returns>public bool Delete(int id){return true;}}
}
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mode;
using IDal;namespace DaL
{public class OrderDAL : IOrder{/// <summary>/// 向Order表插入数据/// </summary>/// <returns>成功:true,失败:false</returns>public bool Insert(){return true;}/// <summary>/// 修改Order表数据/// </summary>/// <param name="model">表数据对应实体</param>/// <returns>成功:true,失败:false</returns>public bool Update(OrderModel model){return true;}/// <summary>/// 删除Order表指定ID的记录/// </summary>/// <param name="id">表ID</param>/// <returns>成功:true,失败:false</returns>public bool Delete(int id){return true;}}
}
View Code

    现在我们队BLL层OrderBLL做相应的改动,把数据访问实体orderDAL的类型,定义为接口IOrder类型,如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DaL;
using Mode;
using IDal;namespace Bll
{public class OrderBLL{protected IOrder orderDal;public OrderBLL(){ //在这里需要实力化orderDal
        }public bool Insert(OrderModel model){//业务点1//业务点2return orderDal.Insert();}/// <summary>/// 修改Order表数据/// </summary>/// <param name="model">表数据对应实体</param>/// <returns>成功:true,失败:false</returns>public bool Update(OrderModel model){//业务点1//业务点2return orderDal.Update(model);}/// <summary>/// 删除Order表指定ID的记录/// </summary>/// <param name="id">表ID</param>/// <returns>成功:true,失败:false</returns>public bool Delete(int id){//业务点1//业务点2return orderDal.Delete(id);}}
}
View Code

   好了,现在让我们把目光聚集OrderBLL的构造函数。我们知道,OrderBLL会调用orderDAL去操作数据库,而orderDAL的类型为IOrder,也就是说但凡实现了IOrder接口的类实例都可以赋值给orderDAL。但是现在问题的关键是我们如何为orderDAL赋值,前面我们说过BLL移除了DAL的引用,所以在BLL层中直接去new数据访问层的实例,是不可能的。这里我提供两种处理方式,第一种采用IOC容器如spring.net帮助我们创建DAL层实例然后在OrderBLL的构造函数中赋值给orderDAL,另一种则是利用工厂模式和反射来创建DAL层实例了,本文将详述第二种,至于第一种在后面的系列中会有专门的章节讲述。现在我们在项目中添加一个Factoy工厂类库,并让BLL层引用Factoy类库,如图:

 

   接着我们来写工厂类。我们从配置文件中读出程序集路径和类的全名,利用反射的原理创建DAL层的实例,代码如下

using IDal;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;namespace Factory
{public class OrderDALFactory{private static readonly string AssemblyName = ConfigurationManager.AppSettings["Assembly"];private static readonly string className = ConfigurationManager.AppSettings["className"];public static IOrder CreateOrder(){return (IOrder)Assembly.Load(AssemblyName).CreateInstance(className);}}
}
View Code

 
  最后我们在OrderBLL的构造函数利用OrderDALFactory工厂给orderDAL赋值

 public OrderBLL(){ //在这里需要实力化orderDalorderDal=OrderDALFactory.CreateOrder();}

  这样我们就实现了BLL与DAL层的解耦,当我们BLL需要不同的IOrder实体的时候,我们只需要修改相应的配置文件即可。

总结  

  程序框架间层与层之间的解耦是富含挑战的一项工作,充分的体现出了面向对象的编程思想,巧妙的运用了各类设计模式。本文实现方法相对简单,仅当抛砖引玉。在接下来的文章中,我将就三层架构中各个层次的抽象与封装做详细说明。因为各个层次的抽象与封装是针对不同技术点来实现的,比如数据访问层,对EF技术与ADO.net技术的抽象与封装细节上就会有所不通。但总体思想是一致的,那就是我们必须为每个层次抽象出统一的接口,供上层引用,同时我们必须提供相应的注入方式,为调用层引用的接口实例赋值实例化。

 

转载于:https://www.cnblogs.com/shaoshun/p/3804474.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/575380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web结构组件

一、Web结构组件 &#xff11;、代理 位于客户端和服务器之间的HTTP实体&#xff0c;接收客户端的所有HTTP请求&#xff0c;并将这些请求转发给HTTP服务器。 &#xff12;、缓存 HTTP的仓库&#xff0c;使常用的页面的副本可以保存在离客户端更近地方 &#xff13;、网关 连接其…

javascript中parseFloat(x)、parseInt(num,radix)、Promise、ReferenceError、Reflect、get和set

parseFloat(x)对象&#xff1a; 将传入的字符转化为浮点数&#xff0c;若传入的字符不能被转化为数字型&#xff0c;则返回NaN <script>console.log(parseFloat(0.0314E2)); //3.14</script>parseInt(num,radix)对象&#xff1a; 将num看成radix某进制的数后转化…

Linux系统isosize指令用法

isosize命令&#xff1a;iso9660文件系统大小显示 isosize命令用于显示iso9660文件系统的大小&#xff0c;还文件可以使普通文件&#xff0c;也可以是块设备&#xff0c;如/dev/sr0或者/dev/sda.如果没有相关选项的规定&#xff0c;默认以字节的方式显示大小。该命令支持大于4G…

web api、获取DOM元素的方式、事件理解、click事件在移动端300ms延时、事件对象、事件委托、常见事件类型

web api: API&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&#xff09;是一些预先定义的函数&#xff0c;目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力&#xff0c;而又无需访问源码&#xff0c;无需理解其内部工作机…

Reset RequiredFieldValidator 重置 验证控件

<td style"width:100px;">姓名<span class"must_star">*</span></td> <td> <asp:TextBox ID"txtNAME" runat"server" MaxLength"60"></asp:TextBox> <asp:RequiredFi…

transitionend、change、classList、兼容代码、元素样式属性的操作、-Attribute自定义属性、阻止跳转、元素绑定相同事件、元素解绑事件、事件冒泡、事件三阶段

transitionend过渡监听事件&#xff1a; 过渡监听事件transitionend指的是CSS3中过渡效果执行一次后触发事件处理函数&#xff0c;如下案例&#xff1a; <!DOCTYPE html><html><head><meta charset"utf-8"><title></title><…

Unix/Linux环境C编程入门教程(18) kali-linuxCCPP开发环境搭建

1. Kali linux是BT5的晋级版本&#xff0c;用于信息安全。基于Debian7内核。新建虚拟机。2. 选择默认虚拟机3. 选择稍后安装操作系统4.选择Linux Debian7 64位&#xff0c;因为KaliLinux基于Linux Debian7。5. 设置虚拟机名称为KaliLinux6.设置处理器为双核。7. 设置内存为2G8.…

javascript中节点操作、节点属性、节点获取、创建节点、删除节点、克隆节点

节点操作 节点操作实际是利用DOM树把节点划分为不同的层次关系&#xff0c;常见父子兄弟级关系 节点属性&#xff1a; 节点一般有三个属性&#xff1a;nodeType节点类型&#xff08;其中元素节点值为1&#xff0c;属性节点值为2&#xff0c;文本节点值为3&#xff09;、node…

OpenGl绘制螺旋线

/** * 缓冲区工具类 */public class BufferUtil { /** * 将浮点数组转换成字节缓冲区 */ public static ByteBuffer arr2ByteBuffer(float[] arr){ ByteBuffer ibb ByteBuffer.allocateDirect(arr.length * 4); ibb.order(ByteOrder.nativeOrder()); FloatBuffer fbb ib…

javascript中BOM介绍、屏幕尺寸、历史记录、URL解析、计算机信息获取、定时器、三大系列及兼容代码、封装动画函数、同步和异步

BOM介绍&#xff1a; BOM指的是浏览器对象模型&#xff0c;是用来操作浏览器的&#xff0c;例如浏览器弹窗、地址栏、滚动条等&#xff0c;浏览器顶级对象&#xff1a;window&#xff1b;页面中的所有内容都是属于window的&#xff0c;window可以省略&#xff1b;confirm(‘带…

Mysql数据库优化技术之配置篇、索引篇 ( 必看 必看 转)

转自&#xff1a;Mysql数据库优化技术之配置篇、索引篇 &#xff08; 必看 必看 &#xff09;(一)减少数据库访问对于可以静态化的页面&#xff0c;尽可能静态化对一个动态页面中可以静态的局部&#xff0c;采用静态化部分数据可以生成XML&#xff0c;或者文本文件形式保存使用…

javascript中本地储存、离线缓存、地理定位、网络状态

本地储存&#xff1a; 实际开发中某些内容是不需要放到服务器中&#xff0c;而是放到了浏览器中&#xff0c;需要的时候可以快速的访问&#xff0c;甚至页面刷新也可能不会丢失数据&#xff0c;容量较大&#xff1b;这里介绍两种数据存储方式&#xff1a;sessionStorage约5M大…

Delphi 变体类型(Variant)的介绍(流与变体类型的相互转换、变体类型常用的函数)...

来源&#xff1a;http://blog.csdn.net/xiongmao000738/article/details/6863988 一、变体类型常用的函数介绍&#xff1a; Variant&#xff1a; 一种可以拥有各种数据类型&#xff1b; 也可以告诉目前存储的数据是什么类型(通过使用VarType函数)&#xff1b; 可以给相同的Var…

HTML 引用Css样式的四种方式

不才&#xff0c;只知道HTML引用CSS样式有四种方式&#xff0c;内部引用和外部引用各两种&#xff0c;因为老是忘记细节&#xff0c;记下了随时翻阅亦可方便如我般的初学者 内部引用方式1&#xff1a; 直接在标签内用 style 引用&#xff0c;如&#xff1a; <div class"…

javascript中实例对象和构造函数关系、原型语法、原型链、call实现继承、apply调用函数、bind拷贝函数、拷贝继承、class类、super、严格模式、高阶函数、闭包、递归、es6简介

实例对象和构造函数的关系及原型&#xff1a; 实例对象是通过构造函数创建出来的&#xff0c;实例对象的构造器constructor就是指向构造函数的&#xff1b;每个实例对象中的同一方法的指向是各不相同的&#xff0c;但是其属性值相同的属性可以相同&#xff0c;这样会造成内存浪…

phpstorm IDE编辑器使用手记

a.选项卡限制问题 突破打开的文件选项卡个数限制&#xff1a;默认安装后的phpstorm打开的文件个数是有限制的&#xff0c;默认是10个。也就是最多同时打开10个文件进行编辑。 超过10个就会自动关闭前面最先打开的文件。所以我们要修改设置&#xff1a;File > Setting 打开设…

移动web现状、viewport视口、二倍图、移动web开发主流方案、布局技术选型(流式布局、flex弹性布局、less+rem+媒体查询布局、混合布局、媒体查询、bootstrap)

移动端web现状&#xff1a; 移动端常见浏览器&#xff1a;UC浏览器&#xff0c;QQ浏览器&#xff0c;Opera浏览器&#xff0c;百度手机浏览器&#xff0c;360安全浏览器&#xff0c;谷歌浏览器&#xff0c;搜狗手机浏览器&#xff0c;猎豹浏览器及杂牌浏览器。移动端常见的浏览…

Centos 6.5(64bit)上安装Vertica single node

在Win8上使用虚拟机Virtualbox安装Centos6.5,想在上面安装vertica. 以下记录了我在安装的过程中遇到的问题与一些解决方案。 1.安装Centos的时候遇到了一个恼人的问题&#xff0c;即安装成功后第一次启动时会需要你配置一些用户信息&#xff0c;时区信息和Kdump信息。 我当时选…

less简介、less安装、编译、less语法之变量、嵌套、类混入、函数混入、运算、less文件导入

less基础&#xff1a; CSS是一门非程序语言&#xff0c;没有变量、函数、作用域等&#xff0c;此时使用rem单位就会出现图片等大量计算尺寸的问题&#xff0c;但是less可以轻松实现运算&#xff0c;它是CSS预处理语言&#xff0c;引入了变量、混入、函数等&#xff0c;常见的C…

Mysql数据库备份和按条件导出表数据

Mysql数据库备份和按条件导出表数据一、备份数据库# mysqldump -u root -p dbcurr>/home/20090219.sqlmysqldum为备份命令&#xff0c;-u用户&#xff0c;-p密码&#xff0c;dbcurr数据库名&#xff0c;>备份符&#xff0c;/home/20090219.sql存储数据文件的路径。www.2…