真正的模块化编程原来是这样的!

2023-05-16

已剪辑自: https://mp.weixin.qq.com/s/uo4tnsEnpULAruayZHcKAw

随着我们工程化经验的增加,不知不觉的我们就会关心到这个问题,模块化,模块设计就显现出来,那到底什么是模块化呢?

这不叫模块化!

我相信在很多时候,我们刚开始从零开始接手一个项目的时候,编码之前总想着要实现什么的功能需要的模块,然后要程序模块化,这种思想是值得认同的,但往往我们并没有做到真正的模块化。

例如在一个需要很多外设接口,一般需要硬件初始化,相关配置,中断服务程序,输入输出,逻辑处理等功能,我们的做法可能就是把代码分布到多个文件和目录里面,然后把这些目录或者文件取名xxxModule

甚至把这些目录分放在不同的仓库目录里面,结果随着编码的增加,发现好多小功能都重复了,或者本可以写在一起的函数并没有放在一起,导致我们的代码思想不是很流畅,这样做会误导我们,甚至整个项目实现的思路。

究其原因这是因为我们其实并不理解什么叫做模块,而仅仅是肤浅的把代码切割开来,分放在不同的位置,虽然这确实达到了部分模块化的目的,但是也会制造一些不必要的麻烦。

什么是真正的模块化?

真正的模块化,并不是简单文本意义上的,而是与逻辑相关的有逻辑意义的。一个模块应该像一个集成电路芯片,我们能见到能使用的都很清晰,它定义了良好的输入和输出。

模块是可能分开地被编写的单位。这使他们可再用和允许广泛人员同时协作、编写及研究不同的模块。

实际上,编程语言已经为我们提供了一种很好的模块化方法,它的名字叫做函数。每一个函数都有明确的输入(参数)和输出(返回值),同一个文件里可以包含多个函数,所以你其实根本不需要把代码分开在多个文件或者目录里面,同样可以完成代码的模块化。

按照函数这个原则,我可以把代码全都写在同一个文件里,却仍然是非常模块化的代码,是不是觉得与之前的想法不一样?

是的,软件编程模块是一套一致而互相有紧密关联的软件组织,每个模块完成一个特定的子功能,所有的模块按某种方法组装起来,成为一个整体,完成整个系统所要求的功能,这就是真正的模块化。

怎么模块化?

我们知道了模块化的原则与道理之后,就可以按照这个思路去开发项目了,想要达到很好的模块化,你需要做到以下几点。我们从实现角度来说。

避免写太长的函数

一眼望去,如果一个函数的代码你电脑一页都看不完,那肯定是冗长了或者不符合模块化编程了。不需要滚屏就可以看得到全部的函数内容,那对我们的理解也有帮助。

一般来说一个函数最好不要超过40行代码(当然这个不是死规定,只是一个经验建议而已),如果写的函数太大了,就应该把它拆分成几个更小的函数。

也许你会说到,这很难办到,逻辑很多或者很多判断条件的时候,40行往往不够吧,那么其实我们也需要考虑到函数里面一些复杂的部分,是不是可以提取出来,单独写一个小函数,再从原来的函数里面调用。

另外函数层级也不要太多,比如一个函数长成这样:

 1void function(void* para)
 2{
 3    if (getOS().equals("MacOS")) {
 4        a();
 5        if(getOS().equals("AndroidOS")){
 6            b();
 7            if(getOS().equals("flymeOS")){
 8              c();
 9            }
10        }
11    } 
12}

我们看到这个函数由于很多的判断,函数层级已经超过4层了,这其实对我们的理解很不利,另一方面,一些逻辑变化也会导致我们的更改很麻烦,费脑子。

每个函数只做一件简单的事情

有些人喜欢写一些通用函数,一般我都放在publicModule里面,既可以实现这个工又可以实现那个功能,它的内部依据某些变量或者是某些条件,来选择这个函数所要实现的小功能。

比如,写出这样的函数:

 1void function() {
 2  if (getOS().equals("MacOS")) {
 3    a();
 4  } else {
 5    b();
 6  }
 7
 8  c();
 9
10  if (getOS().equals("MacOS")) {
11    d();
12  } else {
13    e();
14  }
15}

这个函数,是想表达根据系统是否为MacOS,从而来做不同的事情。从这个函数可以很容易的看出,其实只有函数c()是两种系统共有的,而其它的函数a(), b(), d(), e()都属于不同的分支。

但是这种复用的写法其实是很不利的。如果一个函数可能实现2个功能,并且它们之间共同点少于它们的不同点,那我们最好就写两个不同的函数,否则这个函数的逻辑就不会很清晰,容易出现错误。

不要害怕,函数简单点不丢人,我们不需要炫技。

好了,根据上面的说法,这个函数可以改写成两个函数:

1void funMacOS() {
2  a();
3  c();
4  d();
5}

1void funOther() {
2  b();
3  c();
4  e();
5}

如果我们发现两件事情大部分内容相同,只有少数不同,也就是说共同点大于它们的不同点,那就更简单了,我们可以把相同的部分提取出去,做成一个辅助函数

比如,如果有个函数是这样:

 1void function() {
 2
 3  a();
 4  b()
 5  c();
 6
 7  if (getOS().equals("MacOS")) {
 8    d();
 9  } else {
10    e();
11  }
12}

其中函数a(),b(),c()都是一样的,只有函数d()、e()根据系统有所不同。那么你可以把函数a(),b(),c()提取出去,代码如下:

1void preFun() {
2  a();
3  b()
4  c();
5}

然后分别写两个函数:

1void funMacOS() {
2  preFun();
3  d();
4}

1void funOther() {
2  preFun();
3  e();
4}

这样更改之后,是不是清晰了很多,我们既共享了代码,避免冗余,又做到了每个函数只做一件简单的事情。这样的代码,逻辑就更加清晰了。

工具函数

是的,我再说一遍,每个函数只做一件简单的事情。但是有些时候在我们的工程代码中,我们可能会发现其实里面有很多的重复。

所以一些常用的代码,不管它有多短或者多么的简单,都可以提取出去做成函数,例如有些帮助函数也许就只有2行,但是我们把它封装成一个函数的话,就能大大简化主要函数里面的逻辑。

也许你可能会说,这些函数调用会增加代码开销,但随着硬件发展以及技术变革,这已经是一种过时的观念了。

现代的很多编译器都能自动的把小的函数内联(inline)到调用它的地方,所以根本不会产生函数调用,也就不会产生任何多余的开销。

那么我可以使用宏来代替工具函数?一行代码搞定了,比如

1#define FillAndSendTxOptions( TRANSSEQ, ADDR, ID, LEN, TxO ) { \
2afStatus_t stat;                                    \
3ZDP_TxOptions = (TxO);                              \
4stat = fillAndSend( (TRANSSEQ), (ADDR), (ID), (LEN) );          \
5ZDP_TxOptions = AF_TX_OPTIONS_NONE;                 \
6return stat;                                        \
7}

同样,这也许也过时了,我不想让宏(macro)来背这个锅,在早期的C语言编译器里,只有宏是静态内联的,所以使用宏是为了达到内联的目的。

然而能否内联,其实并不是宏与函数的根本区别,这里我不细说了,只要记住:应该尽量避免使用宏

为了内联而使用宏,其实是滥用了宏,这会引起各种各样的麻烦,比如使程序难以理解,难以调试,容易出错等等。

尽量使用局部变量和参数

我们应该尽量避免使用全局变量和类成员(class member)来传递信息,举个例子:

 1class A {
 2   String x;
 3
 4   void findX() {
 5       ...
 6       x = ...;
 7   }
 8
 9   void fun() {
10       findX();
11       ...
12       print(x);
13   }
14}

首先,使用函数findX(),把一个值写入成员x。然后,调用x的值。这样,x就变成了findX和print之间的数据通道。

由于x属于class A,这样程序就失去了模块化的结构,与我们所说的模块化意义不符了。两个函数依赖于成员x,就不再有明确的输入和输出,而是依赖全局的数据。

函数findX和fun不再能够离开class A而存在,具有依赖性,并且由于类成员还有可能被其他代码改变,这样就会导致代码变得复杂难以理解,函数的正确性也难以保证。

如果使用局部变量和参数来传递信息,那么这两个函数就不需要依赖于某一个class,不易出错,代码如下:

1String findX() {
2    ...
3    x = ...;
4    return x;
5 }
6 void foo() {
7   String x = findX();
8   print(x);
9 }

总结

模块化是指解决一个复杂问题时,自顶向下,逐层把系统划分成若干模块的过程,深入理解模块化,什么是真正的模块化,那么我们才能够事半功倍。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

真正的模块化编程原来是这样的! 的相关文章

  • 怎么成为稚晖君?

    如何成为IT大佬稚晖君 电子系统设计应具备的基本技能和方法论 快速提高电子技术的必经之路 一些老生常谈的道理 嵌入式AI入坑经历 稚晖君软件硬件开发环境总结 首先 xff0c 机器学习深度学习这些和硬件是两个领域的内容 xff0c 个人不建
  • 单片机、DSP、ARM、FPGA,它们都能干什么

    已剪辑自 https zhuanlan zhihu com p 476394240 概述 首先 xff0c 嵌入式 这是个概念 xff0c 准确的定义没有 xff0c 各个书上都有各自的定义 但是主要思想是一样的 xff0c 就是相比较PC
  • DO-178B-软件配置管理过程

  • DO-178B-软件质量保证过程

  • 软件工程化与机载软件适航审查实践--质量保证过程

  • 安全关键软件开发与审定——DO-178C标准实践指南阅读笔记九——软件验证

  • 三十分钟做一个网页游戏

    本文目的是短时间之内 xff0c 通过做出一个简单的缘分对对碰游戏 xff0c 了解网页的基本要素 之前没有接触过网页开发 xff0c 这次算是个入门了 对于大部分网页 xff0c 都要包括HTML CSS JavaScript三种技术 而
  • 适航文章汇总

    DO 178C 软件安全基础 不同行业的软件安全标准介绍和对比 DO 178B及适航相关的术语表 我来说说DO 178B标准 软件适航加油站文章汇总 DO 178B和DO 178C的异同 GJB5000A与DO178B C简介及对比 DO
  • 适航文章汇总

    DO 178C 软件安全基础 不同行业的软件安全标准介绍和对比 DO 178B及适航相关的术语表 我来说说DO 178B标准 软件适航加油站文章汇总 DO 178B和DO 178C的异同 GJB5000A与DO178B C简介及对比 DO
  • 嵌入式软件之路的几个阶段,你目前在哪个阶段?

    已剪辑自 https mp weixin qq com s t9bXyGML9DbPDO 7kzaeMg 很多读者总爱问一个问题 xff1a 该不该放弃嵌入式 单片机这条路 xff1f 以下是梦人亦冷分享的嵌入式开发经历 xff0c 希望可
  • 校验算法总结

    文章目录 什么是数据校验奇偶校验1 什么是奇偶校验码2 算法3 应用举例4 错误检测能力5 奇校验还是偶校验奇偶校验基本算法优点缺点改进 纵向冗余校验基本算法优点缺点 整数加法校验和基本算法优点缺点 异或校验CRC校验1 什么是CRC xf
  • 嵌入式环形队列、消息队列的实现原理及异同

    已剪辑自 https mp weixin qq com s opF5FCDRarYwGhrG4gGCWQ 环形队列 和 消息队列 在嵌入式领域有应用非常广泛 xff0c 相信有经验的嵌入式软件工程师对它们都不陌生 但经常看到一些初学者问一些
  • 华为专家自述:一个成功码农要经历四个阶段

    已剪辑自 https mp weixin qq com s RyykrGlpxVM1z24bFdJyog 无论是在T W公司还是在华为 xff0c 我有幸得以一直从事OS xff08 操作系统 xff09 行业 xff0c 但坦率来讲 xf
  • 陈吉宁经典演讲:平庸与卓越的差别

    来 源 xff1a 清华大学研究生教育 xff08 ID tsinghua grad edu xff09 亲爱的同学们 xff1a 今天 xff0c 共有1318名同学获得博士 硕士学位 首先 xff0c 我代表学校 xff0c 向同学们奋
  • 谈谈汽车软件中间件(Autosar为例)

    文章目录 操作系统 xff0c 中间件 xff0c 应用软件 各司其职分工不同什么是汽车软件中间件 xff1f 汽车软件中间件有什么好处 xff1f 中间件的明星方案 AUTOSARAUTOSAR Adaptive拯救AUTOSAR技术细节
  • 深入浅出讲解低功耗蓝牙(BLE)协议栈

    已剪辑自 https www cnblogs com bluestorm p 12031957 html 详解BLE 空中包格式 兼BLE Link layer协议解析 https www cnblogs com iini p 897780
  • 常见通信协议

    文章目录 1 通信 与 通讯 傻傻分得清2 通讯协议2 1 HTTP HTTPS2 2 WebService REST2 3 CoAP 协议2 4 MQTT 协议 低带宽 2 5 DDS 协议 高可靠性 实时 2 6 AMQP 协议 互操作
  • 渲染业务领域全景图

    最近图形学应用领域愈发广泛 xff0c 根据我的理解 xff0c 制作了一张渲染相关业务全景图 xff0c 希望对大家的职业规划有一定帮助
  • 谈一谈AI对人工的取代

    文章目录 AI绘画现在达到了什么水平 xff1f 易用性怎么样 xff1f 缘起 xff1a 2015年 用文字画画 2021年 Dalle 与 开源社区的程序员们 openAI与它并不open的Dalle AI开源社区 Dream by
  • 推荐几个代码自动生成器

    文章目录 老的代码生成器的地址 xff1a https www cnblogs com skyme archive 2011 12 22 2297592 html https link zhihu com target 61 https 3

随机推荐

  • 开始做公众号的一些方法技巧总结

    文章目录 封面正文预览公众号文章排版公众号运营全攻略 xff08 理论篇 xff09 公众号运营全攻略 xff08 工具技巧篇 xff09 封面 因为公众号的封面是分两个尺寸的 在头条的封面会长一些 xff0c 比例为 xff08 2 35
  • 程序员需要建立的对技术、业务、行业、管理、投资的认知

    文章目录 作为 IT 行业的过来人 xff0c 你有什么话想对后辈说的 xff1f 谈谈程序员转型的事儿 xff08 1 程序员应该重视技术吗 xff09 到底什么是IT技术 xff1f 怎么找到自己的学习方向 xff1f 献给新手程序员最
  • 虚拟化技术在机载软件中的应用

    虚拟化技术在航空计算领域的应用 基于软件虚拟化技术的新一代航空机载软件设计
  • 如何判断一段程序是否是裸机程序?

    在嵌入式MCU领域 xff0c 一般将不移植操作系统直接烧录运行的程序称为裸机程序 一般来说 xff0c 非易失性存储 xff0c 时钟 xff0c 图形显示 xff0c 网络通讯 xff0c 用户I O设备 都需要硬件依赖 基于硬件基础
  • 单片机STM32有什么推荐的裸机编程架构

    作者 xff1a DBinary 链接 xff1a https www zhihu com question 438340661 answer 2735154401 来源 xff1a 知乎 著作权归作者所有 商业转载请联系作者获得授权 xf
  • 一文讲清微服务架构、分布式架构、微服务、SOA

    文章目录 四种软件架构 xff1a 单体架构 分布式架构 微服务架构 Serverless架构一 单体架构二 分布式应用三 微服务架构四 Serverless架构 微服务是什么 xff1f 一 单体软件二 面向服务架构三 微服务 SOA架构
  • 敏捷开发,持续集成/交付/部署, DevOps总结

    文章目录 敏捷开发入门教程一 迭代开发二 增量开发三 敏捷开发的好处3 1 早期交付3 2 降低风险 四 如何进行每一次迭代五 敏捷开发的价值观六 十二条原则七 参考链接 持续集成 交付 部署一 概念二 持续交付三 持续部署四 流程4 1
  • IC集成电路 测试与验证的区别?

    在数字IC中 xff0c 验证与测试完全是两个概念 验证是在pre silicon 阶段 xff0c 也就是流片之前 xff0c 随着设计一起进行的 验证的主要目的是保证芯片逻辑功能的正确性和功能的完备性 验证的一般流程如下 xff1a 测
  • EGL综述

    参考 xff1a https www khronos org registry EGL specs eglspec 1 5 pdf 什么是EGL EGL是支持多平台 多操作系统的 xff0c 比如安卓 Unix Windows等 为了扩展性
  • pcie的rc模式和ep模式有什么区别?

    pcie的rc模式和ep模式有什么区别 xff1f RC PCI Express root complex 在RC模式时 xff0c 使用PCIE类型1配置头 xff1b EP endpoint device 工作方式 在EP模式时 xff
  • Android程序员一年没上班该如何找工作

    前言 Android程序员老王在21年7月份向公司提出了离职 离职后老王觉得在上家工作那么久 xff0c 就想趁着这个机会好好放松一下 由于让自己休息了两个月在加上他自己存了一点积蓄 xff0c 导致后面半年时间都没有找工作面试 到了22年
  • 为什么C语言执行效率高,运行快?

    已剪辑自 https mp weixin qq com s JUucTzACS IFO3iTO77DhQ 简述 都说C语言编写的程序执行效率比较高 xff0c 那么到底高在哪里 xff0c 我们一块来学习学习 C语言由来 C语言源自于BCP
  • 嵌入式5个RTOS程序设计建议

    已剪辑自 https mp weixin qq com s cCgQ5nfGiQckyqkXKxWtLQ 今天聊一下RTOS应用程序设计的五个实践技巧 我在编写RTOS应用程序的过程中 xff0c 经常会遇到这些困难 xff0c 包括正确确
  • 详解C语言二级指针三种内存模型

    已剪辑自 https mp weixin qq com s EBoKOgoVFl751jPe QEAlg 整理 xff1a 李肖遥 二级指针相对于一级指针 xff0c 显得更难 xff0c 难在于指针和数组的混合 xff0c 定义不同类型的
  • 软件架构设计与需求分析方法论

    文章目录 1 软件架构体系1 1 系统与子系统1 2 模块 组件 服务1 3 软件架构体系 2 架构原则2 1 解耦2 2 分层2 3 封装 3 架构的方法3 1 业务架构3 2 功能架构3 3 系统架构3 4 技术架构3 5 数据架构3
  • 马斯洛人类需求五层次理论(Maslow‘s Hierarchy of Needs)

    已剪辑自 https wiki mbalib com wiki E9 A9 AC E6 96 AF E6 B4 9B E4 BA BA E7 B1 BB E9 9C 80 E6 B1 82 E4 BA 94 E5 B1 82 E6 AC A
  • 从需求收集到需求落地,需求分析如何才能更全面?

    从需求收集到需求落地 xff0c 需求分析如何才能更全面 xff1f 已剪辑自 http www moonpm com 503 html 一 什么是需求 心里学上定义 xff1a 需求是由个体在生理上或者心理上感到某种欠缺而力求获得满足的一
  • 什么是云原生?

    已剪辑自 https juejin cn post 6844904197859590151 伴随云计算的滚滚浪潮 xff0c 云原生 CloudNative 的概念应运而生 xff0c 云原生很火 xff0c 火得一塌糊涂 xff0c 都0
  • 三年!我完成了自己的一次蜕变

    已剪辑自 https mp weixin qq com s r9Qv4XkLQ 3QClOeb5f19g 大家好 xff0c 我是txp xff0c 今天分享一篇我个人的一个成长经历 xff01 希望对大家有帮助 xff0c 文字可能会稍微
  • 真正的模块化编程原来是这样的!

    已剪辑自 https mp weixin qq com s uo4tnsEnpULAruayZHcKAw 随着我们工程化经验的增加 xff0c 不知不觉的我们就会关心到这个问题 xff0c 模块化 xff0c 模块设计就显现出来 xff0c