开发时的分层解耦

一.三层结构

1.介绍

在我们进行程序设计以及程序开发时,尽可能让每一个接口、类、方法的职责更单一些(单一职责原则)。

  • 单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。

  • 这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利于后期的维护。

在我们项目开发中,从组成上看可以分为三个部分:

  • 请求处理、响应数据:负责,接收页面的请求,给页面响应数据。

  • 逻辑处理:负责业务逻辑处理的代码。

  • 数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。

可以将代码分为三层:

  • Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。

  • Service:业务逻辑层。处理具体的业务逻辑。

  • Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。

2.流程 

基于三层架构的程序执行流程,如图所示:

  • 前端发起的请求,由Controller层接收(Controller响应数据给前端)

  • Controller层调用Service层来进行逻辑处理(Service层处理完后,把处理结果返回给Controller层)

  • Serivce层调用Dao层(逻辑处理过程中需要用到的一些数据要从Dao层获取)

  • Dao层操作文件中的数据(Dao拿到的数据会返回给Service层)

二.分层解耦 

1.解耦思路

  • 提供一个容器,容器中存储一些对象(例:UserService对象)

  • Controller程序从容器中获取UserService类型的对象(应用程序中用到这个对象,就直接从容器中获取

2.解耦操作的知识储备

  • 控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。

    • 对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器。

  • 依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。

    • 程序运行时需要某个资源,此时容器就为其提供这个资源。

    • 例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象。

  • bean对象:IOC容器中创建、管理的对象,称之为:bean对象。

3.IOC和DI入门

(1)将Service及Dao层的实现类,交给IOC容器管理

在实现类加上 @Component 注解,就代表把当前类产生的对象交给IOC容器管理。

A. UserDaoImpl

@Component
public class UserDaoImpl implements UserDao {
    @Override
    public List<String> findAll() {
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
        ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
        return lines;
    }
}

B. UserServiceImpl 

@Component
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @Override
    public List<User> findAll() {
        List<String> lines = userDao.findAll();
        List<User> userList = lines.stream().map(line -> {
            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts[0]);
            String username = parts[1];
            String password = parts[2];
            String name = parts[3];
            Integer age = Integer.parseInt(parts[4]);
            LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            return new User(id, username, password, name, age, updateTime);
        }).collect(Collectors.toList());
        return userList;
    }
}

(2)为Controller 及 Service注入运行时所依赖的对象

A. UserServiceImpl

@Component
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;
    
    @Override
    public List<User> findAll() {
        List<String> lines = userDao.findAll();
        List<User> userList = lines.stream().map(line -> {
            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts[0]);
            String username = parts[1];
            String password = parts[2];
            String name = parts[3];
            Integer age = Integer.parseInt(parts[4]);
            LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            return new User(id, username, password, name, age, updateTime);
        }).collect(Collectors.toList());
        return userList;
    }
}

 B. UserController

@RestController
public class UserController {
    
    @Autowired
    private UserService userService;

    @RequestMapping("/list")
    public List<User> list(){
        //1.调用Service
        List<User> userList = userService.findAll();
        //2.响应数据
        return userList;
    }

}

 三.IOC详解

  • 前面我们提到IOC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象。IOC容器创建的对象称为bean对象。

  • Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:

    注解

    说明

    位置

    @Component

    声明bean的基础注解

    不属于以下三类时,用此注解

    @Controller

    @Component的衍生注解

    标注在控制层类上

    @Service

    @Component的衍生注解

    标注在业务层类上

    @Repository

    @Component的衍生注解

    标注在数据访问层类上(由于与mybatis整合,用的少)

    那么此时,我们就可以使用 @Service 注解声明Service层的bean。 使用 @Repository 注解声明Dao层的bean。

  • 注意1:声明bean的时候,可以通过注解的value属性指定bean的名字,如果没有指定,默认为类名首字母小写。

    注意2:使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。

四.DI详解

  1. 依赖注入,是指IOC容器要为应用程序去提供运行时所依赖的资源,而资源指的就是对象。
  2. 使用@Autowired这个注解,表示完成依赖注入的操作,而这个Autowired翻译过来叫:自动装配。 
  3. @Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)

  4. @Autowired 进行依赖注入,常见的方式:属性注入

@RestController
public class UserController {

    //方式一: 属性注入
    @Autowired
    private UserService userService;
    
  }

五.注意事项

  1.  如果在IOC容器中,存在多个相同类型的bean对象,要怎么办呢

方案一:使用@Primary注解

当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现。

@Primary
@Service
public class UserServiceImpl implements UserService {
}

方案二:使用@Qualifier注解

指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。 @Qualifier注解不能单独使用,必须配合@Autowired使用。

@RestController
public class UserController {

    @Qualifier("userServiceImpl")
    @Autowired
    private UserService userService;

 方案三:使用@Resource注解

是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。

@RestController
public class UserController {
        
    @Resource(name = "userServiceImpl")
    private UserService userService;

面试题:@Autowird 与 @Resource的区别

  • @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解

  • @Autowired 默认是按照类型注入,而@Resource是按照名称注入

五.常见状态码

六.翻译理解

component(零件),resource(资源),qualifier(修饰符),primary(首选),implement(实现)

<think>嗯,用户问的是关于嵌入式开发中软件分层解耦设计的详细介绍。首先,我需要确认用户的基础,可能是一个刚开始学习嵌入式的开发者,或者有一定经验但想优化架构的人。他们可能在实际项目中遇到了模块间耦合度高、维护困难的问题,想要了解如何通过分层解耦,提升代码的可维护性和扩展性。 接下来,我应该回忆软件分层的基本概念,常见的分层方式,比如硬件抽象层、驱动层、中间件层、应用层等。然后,解耦设计的关键点,比如模块化、接口抽象、依赖倒置等原则。需要结合实际例子,比如在STM32开发中,如何将GPIO操作封装到硬件抽象层,让上层应用不直接依赖具体硬件。 用户可能想知道分层的具体好处,比如便于移植、团队协作、测试等。同,可能存在的挑战,比如性能开销、设计复杂度增加,也需要提及,以提供全面的信息。 还要考虑用户可能的深层需求,比如是否在特定项目中遇到问题,或者希望学习最佳实践。因此,除了理论,加入实际应用场景和注意事项会更有帮助。比如,举一个传感器数据采集的例子,说明各层如何协作,如何通过接口隔离变化。 最后,确保语言通俗易懂,结构清晰,分点说明,让读者能够逐步理解分层解耦的设计思路和实现方法。可能还需要推荐一些设计模式,如工厂模式、观察者模式,来进一步解耦模块间的依赖。</think>嵌入式开发中的软件分层解耦设计是一种通过模块化、抽象化和接口隔离来提升代码可维护性、可移植性和扩展性的架构设计方法。以下是其核心思想和实现方式的详细说明: --- ### 一、为什么要分层解耦? 1. **降低耦合性** - 各层通过接口通信,避免直接依赖具体实现。 - 硬件更换(如传感器型号变更),只需修改底层代码。 2. **提高复用性** - 中间层可跨项目复用(如通信协议栈)。 3. **简化协作开发** - 团队可分层并行开发,例如硬件组实现驱动层,应用组专注于业务逻辑。 4. **便于测试** - 通过Mock接口实现单元测试(如模拟传感器数据输入)。 --- ### 二、典型分层架构(以物联网设备为例) ``` +-------------------+ | 应用层 | // 业务逻辑(如数据上传策略) +-------------------+ | 中间件层 | // 通用服务(RTOS、协议栈、文件系统) +-------------------+ | 驱动层 | // 硬件驱动(SPI/I2C、传感器芯片) +-------------------+ | HAL(硬件抽象层) | // 屏蔽硬件差异(如STM32与ESP32的GPIO操作统一化) +-------------------+ | BSP(板级支持包) | // 具体芯片外设配置(钟、中断) +-------------------+ ``` --- ### 三、关键实现方法 1. **接口抽象** ```c // 定义统一的传感器接口 typedef struct { int (*init)(void); float (*read_temperature)(void); } SensorDriver; // 具体传感器实现(如BMP280) const SensorDriver bmp280_driver = { .init = bmp280_init, .read_temperature = bmp280_read_temp }; // 应用层通过接口调用 float get_temperature() { return current_driver->read_temperature(); } ``` 2. **依赖倒置原则** - 上层定义接口,下层实现接口(如应用层定义数据存储接口,驱动层实现Flash/SD卡具体操作)。 3. **消息队列解耦** - 使用事件驱动架构,通过消息传递代替直接函数调用: ```c // 传感器数据采集线程 void sensor_thread() { float temp = read_sensor(); post_message(EVENT_TEMP_UPDATE, &temp); // 发送到消息队列 } // 应用层监听事件 void app_task() { while(1) { Event msg = wait_for_event(); if(msg.type == EVENT_TEMP_UPDATE) { handle_temperature(msg.data); } } } ``` --- ### 四、实际应用场景 **案例:智能家居温控系统** 1. **硬件变更**:从DS18B20更换为DHT22,只需修改驱动层实现,应用层代码无需变动。 2. **协议升级**:从MQTT切换到CoAP协议,仅需替换中间件层的网络模块。 3. **功能扩展**:新增蓝牙功能,通过添加新的驱动模块和中间件服务实现。 --- ### 五、注意事项 1. **性能权衡** - 多层调用可能增加函数栈开销,实性要求高的场景需谨慎设计(如电机控制优先直接寄存器操作)。 2. **层级划分粒度** - 小型项目可采用3层(HAL+Driver+App),复杂系统可能需要更多层级(如加入Service层)。 3. **跨平台支持** - 通过条件编译实现多平台兼容: ```c #ifdef STM32 #include "stm32_gpio.h" #elif defined(ESP32) #include "esp32_gpio.h" #endif ``` --- ### 六、扩展设计模式 1. **观察者模式**:实现模块间松耦合的事件通知 2. **工厂模式**:动态创建设备驱动实例 3. **适配器模式**:兼容不同接口规范的第三方库 通过分层解耦设计,可使嵌入式系统具备更好的架构弹性,适应快速迭代的物联网设备开发需求,同降低长期维护成本。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值