C#从零开始学习笔记---第十八天
大家好啊!也是很久没有更新了,不过先声明一下,不是主播懒,主要是主播在学习完分层结构之后写了一个案例来巩固,感觉还行,能接受,然后还有一些深入的知识点嘛,准备在学习完winform之后在顺道学习了,然后就开启了winform的学习,刚开始还行,也就是拖拖控件,认识认识控件和相应的属性,但是后来随着学习的深入,控件好多,属性也好多,记不过来了,也正因如此,主播一直都在巩固。刚好这要赶上端午假期了,主播就来还还帐。还望谅解。
ok,今天我们来学习C#基础的最后一部分,分层结构。这边我会带着大家梳理一下三层分层架构的核心改建,层级职责和引用规则。顺便带大家从零开始搭建一个可运行的三层小项目。话不多说,我们开始吧!
1.三层结构是那三层
可能大家在了解一些三层分层结构的东西之后会思考,一共不是四层吗?不是还有一个Model层吗?这里先跟大家说一下,Model不是一层,他是公共数据载体,所有层都能调用,它不属于三层里的任何一层。标准的三层结构从上到下分别是UI层(界面),BLL层(业务逻辑层),DAL层(数据访问层)。我们来一次认识一下他们。
1.1 UI层(界面)
UI层就是我们的人机交互界面,负责和用户进行交互,他的本职工作就是采集用户输入,传给下一层;拿到处理结果后,通过界面展示给用户。同时我们要注意,绝对不能在UI层里写数据存储的逻辑,写业务校验的逻辑或者直接操作数据库。
1.2BLL层(业务逻辑层)
BLL层相当于整个系统的大脑,他来管理所有的业务规则和判断逻辑。他的本职工作就是进行数据合法性校验、重复数据判断、业务规则约束、事务包装、状态流转控制。同样,在BLL层里我们也不能写数据存储的逻辑,也不能操作界面控件或者直接和数据库交互。
1.3 DAL层(数据访问层)
DAL层是专门和数据库打交道的层,他只做纯数据的读写。他的本职工作就来写数据的存储逻辑,进行数据的增删改查操作,返回最后的数据结果。
1.4 Model层(实体层)
Model层就是我们正常定义的类,属于纯数据容器,类只有属性,额米有任何业务逻辑。他的核心作用是让所有层之间传递数据都用它,相当于大家统一使用的档案袋,不用零散传递一堆参数。
我用我们生活中的例子来类比一下,相信大家会有一个更深的认识。我们把整个项目当成一个公司,把每个层当成公司的各个部门,那么UI层就相当于公司前台,他只负责接待客户,收资料、递结果,不审核业务。BLL层就相当于业务经理,他只负责审核资料、判断规则、安排工作,不碰底层数据存储。DAL层就相当于档案管理员,他只负责存档案、取档案,不管档案内容合不合规。而Model层就相当于档案袋,大家传递信息都用它,统一格式,高效规范。
2.为什么要分层
可能我们会觉得,一个小功能三行代码就写完了,拆成三层反而多写好多代码。短期看确实是这样,但只要项目要迭代、功能要增加,不分层的代码会迅速变成维护灾难。
不分层的话我们会遇到以下问题:
- 代码完全没法复用:新增用户的校验逻辑写在新增窗体里,后续做批量导入用户时,同样的校验要再抄一遍。改一个规则,要找遍所有窗体修改,非常容易遗漏。
- 改一处崩一片:换个数据库、改个表结构,所有窗体里的 SQL 都要改,一不小心就改出 bug。
- 根本没法维护:一个功能要写几百行,既有界面操作、又有业务判断、还有 SQL,出问题要在一堆代码里慢慢扒。
- 没法团队协作:写界面的和写数据库的都改同一个文件,天天代码冲突。
而分层的本质说白了就是用一层调用来换取长期可维护性。分层的核心思想是职责单一,隔离变化。也就是我们改UI不会影响业务逻辑和数据存储;改BLL层又不用动界面和数据;我们想换一组数据只需要对DAL进行修改,其他的层完全不用动。现在我们都写一些小项目,所以看着麻烦,而当项目越大、迭代越多,分层的收益就越明显。
3. IDAL接口层
我们之前学过接口这个概念,很明显,IDAL就是DAL层的一个接口,用来规范我们的DAL层要实现的功能。那么可能大家会有疑惑:DAL 已经能干活了,再多一层 IDAL 接口,不是多此一举?IDAL 有两个核心作用,一个是「省代码」,一个是「解耦合」,后者才是架构的核心价值。
3.1 作用一:抽取共性,减少代码重复
所有实体都有增、删、改、按 ID 查询、查列表这些通用操作,每个实体的接口都写一遍,全是重复代码。所以我们可以抽一个泛型基接口 IBase<T>,把通用 CRUD(增删改查) 都放进去:
public interface IBase<T>
{
bool Add(T model);
bool Update(T model);
bool Delete(int id);
T GetById(int id);
List<T> GetList();
}
然后每个业务接口继承基接口就好:
public interface IUser : IBase<User>
{
// 只需要写 User 独有的方法,通用 CRUD 不用重复写
User GetUser(string userName, string password);
}
对应的 DAL 层也可以写 BaseDAL<T> 基类,实现通用的 CRUD 逻辑,每个实体的 DAL 只需要写自己独有的查询方法。这就是大家常说的「把共性抽出来」,核心目的是少写重复代码。
3.2 作用二:隔离实现,让BLL和DAL彻底解耦
这个作用是IDAL层的核心价值。说白了,IDAL就是夹在DAL和BLL中间的隔离墙。我们来对比一下这两种写法的区别:
3.2.1 BLL直接依赖具体的DAL类
public class UserBLL
{
// 变量类型就是具体的 SqlServer 实现类
private readonly UserDAL _userDal = new UserDAL();
}
这样的话当我们以后要是换一个数据源,比如以后项目要从 SqlServer 换成 MySql,你就得写一个 MySqlUserDAL,然后去每一个 BLL 类里改变量类型、改 new 的那一行。改漏一个就崩,BLL 和 DAL 彻底绑死了。
3.2.2 BLL只依赖接口,不依赖具体实现
public class UserBLL
{
// 左边:声明接口类型(只认契约)
// 右边:实例化具体实现类
private readonly IUserDAL _userDal = new UserDAL();
}
这里的核心就一句话:声明用接口类型,实例化用具体实现类。
也就是说整个 BLL 里,只有赋值这一行依赖了具体的 DAL 类,其余所有业务代码,都只调用接口里定义的方法,完全不关心底层是谁实现的。以后换数据库,只需要写一个新的 DAL 类实现 IUserDAL 接口,然后把 new UserDAL() 改成 new MySqlUserDAL() 就完事了。BLL 的业务代码一行都不用动。这就是经典的依赖倒置原则:高层模块不依赖低层模块,二者都依赖抽象。接口就是抽象,DAL 类就是具体细节。
4.核心规则
在写分层结构的时候我们要牢记两个规则:第一是依赖方向永远向下,即只能上层调用下层,绝对不能反向依赖。第二是每一层只能依赖直接下一层,绝对不能跨层调用。
4.1每层的依赖清单
| 层级 | 可以依赖谁 | 绝对不能依赖 |
|---|---|---|
| UI 层 | BLL 层、Model 层 | DAL 层、IDAL 层(跨层违规) |
| BLL 层 | IDAL 层、Model 层 | UI 层、具体 DAL 实现类 |
| DAL 层 | IDAL 层、Model 层 | BLL 层、UI 层 |
| Model 层 | 谁都不依赖 | 所有上层 |
4.2 为什么要声明成类的私有字段
我们看到的规范写法,都是在类顶部声明private readonly 字段,而不是每个方法里都 new 一次:
public class UserBLL
{
private readonly IUserDAL _userDal = new UserDAL();
}
这里的原因有三个:
- 复用:类里多个方法都要用,声明成字段不用每个方法都 new 一次,减少对象创建开销
- 好维护:以后要换实现类,只改这一行就行,不用每个方法都改
- 安全:加
readonly保证它只能被赋值一次,不会被意外修改
4.3 常见的争议:BLL层能不能省略
很多小项目里会省略 BLL,让 UI 直接调用 DAL,这本质是工程妥协,不是规范设计。在我们正常的定义里哪怕只有简单的 CRUD,也会有数据校验、重复判断这些业务逻辑,这些逻辑写在 UI 里,就是职责越界。后期功能一多,界面代码会越来越臃肿,最后变成没人敢动的屎山代码。
5.将某些函数的参数封装成类
这里我们就举增删改查里的查作为例子。我们都知道这里在没有要求的情况下我们大部分时候都做模糊查询,这时候我们也不能传整个对象,而是要传一条条数据,这时候如果我们查询的条件变多,那么我们要传的参数就会变多,写起来很麻烦是一方面,另一方面是万一我们传参的时候参数的顺序写错了,那就完蛋了。这种要传很多参数的情况就说明这组参数不是一个独立的业务整体。那什么是独立的业务整体呢?像我们增删改这种直接传一个对象,或者直传一个数据的这种就是。
那我们将函数的参数封装成类的核心判断标准就是:这组参数是不是一个独立的业务整体,以及会不会持续扩展。
6.三层架构的正确开发顺序
一句话:自底向上,那功能逐个打通。不要一上来把整层所有方法全写完,再写下一层;而是做一个功能,就把这个功能从底到顶全链路写通、调通,再写下一个功能。说白了就是Model → IDAL → DAL → BLL → UI。
总而言之,三层架构不是什么高深的技术,他的本质就是职责拆分的编程思想:Model 负责装数据;DAL 负责读写数据;BLL 负责管业务规则;UI 负责和用户交互;IDAL 负责解耦,让业务和数据存储彻底分开。
不管怎么样,学习阶段不要怕麻烦,哪怕是小项目也按规范写,养成好习惯。等你写多了就会发现,分层带来的秩序感,会让你写代码的时候思路越来越清晰,后期维护也会越来越省心。
今天的分析就到这里了,确实很久没写了,今天也算是对C#基础语法阶段做了一个收尾。Winform部分其实主播也已经学完了,后续也会陆陆续续写Winform的博客,总之这段时间谢谢大家的点赞和收藏,也谢谢你能看到这里。其实真正学下来才会发现,东西确实蛮多的,学的也都只是皮毛,不过没关系,只要我们努力,就一定会更好,那么,C#基础就完结撒花了。希望有机会的话,我们能在Winform区再次相见!!!
拜拜~
更多推荐
所有评论(0)