良许Linux教程网 干货合集 如何写模块化的代码?

如何写模块化的代码?

随着我们工程化经验的增加,我们逐渐开始关注一个重要问题——模块化和模块设计是如何发挥作用的?那么,什么是真正的模块化呢?

在接手一个项目时,我们通常会想到要实现哪些功能,需要哪些模块。为了达到程序的模块化,我们可能会将代码分散到多个文件和目录中,并为这些目录或文件命名为”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语言编译器里,只有宏是静态内联的,所以使用宏是为了达到内联的目的。

然而能否内联,其实并不是宏与函数的根本区别,这里我不细说了,只要记住:应该尽量避免使用宏。如果想了解可以参考避免这7个误区,才能让【宏】削铁如泥

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

尽量使用局部变量和参数

我们应该尽量避免使用全局变量和类成员(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 }

总结

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

如果你喜欢我的文章表达的思维,转发分享是对我的厚爱,期待您的点赞在看,下一期,再见!

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部