在网上收集的比较好的讲解三层架构的理解,我收集两部分,第一部分简单理解,第二部分比较详细。也有一些重复的地方
一、
C#三层架构登陆实例
很早之前,就听说过三层结构了。当时只知道 三层结构 是把 系统的 界面 跟 数据库操作等不相关的程序分别开来。原来这么简单的实现,确实传说中的 三层结构啊。
首先,先来看一下是哪三层。表示层(UI,User Interface),业务逻辑层(BLL BusinessLogicLayer),数据访问层(DAL Data Access Layer)。三层的划分是物理上的划分。
表示层(UI),这个最容易理解,就是用户看到的主界面。
数据访问层(DAL),也不难理解,主要是负责数据的增删改查。
业务逻辑层(BLL),算是表示层和数据访问层的桥梁吧。里面主要存放一些业务流程。也就是逻辑。主要作用就是从DAL中获取数据,然后显示到UI上。
举一个例子,三层结构可以用饭店的实例来理解。
UI指的是服务员,BLL是厨师,DAL是采购员。
在顾客的眼里,只能看到服务员为他们服务。并不知道后台厨师和采购员是如何做的。对于上述三种不同的角色来说,无论哪个环节出了问题,只需要更换一个员工就可以照常营业的。
三层架构的优势,还是职责分离,降低耦合。
接下来,看一个使用三层结构的登陆实例。首先,需要声明一下。这个实例中有很多bug需要优化。不过对于展示三层的主要思想足够了。仅仅是一个实例而已。
数据库表:
这是数据模块图:
细心的读者肯定会发现,除了UI,BLL,DAL这三个之外还有一个Moudel存在,这个Moudel不属于任何一层,只是为了更好地链接三层而存在的。这个类只存储,与以上三类共同使用的东西。起一个协调的作用。Moudel类,也就是实体类。
下面是这几个层次的关系。
接下来需要看一下,他们分别是如何实现各自的分工的。
Entity类:
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace Login.Model { public class UserInfo //实体类,用于保存用户信息 { public int ID { get; set; } public string UserName { get; set; } public string Password { get; set; } public string Email { get; set; } } } |
U层:
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;
namespace LoginUI { public partial class Form1 : Form { public Form1() { InitializeComponent(); }
private void label1_Click(object sender, EventArgs e) {
}
private void btnLogin_Click(object sender, EventArgs e) { try { string userName = txtUserName.Text.Trim(); //取出用户界面的数据 string password = txtPassword.Text; Login.BLL.LoginManager mgr = new Login.BLL.LoginManager(); Login.Model.UserInfo user = mgr.UserLogin(userName, password); //使用用户界面数据 进行查找
//如果没有问题,则登陆成功 MessageBox.Show("登陆用户:" + user.UserName); } catch (Exception ex) //如果登陆有异常 则登陆失败 { MessageBox.Show(ex.Message); } } } } |
B层:
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace Login.BLL //业务逻辑层 { public class LoginManager { public Login.Model.UserInfo UserLogin(string userName, string Password) { ///throw new NotImplementedException(); Login.DAL.UserDAO uDAO = new Login.DAL.UserDAO(); //创建一个user Login.Model.UserInfo user= uDAO.SelectUser(userName, Password); //通过ui中填写的内容 返回来相应的数据
if (user!= null) //如果数据库中没有数据,即为首次登陆了。增加10积分 { Login.DAL.ScoreDAO sDAO = new Login.DAL.ScoreDAO(); sDAO.UpdateScore(userName, 10); return user; } else //如果数据库中没有该用户名,则登陆失败 { throw new Exception("登陆失败"); } } } } |
D层:
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace Login.DAL //数据访问层 { class DbUtil //用于保存 链接服务器的sql语句 { public static string ConnString = @"Server=zc-pc;Database=Login;User ID=sa; Password=123456"; } } |
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data; using System.Data.SqlClient;
namespace Login.DAL { public class UserDAO { public Login.Model.UserInfo SelectUser(string userName, string Password) //根据 ui 选择返回一个user { using (SqlConnection conn = new SqlConnection(DbUtil.ConnString)) { //创建一个命令对象,并添加命令 SqlCommand cmd = conn.CreateCommand(); cmd.CommandText = @"SELECT ID,UserName,Password,Email FROM USERS WHERE UserName=@UserName AND Password=@Password"; cmd.CommandType = CommandType.Text; cmd.Parameters.Add(new SqlParameter("@userName", userName)); cmd.Parameters.Add(new SqlParameter("@Password", Password));
conn.Open(); //打开数据链接 SqlDataReader reader= cmd.ExecuteReader();
Login.Model.UserInfo user=null; //用于保存读取的数据
while (reader.Read()) //开始读取数据 { if (user==null) //如果没有,则重新生成一个 { user=new Login.Model.UserInfo(); } user.ID=reader.GetInt32(0); user.UserName=reader.GetString(1); user.Password=reader.GetString(2); if(!reader.IsDBNull(3)) //不要求一定要有email,也可以返回 { user.Email=reader.GetString(3); } } return user; }
} } } |
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data.SqlClient;
namespace Login.DAL { public class ScoreDAO //首次登陆,增加10积分 { public void UpdateScore(string userName, int value) { using (SqlConnection conn = new SqlConnection(DbUtil.ConnString)) { SqlCommand cmd = conn.CreateCommand(); //创建一个命令对象 cmd.CommandText = @"INSERT INTO SCORES(UserName,Score) Values(@UserName,@Score)"; //修改Score表数据 cmd.Parameters.Add(new SqlParameter("@userName", userName)); cmd.Parameters.Add(new SqlParameter("@Score", value));
conn.Open(); cmd.ExecuteNonQuery(); } } } } |
接下来,看一下执行结果:
执行成功的情况:
输入错误信息:
虽然这是一个很小的实例,但是用来学习三层却足够了。有写的不好的地方可以理解。
总结:对于使用三层架构的程序来说,哪层出错改哪里。极大程度的降低了系统的耦合性。当然,具有层次的程序,维护起来必然要方便许多。
-------------------------------------------------------------------------------------------------------------------------
二、
理解C#中得三层架构
- 表示层
- 业务逻辑层(BLL)
- 数据访问层(DAL)
关联:产生依赖关系
描述:三层体系结构就是在客户端和数据库之间加一个中间层,通常情况下客户端不能直接和数据库进行交互,而是通过中间层和数据层进行交互的。
【表示层】
主要负责完成用户和系统的交互。
它接收用户输入的信息,并把上级传来的处理结果表示给用户。盖层有时候兼容一小部分业务逻辑层的作用(验证用户输入的数据及其合法性)
一般作为Windows或者Web应用程序
【业务逻辑层】
接收表示层传来的数据,以参数的形式传给数据访问层;然后接收数据层发来的处理好的数据,传给表示层。
该层所做的工作比较复杂,如:实现业务流程、业务的数据校验
是表示层和数据层之间的桥梁,都需要通过业务逻辑层来完成信息数据的收发
【数据访问层】
数据访问层为系统提供了规范,统一数据的访问入口,保证业务逻辑层对系统数据访问层的规范和高效,从而实现数据被高效、安全的访问
该层会根据业务逻辑层的请求去处理和数据库之间的交互,不对数据做任何业务上的加工
【本人见解】
表示层接受用户的请求,根据用户的请求去通知业务逻辑层,业务逻辑层收到请求,首先对请求进行审核,然后将请求发给数据访问层或者直接返回到表示层,数据访问层收到请求后就开始访问数据库,数据访问层通过数据库的访问得到请求的结果,并把结果通知业务逻辑层,业务逻辑层收到请求结果将请求结果通知表示层,表示层收到请求结果,然后把结果显示给用户;
ps:用户登录系统就是这一应用的实例。
用户输入账号密码---->表示层先判断,符合----->业务逻辑层进行封装--------->数据访问层进行进一步安全、高效的判断------->结果给业务逻辑层------->表示层显示结果
这种传递是一步一步的进行下去,一步一步的回来,不能跨越传递。
在三层之间,各层之间是相互依赖的关系的,表示层依赖业务层逻辑,业务层依赖数据访问层,三层之间通过自身提供的对外方法来传递访问
三层之间的松耦合互不干扰,之间的联系是通过自己提供的接口来访问;
三层结构各层之间的的数据传递方法分为请求和响应两个方法;
项目依赖方向:先从表示层传递到业务层再到数据层;
数值返回:数据层返回给业务逻辑层,在由业务逻辑层返回给表示层,然后由表示层进行显示;
------------------------------------------------------------------------------------------
三层架构(我的理解及详细分析)
1什么是三层
三层架构已经学了一段时间,一直想做一个比较完整、比较完美的总结。但是左思右想,不知道如何下笔。都说万事开头难嘛,今天整理了一下凌乱的思路,哎,还是没整理好,想到哪就说到哪吧。
初学者很不理解:
1,什么是三层?
2,为什么使用三层?
3,三层与以往使用的两层相比有什么不同?它的优势在哪里?
4,如何学好三层?如何应用三层?
……
这篇博客里我会给大家一一解释一下,略懂皮毛忘大家见谅!!!
米老师一直强调:让学习和生活结合,把学习和生活联系,这样的学习才叫会学习,会生活。
对于三层我左思右想,如何与实际相联系。好嘛,昨晚突然有了“灵感”。还记得大话设计模式里第23章大鸟和小菜吃羊肉串的故事——由在小摊吃到饭店吃引来的一个命令模式(当然今天不是研究命令模式)。服务员、厨师、采购员。
这不就是个典型的三层架构吗???(⊙ o ⊙ )啊!哈哈(这个后面再做解释)
先了解:
1,什么是三层?
UI(表现层):主要是指与用户交互的界面。用于接收用户输入的数据和显示处理后用户需要的数据。
BLL:(业务逻辑层):UI层和DAL层之间的桥梁。实现业务逻辑。业务逻辑具体包含:验证、计算、业务规则等等。
DAL:(数据访问层):与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交给业务层,同时将业务层处理的数据保存到数据库。(当然这些操作都是基于UI层的。用户的需求反映给界面(UI),UI反映给BLL,BLL反映给DAL,DAL进行数据的操作,操作后再一一返回,直到将用户所需数据反馈给用户)
每一层都各负其责,那么该如何将三层联系起来呢?
1>单项引用(见下图)
2>这时候实体层(Entity)来了。(注:当然,实体层的作用不止这些)
Entity(实体层):它不属于三层中的任何一层,但是它是必不可少的一层。
Entity在三层架构中的作用:
1,实现面向对象思想中的"封装";
2,贯穿于三层,在三层之间传递数据;
(注:确切的说实体层贯穿于三层之间,来连接三层)
3,对于初学者来说,可以这样理解:每张数据表对应一个实体,即每个数据表中的字段对应实体中的属性(注:当然,事实上不是这样。为什么?1>,可能我们需要的实体在数据表对应的实体中并不存在;2>,我们完全可以将所有数据表中的所有字段都放在一个实体里)
4,每一层(UI—>BLL—>DAL)之间的数据传递(单向)是靠变量或实体作为参数来传递的,这样就构造了三层之间的联系,完成了功能的实现。
但是对于大量的数据来说,用变量做参数有些复杂,因为参数量太多,容易搞混。比如:我要把员工信息传递到下层,信息包括:员工号、姓名、年龄、性别、工资....用变量做参数的话,那么我们的方法中的参数就会很多,极有可能在使用时,将参数匹配搞混。这时候,如果用实体做参数,就会很方便,不用考虑参数匹配的问题,用到实体中哪个属性拿来直接用就可以,很方便。这样做也提高了效率。
(注:这里为什么说可以暂时理解为每个数据表对应一个实体??答:大家都知道,我们做系统的目的,是为用户提供服务,用户可不关心你的系统后台是怎么工作的,用户只关心软件是不是好用,界面是不是符合自己心意。用户在界面上轻松的增、删、改、查,那么数据库中也要有相应的增、删、改、查,而增删改查具体操作对象就是数据库中的数据,说白了就是表中的字段。所以,将每个数据表作为一个实体类,实体类封装的属性对应到表中的字段,这样的话,实体在贯穿于三层之间时,就可以实现增删改查数据了)
综上所述:三层及实体层之间的依赖关系:
思想来源于生活:
服务员:只管接待客人;
厨师:只管做客人点的菜;
采购员:只管按客人点菜的要求采购食材;
他们各负其职,服务员不用了解厨师如何做菜,不用了解采购员如何采购食材;厨师不用知道服务员接待了哪位客人,不用知道采购员如何采购食材;同样,采购员不用知道服务员接待了哪位客人,不用知道厨师如何做菜。
他们三者是如何联系的?
比如:厨师会做:炒茄子、炒鸡蛋、炒面——此时构建三个方法( cookEggplant()、cookEgg()、cookNoodle())
顾客直接和服务员打交道,顾客和服务员(UI层)说:我要一个炒茄子,而服务员不负责炒茄子,她就把请求往上递交,传递给厨师(BLL层),厨师需要茄子,就把请求往上递交,传递给采购员(DAL层),采购员从仓库里取来茄子传回给厨师,厨师响应cookEggplant()方法,做好炒茄子后,又传回给服务员,服务员把茄子呈现给顾客。
这样就完成了一个完整的操作。
在此过程中,茄子作为参数在三层中传递,如果顾客点炒鸡蛋,则鸡蛋作为参数(这是变量做参数)。如果,用户增加需求,我们还得在方法中添加参数,一个方法添加一个,一个方法设计到三层;何况实际中并不止设计到一个方法的更改。所以,为了解决这个问题,我们可以把茄子、鸡蛋、面条作为属性定义到顾客实体中,一旦顾客增加了炒鸡蛋需求,直接把鸡蛋属性拿出来用即可,不用再去考虑去每层的方法中添加参数了,更不用考虑参数的匹配问题。
这样讲,不知道大家是不是可以明白。(待会实例解释吧)
2,为什么使用三层?
使用三层架构的目的:解耦!!!
同样拿上面饭店的例子来讲:
(1)服务员(UI层)请假——另找服务员;厨师(BLL层)辞职——招聘另一个厨师;采购员(DAL)辞职——招聘另一个采购员;
(2)顾客反映:1>你们店服务态度不好——服务员的问题。开除服务员;
2>你们店菜里有虫子——厨师的问题。换厨师;
任何一层发生变化都不会影响到另外一层!!!
3,与两层的区别??
两层:
(当任何一个地方发生变化时,都需要重新开发整个系统。“多层”放在一层,分工不明确耦合度高——难以适应需求变化,可维护性低、可扩展性低)
三层:
(发生在哪一层的变化,只需更改该层,不需要更改整个系统。层次清晰,分工明确,每层之间耦合度低——提高了效率,适应需求变化,可维护性高,可扩展性高)
综上:三层架构的
优势:1,结构清晰、耦合度低,2,可维护性高,可扩展性高;3,利于开发任务同步进行;容易适应需求变化
劣势:1、降低了系统的性能。这是不言而喻的。如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成。
2、有时会导致级联的修改。这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码
3、增加了代码量,增加了工作量
4,三层的具体表现形式??
UI:
(大家不要误会,UI层不只是一个个用户界面,也是需要有代码的)
(1,功能:用户输入数据、反馈给用户数据;2,大家观察代码:没有涉及到业务逻辑,直接传参、函数、方法调用,没有涉及到与数据库打交道的SQL语句和ADO.NET)
BLL:
(1,BLL是表示层与数据访问层之间的桥梁,负责数据处理、传递;2,大家观察代码,没有涉及到界面上的控件,没有涉及到SQL语句和ADO.Net)
DAL:
(1,以上是DAL层中DbUtil类、user_DA类和workRecord_DA类中的代码;2,大家观察代码,没有涉及到界面控件,没有涉及到业务逻辑;只有与数据库打交道的SQL语句和ADO.net)
Entity(Model)层:
(定义了实体类user)
观察以上三层:
1,实体类user作为参数贯穿于三层之间;
2,通过传参、方法调用来实现功能;
3,各层之间各负其责;互不影响
对比两层结构,让大家深刻体会三层的极大好处:
还是以机房收费系统的登陆为例:
(观察上面的两层的代码:将业务逻辑、数据访问都展现在用户表现层,当需求需要改变时,需要改变整个系统。比如,我把文本框txtPassWord的名称改为txtPwd的话,大家观察一下得需要更改多少地方。这样的改动算是小的,如果真的有业务需求上的改动才叫麻烦复杂,程序员不跳楼才怪。呵呵、、开个玩笑)
与如此难以适应需求变化的两层相比,大家再次观察三层代码,再次思考,三层架构有什么好处呢?自己思考。。。。。
自己去发掘吧!!!