AI大模型教程
一起来学习

掌握应用分层:高内聚低耦合的艺术

应用分层

应用分层是一种软件开发设计思想, 它将应用程序分成N个层次, 这N个层次分别负责各自的功能, 多个层次之间协同提供完整的功能

常见的MVC设计模式, 就是应用分层的一种具体体现

为什么需要应用分层?

一开始,为了让项目快速上线,我们通常是不考虑分层的。但是随着业务越来越复杂,大量的代码混在一起,会出现逻辑不清晰、各模块相互依赖、代码扩展性差、牵一发而动全身等问题。

如何分层

MVC 就是把系统分成了 Model(模型), View(视图)和 Controller(控制器)三个层次,也就是将用户视图和业务处理隔离开,并且通过控制器连接起来,很好地实现了表现和逻辑的解耦,是一种标准的软件分层架构

目前主流的开发方式是 “前后端分离” 的方式, 后端开发工程师不再需要关注前端的实现, 所以对于Java后端开发者, 又有了一种新的分层架构: 把整体架构分为表现层、业务逻辑层和数据层。这种分层方式也称为”三层架构”

  1. 表现层: 是最靠近用户的一层,负责接收页面的请求,给页面响应数据
  2. 业务逻辑层: 负责处理业务逻辑
  3. 数据层: 负责存储和管理与应用程序相关的数据,负责业务数据的维护操作,包括增、删、改、查等操作

这三个部分, 在Spring的实现中, 均有体现:

  • Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
  • Service:业务逻辑层。处理具体的业务逻辑。
  • Dao:数据访问层,也称为持久层。负责数据访问操作,包括数据的增、删、改、查

MVC 和 三层架构 的区别和联系

从概念上来讲, 二者都是软件工程领域中的架构模式.

  • MVC架构模式由三部分组成, 分别是: 模型(Model), 视图(View)和控制器(Controller)
  • 三层架构将业务应用划分为:表现层, 业务逻辑层, 数据访问层

MVC中, 视图和控制器合起来对应三层架构中的表现层,模型对应三层架构中的业务逻辑层, 数据层以及实体类

二者其实是从不同角度对软件工程进行了抽象

  • MVC模式强调数据和视图分离, 将数据展示和数据处理分开, 通过控制器对两者进行组合
  • 三层架构强调不同维度数据处理的高内聚和低耦合, 将交互界面, 业务处理和数据库操作的逻辑分开

角度不同也就谈不上互相替代了,在日常的开发中可以经常看到两种共存的情况,但是二者的目的是相同的, 都是”解耦,分层,代码复用”

软件设计原则:高内聚低耦合

  • 高内聚:实现某个功能的时候,如果和这个功能相关的代码是集中放在一起的,就认为是”高内聚”; 如果是散落在项目的各个文件,各个角落中就认为是”低内聚”。高内聚就非常方便找到并修改代码
  • 低耦合:软件中各个层、模块之间的依赖关联越小越好。修改一处代码, 其他模块的代码改动越少越好

内聚和耦合并没有必然的联系

  • 内聚描述的是模块内部的事情(也有可能是模块之间)
  • 耦合描述的是模块之间的事情

高内聚低耦合矛盾吗?

不矛盾, 高内聚指的是一个模块中各个元素之间的联系紧密程度, 低耦合指的是各个模块之间的紧密程度

这就好比一个企业, 包含很多部门, 各个部门之间的关联关系要尽可能的小, 一个部门发生问题, 要尽可能对降低对其他部门的影响, 这就是”低耦合”; 但是部门内部员工关系要尽量紧密,遇到问题一起解决克服,这叫做”高内聚”。又比如邻里邻居, 楼上漏水, 楼下遭殃, 就是”高耦合”;一个家庭内部的关系越紧密越好,一个家庭成员生病, 其他成员帮忙照顾, 就叫”高内聚”,一个家庭尽可能减少对另一个家庭的影响,就是”低耦合”

项目示例

  • controller包下是控制层: 接收前端发送的请求,对请求进行处理,并响应数据
import com.example.messagewall.model.MessageInfo;
import com.example.messagewall.service.MessageInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RequestMapping("/message")
@RestController
public class MessageController {
    @Autowired
    private MessageInfoService messageInfoService;

    @RequestMapping(value = "/publish", method = RequestMethod.POST)
    public Boolean publish(@RequestBody MessageInfo messageInfo) {
        //添加到数据库
        messageInfoService.insert(messageInfo);
        return true;
    }

    @RequestMapping("/getList")
    public ListMessageInfo> getList() {
        //从数据库查
        return messageInfoService.query();
    }
}
  • service包下是业务逻辑层: 处理具体的业务逻辑
import com.example.messagewall.mapper.MessageInfoMapper;
import com.example.messagewall.model.MessageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class MessageInfoService {
    @Autowired
    private MessageInfoMapper messageInfoMapper;

    public Integer insert(MessageInfo messageInfo) {
        return messageInfoMapper.insert(messageInfo);
    }

    public ListMessageInfo> query() {
        return messageInfoMapper.query();
    }
}
  • mapper包下是数据访问层: 负责数据访问操作,包括数据的增、删、改、查
import com.example.messagewall.model.MessageInfo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface MessageInfoMapper {
    //添加留言
    @Insert("insert into message_info (`from`,`to`, `message`) values " +
            "(#{from}, #{to}, #{message})")
    Integer insert(MessageInfo messageInfo);

    //查询留言
    @Select("select * from message_info where delete_flag=0")
    ListMessageInfo> query();
}
  • model包下是实体类通常对应数据库中的表,包含该事物的属性(数据)和行为(方法),是数据存储和业务逻辑的基础载体
import lombok.Data;

@Data
public class MessageInfo {
    private Integer id;
    private String from;
    private String to;
    private String message;
    private Integer deleteFlag;
    private String createTime;
    private String updateTime;
}

前端messagewall.html代码:

DOCTYPE html>
html lang="en">

head>
    meta charset="UTF-8">
    meta name="viewport" content="width=device-width, initial-scale=1.0">
    title>留言板title>
    style>
        .container {
            width: 350px;
            height: 300px;
            margin: 0 auto;
            /* border: 1px black solid; */
            text-align: center;
        }

        .grey {
            color: grey;
        }

        .container .row {
            width: 350px;
            height: 40px;

            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .container .row input {
            width: 260px;
            height: 30px;
        }

        #submit {
            width: 350px;
            height: 40px;
            background-color: orange;
            color: white;
            border: none;
            margin: 10px;
            border-radius: 5px;
            font-size: 20px;
        }
    style>
head>

body>
    div class="container">
        h1>留言板h1>
        p class="grey">输入后点击提交, 会将信息显示下方空白处p>
        div class="row">
            span>谁:span> input type="text" name="" id="from">
        div>
        div class="row">
            span>对谁:span> input type="text" name="" id="to">
        div>
        div class="row">
            span>说什么:span> input type="text" name="" id="say">
        div>
        input type="button" value="提交" id="submit" onclick="submit()">
        
    div>

    script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js">script>
    script>
        $.ajax({
            type: "get",
            url: "/message/getList",
            success: function(messages) {
                if(messages!=null) {
                    for(let message of messages) {
                        var divE = "
"+message.from +"对" + message.to + "说:" + message.message +"
"
; $(".container").append(divE); } } } }); function submit(){ //1. 获取留言的内容 var from = $('#from').val(); var to = $('#to').val(); var say = $('#say').val(); if (from== '' || to == '' || say == '') { return; } //调用后端接口,发表留言 $.ajax({ type: "post", url: "/message/publish", contentType: "application/json", data: JSON.stringify({ "from": from, "to": to, "message": say }), success: function(result) { if(result) { //成功 //2. 构造节点 var divE = "
"+from +"对" + to + "说:" + say+"
"
; //3. 把节点添加到页面上 $(".container").append(divE); //4. 清空输入框的值 $('#from').val(""); $('#to').val(""); $('#say').val(""); } else { //失败 alert("发布失败"); } } }); }
script> body> html>

配置文件application.yml:

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
    username: root
    password: '123456'
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration: # 配置打印 MyBatis日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true #配置驼峰自动转换

pom文件引入依赖部分:

    dependencies>
        dependency>
            groupId>org.springframework.bootgroupId>
            artifactId>spring-boot-starter-webartifactId>
        dependency>

        dependency>
            groupId>org.springframework.bootgroupId>
            artifactId>spring-boot-starter-testartifactId>
            scope>testscope>
        dependency>
        dependency>
            groupId>org.projectlombokgroupId>
            artifactId>lombokartifactId>
            optional>trueoptional>
        dependency>
        dependency>
            groupId>org.mybatis.spring.bootgroupId>
            artifactId>mybatis-spring-boot-starterartifactId>
            version>3.0.3version>
        dependency>
        dependency>
            groupId>com.mysqlgroupId>
            artifactId>mysql-connector-jartifactId>
            scope>runtimescope>
        dependency>
    dependencies>

按我配置的话,MySQL建库建表语句是这样的:

CREATE DATABASE IF NOT EXISTS mybatis_test;

USE mybatis_test;

CREATE TABLE message_info (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '消息ID',
    `from` VARCHAR(255) COMMENT '发送者',
    `to` VARCHAR(255) COMMENT '接收者',
    message VARCHAR(255) COMMENT '消息内容',
    delete_flag TINYINT DEFAULT 0 COMMENT '逻辑删除标志:0=有效,1=无效',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
);

全部搞定之后访问http://127.0.0.1:8080/messagewall.html就能交互啦!

应用分层的好处

  • 降低层与层之间的依赖, 结构更加明确, 利于各层逻辑的复用
  • 开发人员可以只关注整个结构中的其中某一层, 极大地降低了维护成本和维护时间
  • 可以很容易的用新的实现来替换原有层次的实现
  • 有利于标准化

企业规范

适用于多数企业, 均不做强制要求. 具体以所在企业为准

  1. 类名使用大驼峰风格,但以下情形例外:DO/BO/DTO/VO/AO
  2. 方法名、参数名、成员变量、局部变量统一使用小驼峰风格
  3. 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词

常见命名风格介绍

  • 大驼峰: 所有单词首字母都需要大写, 又叫帕斯卡命名法, 比如: UserController
  • 小驼峰:除了第一个单词,其他单词首字母大写,比如: userController
  • 蛇形:用下划线 (_) 作为单词间的分隔符,一般小写,又叫下划线命名法,比如: user_controller
  • 串形:用短横线 (-) 作为单词间的分隔符,又叫脊柱命名法,比如: user-controller

文章来源于互联网:掌握应用分层:高内聚低耦合的艺术

赞(0)
未经允许不得转载:5bei.cn大模型教程网 » 掌握应用分层:高内聚低耦合的艺术
分享到: 更多 (0)

AI大模型,我们的未来

小欢软考联系我们