(一)什么是架构
首先,我们从架构层面大致可以分为:系统架构和应用架构。
所谓的系统架构,也是我们通常所说的网络架构;而应用架构也就是我们所说的代码架构。
(二)为什么需要架构
以前系统简单,一个应用部署在一台服务器上,且大部分开发工作是CRUD,应用结构简单,
且易于维护。随着系统业务复杂度越来越高,功能模块越来越庞大,耦合度也越来越高,导致系统的复杂度越来越不可控。
为了更好的降低应用及模块间的耦合度,于是,诞生了一些我们熟知的网络架构(分布式微服务)
和应用架构(三层架构、MVC架构,在此架构上又出现了一些框架,如SSM框架、
SSH框架等,使用框架的好处就是结构清晰易于维护)。
(三)什么是三层架构
三层架构就是为了符合“高内聚,低耦合”思想,把各个功能模块划分为
表示层(UI)、业务逻辑层(BLL)和数据访问层(DAL)三层架构。
(图3.1)
表示层(UI):位于三层构架的最上层,与用户直接接触,表示层就是实现用户的界面功能,
也是系统数据的输入与输出,是为用户传达和反馈信息的。
业务逻辑层(BLL):是针对具体的问题的操作,也可以理解成对数据层的操作,
对数据业务逻辑处理。同时也是表示层与数据层的桥梁,实现三层之间的数据连接和指令传达。
数据访问层(DAL):有时候也称为是持久层,主要功能是对原始数据(数据库或者文本文件等存放数据的形式)的操作
(实现数据的增加、删除、修改、查询等)。具体为业务逻辑层或表示层提供数据服务。
(四)三层架构设计思想
在三层架构程序设计中,采用面向接口编程。各层之间采用接口相互访问,
并通过对象模型的实体类(Model)作为数据传递的载体。
层是一种弱耦合结构,层与层之间的依赖是向下的,
上层对下层的调用,是通过接口实现的,而真正提供服务的是下层的接口实现类。
服务标准接口是相同的,而实现类是可以替换的,这样就实现了层与层间的解耦。
(五)以下是一个三层架构在生活中的例子:
(1)假设有一家餐厅,其运作可以类比为三层架构的应用程序。
表示层(UI):餐厅的服务员。他们直接与顾客交流,接受顾客的点餐请求,并确保顾客得到满意的服务。
在这个例子中,服务员相当于表示层,负责与顾客的交互。
业务逻辑层(BLL):厨师。他们根据服务员提供的菜单(相当于顾客的请求)准备食物。
厨师需要确保食物的口味和质量满足顾客的要求。在这个例子中,厨师相当于业务逻辑层,负责处理业务逻辑。
数据访问层(DAL):采购员。他们根据厨师的需求采购食材,确保食材的新鲜和质量。
在这个例子中,采购员相当于数据访问层,负责与食材的交互。
这个例子展示了三层架构在生活中的应用。每个层次都有明确的职责,并且各层之间的耦合度很低。
如果某个层次出现问题,例如服务员的服务质量差,只需要更换服务员即可,对其他层次没有影响。
这正是三层架构的优势所在,能够提高应用程序的可维护性和可扩展性。
(2)假设我们要开发一个电子商务网站,该网站允许用户浏览商品、添加到购物车、下订单和支付。
我们可以使用三层架构来设计和实现这个系统。
表示层(Presentation Layer):
这一层负责与用户进行交互,展示网站的界面。
例如,用户可以通过网站的前端界面浏览商品、搜索商品、查看商品详情等。
表示层可以使用前端技术如HTML、CSS和JavaScript来实现。
业务逻辑层(Business Logic Layer):
这一层处理与业务相关的逻辑和功能。
在电子商务网站的例子中,业务逻辑层负责处理用户的购物车操作、订单生成、支付处理、库存管理等。
当用户在表示层执行某些操作时,例如将商品添加到购物车或提交订单,这些请求将被传递到业务逻辑层进行处理。
业务逻辑层可以使用后端编程语言(如Java、Python)和框架(如Spring、Django)来实现。
数据访问层(Data Access Layer):
这一层负责与数据库进行交互,存储和检索数据。
在电子商务网站的例子中,数据访问层负责存储商品信息、用户信息、订单信息等。
当业务逻辑层需要访问或修改数据时,它会与数据访问层进行通信。
数据访问层可以使用数据库管理系统(如MySQL、PostgreSQL)和相关的数据访问技术(如JDBC、ORM)来实现。
通过将系统划分为这三个层次,我们可以实现代码的清晰分离和模块化,
使得开发人员可以更加专注于自己的领域,同时也方便了系统的维护和扩展。
(六)用三层架构实现餐厅运营
网上凡是讲三层架构的文章,一般都是以数据库应用软件为例子。
以至于有些作者甚至说到“如果不用数据库应用来讲,那就根本不会选择使用三层架构”。
然而三层架构三层架构的目的是整个业务应用的分层设计,从来没有说过这种架构是数据库应用专用的。
网上的大部分讲数据库应用使用三层架构的文章,看上去仅仅是新建了三个目录 UI/BLL/DAL,然后把代码拆分放到这三个目录下而已。
它们根本没谈到下面这些根本性问题:
(1)BLL和DAL设计时怎么使用接口?
(2)UI、BLL、DAL之间的通讯是什么方式?
(3)UI、BLL、DAL之间到底是什么关系,它们之间谁和谁是松耦合的?
(4)UI、BLL、DAL它们谁是可以复用和更替的?
(6.1)UI、BLL、DAL之间的通讯是什么方式?
接口定义:各层之间通过定义接口来明确通信的契约。
接口定义了各层之间可以调用的方法、传递的参数以及返回的结果。
依赖注入:依赖注入是一种实现层间解耦的技术,它允许将一个层的依赖项(如服务、数据访问对象等)
注入到另一个层中。这样,各层之间可以通过依赖注入的方式实现通信,而不需要直接依赖于具体的实现类。
数据传输对象(DTO):DTO是一种用于在各层之间传输数据的对象。
它通常用于将数据从DAL层传输到BLL层,然后从BLL层传输到UI层。
DTO可以确保各层之间传输的数据格式一致,并且可以减少数据传输过程中的错误。
事件和消息传递:在某些情况下,各层之间可能需要通过事件或消息传递机制进行通信。
例如,当DAL层中的数据发生变化时,它可以通过触发事件来通知BLL层和UI层。
同样,BLL层和UI层也可以通过消息传递机制来请求或响应数据变化。
回调函数:回调函数是一种允许上层调用下层提供的函数的技术。
在三层架构中,BLL层可以定义回调函数,供UI层在需要时调用。
这样,UI层可以通过回调函数与BLL层进行通信,实现数据的请求和响应。
(6.2)UI、BLL、DAL之间到底是什么关系,它们之间谁和谁是松耦合的?UI、BLL、DAL它们谁是可以复用和更替的?
在三层架构中,UI、BLL和DAL之间的关系可以理解为层次递进(上下关系,UI是上层,依赖底层的BLL和DAL)、各司其职的关系。
UI层直接与用户交互,负责接收用户输入和显示处理后的数据;
BLL层是UI层与DAL层之间的桥梁,实现业务逻辑,包括验证、计算、业务规则等;
DAL层则负责与数据库交互,实现对数据的增、删、改、查等操作。
在三层架构中,各层之间的耦合度较低,彼此独立,互不影响。
这意味着UI层、BLL层和DAL层都可以独立地被复用和更替,而不会对其他层造成太大的影响。
例如,UI层可以采用不同的界面技术或框架来实现,BLL层可以根据业务需求选择不同的业务逻辑实现方式,
DAL层可以根据数据存储的需求选择不同的数据库访问技术或中间件。
在耦合度方面,BLL层和DAL层之间通常是紧密耦合的,因为BLL层需要依赖于DAL层提供的接口来访问和操作数据。
而UI层与BLL层之间的耦合度相对较低,因为UI层通常只需要关心业务逻辑的结果,而不关心具体的实现细节。
(6.3) 下面勇哥谈一下几个层之间通讯的方式,并给出演示代码:
(1) 数据传输对象(DTO)
在三层架构中,DTO(Data Transfer Object)是一种设计模式,主要用于在不同的层次之间传输数据。
DTO通常用于从数据库或其他数据源获取数据,并将其转换为业务逻辑层或表示层需要的格式。
DTO充当了数据传输的桥梁,使各层之间的数据交换更加灵活和可维护。
DTO的主要特点是它是一个简单的数据容器,只包含数据字段和对应的getter和setter方法,
而不包含任何业务逻辑。DTO通常是无状态的,即它不维护任何内部状态,只是简单地传递数据。
以下是一个简单的C#例子,展示了如何使用DTO在三层架构中传输数据:
1. 数据访问层(DAL):
public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } }
2. 业务逻辑层(BLL):
public class ProductService { public Product GetProductById(int id) { // 从数据库获取数据并转换为DTO var productDTO = new ProductDTO() { Id = id, Name = "Sample Product", Price = 99.99M }; return productDTO; } }
3. 表示层(UI):
在表示层,我们使用DTO接收从业务逻辑层传递过来的数据,并进行展示。
这里我们使用一个简单的WinForms示例:
public partial class Form1 : Form { private ProductService productService; private ProductDTO productDTO; public Form1() { InitializeComponent(); productService = new ProductService(); } private void Form1_Load(object sender, EventArgs e) { productDTO = productService.GetProductById(1); label1.Text = productDTO.Name; // 展示产品名称 label2.Text = productDTO.Price.ToString("C"); // 展示产品价格(货币格式) } }
ProductDTO类(表示层与业务逻辑层之间的DTO):
public class ProductDTO { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } }
(2)回调函数
下面是BLL调UI层的定义方法
首先,定义一个委托类型,该委托将包含回调函数的签名:
// 定义委托类型,包含回调函数的签名
public delegate void CallbackDelegate();
接着,在BLL层中定义一个类,该类将包含一个使用上述委托作为参数的方法:
// BLL层类
public class BLLClass
{
// 定义一个方法,接受委托作为参数
public void DoSomething(CallbackDelegate callback)
{
// 执行某些操作...
Console.WriteLine("BLL层正在执行操作...");
// 调用回调函数
callback();
}
}
然后,在UI层中,你可以实例化BLL层的类并传递一个实现了CallbackDelegate委托的函数作为回调:
// UI层代码示例
class Program
{
static void Main(string[] args)
{
// 实例化BLL层的类
var bllClass = new BLLClass();
// 定义一个实现了CallbackDelegate委托的函数
var callback = new CallbackDelegate(OnCallback);
// 调用BLL层的方法并传递回调函数
bllClass.DoSomething(callback);
}
// 实现了CallbackDelegate委托的回调函数
private static void OnCallback()
{
Console.WriteLine("UI层回调函数被调用!");
}
}
在这个例子中,当BLLClass的DoSomething方法执行到callback()时,它会调用在UI层中定义的OnCallback方法。
这样就实现了BLL层与UI层之间的回调通信。
(6.4) 以餐厅为例,谈下三种角色的关系、以及通讯设计
服务员(表示层UI)、厨师(业务逻辑层BLL)、采购员(数据访问层DAL)
服务员和厨师的关系: 耦合度极低。服务员跟厨师几乎没什么关系,服务员只用把顾客的点菜条子交给厨师
(甚至这个点菜条子也是通过一个窗口递进去的,连厨师的照面都不打),
剩下的就是只关心出菜和端盘(厨师出菜的时候按下铃,服务员就过来端,两者还是照面都不打)
厨师和采购员的关系:耦合度高。厨师根据采购员买到的食材的质量、新鲜程度、价值这些数据来决定今天推出的菜品。
服务员和采购员的关系:从耦合度上来讲,两者没有直接关系。
但是从餐厅运营的结果(让顾客满意,最大化利润)上来看,厨师是这两者之间的桥梁。
从复用和可替换方面来谈:
服务员: 可替换性高,可以根据餐厅经营情况,随便增加删除或者替换
服务员就是脸面(UI),可以考虑引入高颜值高技能的(如会跳科目3舞蹈的)
服务员属于经常更换的岗位。
厨师:可以被替换,但是要慎重替换,因为厨师一换,餐厅菜品的口味就变了。
但是从餐厅的运营上来看,只要有厨师这个岗位就可以正常运营,更换厨师是没问题的。
采购员:可以被替换,甚至厨师可以只用认识“采购员”这个职位,而不用认识具体是哪个人。
老板根据采购员的业务能力和贪污腐败程度决定是否要更换采购员。(即一般不会轻易更换)
从层通讯方面来讨论一下:
服务员要知道的一些信息有:
1。 实时的菜品名单
因为按食材消耗量,菜单上有些菜可能就要下架,这个信息是随着时间浮动的。
这个信息由厨师提供。通讯方式可以考虑回调函数
2。 食材新鲜度、价值等信息
这个信息由采购员提供;这个信息是由最底层的采购员向上传递的。通信方式可以考虑DTO
3。 特色菜品信息
这个信息由厨师提供。通信方式可以考虑DTO或者回调函数
4。 炒好的菜品
这个由厨师提供,通信方式可以考虑DTO或者回调函数
5。 顾客的反馈信息,例如饭菜质量、味道怎么样、价格是不是贵了
由顾客提供
6。 顾客的点菜名单
由顾客提供
厨师要知道的信息有:
1。 食材新鲜度、价值、数量等信息
由采购员提供
2。 点菜条
由服务员提供
采购员要知道的信息:
1。 当天要采购的内容
由老板决定,老板根据是否节假日、是否周未、平时客流量的推测来决定菜品,并且预估出采购清单给采购员
另外从层关系上来看,服务员是上层,他需要依赖底层的厨师和采购员。
从最前面的图3.1就可以看出这种上下关系。
从可替换性角度来看:
服务员、厨师、采购员都可能有多个实例,所有角色的增删在餐厅管理中都是浮动的,因此他们可以考虑用依赖注入进行动态维护。
(6.5) 餐厅运营的实现代码
待续。。。
(七)三层架构适不适合用来把界面与代码进行分离?
三层架构的主要目的是将业务逻辑、数据访问和界面展示分层,以提高代码的可维护性、可扩展性和可重用性。
在实现界面与逻辑代码的分离方面,三层架构并不是最佳选择。
在三层架构中,UI层通常包含界面逻辑和部分业务逻辑,而BLL层则集中了主要的业务逻辑处理。
这种分层结构使得界面与逻辑代码并没有得到很好的分离。
为了更好地实现界面与逻辑代码的分离,可以考虑使用其他架构模式,如MVC或MVVM。
MVC模式将应用程序分为三个组件:模型、视图和控制器。模型负责处理业务逻辑和数据,
视图负责展示用户界面,而控制器则处理用户交互。
这种模式将界面与逻辑代码完全分离,使得代码结构更加清晰,易于维护和扩展。
对于WinForms应用程序,使用MVVM模式也可以实现界面与逻辑代码的分离。
MVVM模式通过定义ViewModel层来实现这一目标,ViewModel包含UI相关的数据和命令,
并且可以将用户交互转换为数据绑定和命令触发。
总结来说,三层架构不是用来直接实现界面与逻辑代码分离的最佳选择。
为了更好地分离界面和逻辑代码,可以考虑使用MVC或MVVM等其他架构模式。
(八)winform程序如果想界面与逻辑代码分离,一般是推荐什么架构?
对于WinForms程序,如果希望实现界面与逻辑代码的分离,可以考虑使用以下架构:
Model-View-Presenter (MVP):MVP是一种设计模式,它通过将UI的表示逻辑与业务逻辑分离来改进软件的架构。
在这种模式中,Model代表应用程序的数据和业务规则,View代表用户界面,
而Presenter则充当View和Model之间的中介,处理用户交互并确保UI与业务逻辑的分离。
Model-View-ViewModel (MVVM):MVVM是另一种流行的架构模式,适用于WPF应用程序,
但它也可以用于WinForms应用程序。MVVM通过定义ViewModel层来实现界面与逻辑的分离。
ViewModel包含UI相关的数据和命令,并且可以将用户交互转换为数据绑定和命令触发。
在选择架构时,需要考虑应用程序的规模、复杂性和开发团队的经验。对于较小的应用程序或原型,
简单的MVC架构可能就足够了。对于更复杂的应用程序,
建议使用MVVM或MVP架构来更好地分离界面和逻辑,并提高代码的可维护性和可扩展性。
(九)三层架构不适应的情况有哪些?
小型或中等规模的应用程序:在这些应用程序中,强制使用三层架构开发可能会花费大量时间,
并且无法体现其优势,同时可能使开发变得繁琐。
简单的界面:对于简单的界面,严格遵循三层架构,使模型、视图与控制器分离,
可能会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。
项目对性能要求非常高:如果一个项目对性能要求非常高,
那么使用三层架构可能会因为层级之间的交互而带来额外的开销。
项目对实时性要求非常高:如果一个项目对实时性要求非常高,例如在线游戏,
使用三层架构可能会因为层级之间的交互而带来延迟。
总的来说,这些不适应的情况主要是因为三层架构的复杂性和开发成本在某些情况下可能超过了其带来的好处。
(十)三层架构如果用于界面与业务逻辑分离,主要面临哪些问题或者挑战?
在三层架构中,将界面与业务逻辑分离主要面临以下问题:
通信开销:由于界面层和业务逻辑层通常分布在不同的位置(例如前端和后端),它们之间的通信可能带来一定的开销。
数据需要在两个层之间进行传输,这可能导致性能下降。
复杂性增加:分离界面与业务逻辑增加了系统的复杂性。需要确保各层之间的协调和通信,增加了开发和维护的成本。
界面与业务逻辑耦合:尽管将界面与业务逻辑分离,但它们之间仍然存在一定的耦合关系。
界面可能需要根据业务逻辑的改变而更新,反之亦然。这种耦合可能导致系统的灵活性降低。
数据一致性问题:在分离的架构中,确保数据在各层之间的一致性是一项挑战。
需要建立适当的数据同步机制,以避免数据不一致的情况发生。
跨层调用:当需要在不同层之间进行调用时,可能会出现层级之间的依赖关系。
过度的跨层调用可能导致系统的稳定性降低。
测试和维护的挑战:分离界面和业务逻辑可能使得测试和维护工作变得更加复杂。需要针对不同的层进行测试,
并确保它们之间的协调和集成。
(十一)MVC, MVP这些架构被推荐用于界面与业务逻辑分离,
那它们解决全部三层架构在应用界面与业务逻辑分离时遇到的问题了吗?
MVC(Model-View-Controller)和MVP(Model-View-Presenter)确实被推荐用来实现界面与业务逻辑的分离,
它们能够在一定程度上克服三层架构用于界面与业务逻辑分离时面临的某些问题。
首先,MVC和MVP模式都强调将界面与业务逻辑进行明确的分离。
通过分离显示层与逻辑层,它们可以降低界面与业务逻辑之间的耦合度。
这使得界面可以独立于业务逻辑进行开发和修改,提高了系统的灵活性和可维护性。
其次,MVC和MVP模式引入了中间层(Controller或Presenter),负责处理用户交互和界面更新。
这个中间层可以协调界面与业务逻辑之间的通信,
减少了直接的跨层调用和数据一致性问题。通过接口进行通信,中间层可以解耦界面和业务逻辑,
使得它们可以独立变化和测试。
另外,MVC和MVP模式也提供了更好的可扩展性和可测试性。由于界面和业务逻辑的分离,
可以针对各个层次进行独立的单元测试,提高了代码的质量。
同时,它们支持模块化的开发方式,使得系统可以更容易地扩展和修改。
然而,MVC和MVP模式并不能完全解决三层架构用于界面与业务逻辑分离时面临的所有问题。
例如,通信开销仍然存在,因为界面层和业务逻辑层之间的数据传输是不可避免的。此外,
虽然MVC和MVP模式降低了界面与业务逻辑之间的耦合度,
但仍然需要一定的协调工作来确保数据的一致性和系统的稳定性。
因此,虽然MVC和MVP模式在界面与业务逻辑分离方面提供了更好的解决方案,
但仍然需要根据具体的应用场景和需求来选择适合的架构模式,并进行合理的设计和实施。
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!

