模块与接口规范_第1页
模块与接口规范_第2页
模块与接口规范_第3页
模块与接口规范_第4页
模块与接口规范_第5页
已阅读5页,还剩27页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、模块与接口规范模块与接口规范模块及模块接口设计原则一.何为模块 模块由接口和实现两部分组成。 -接口指明模块的功能,即模块能做什么。它声明了使用该模块代码的标识符,类型和函数原型等; -实现指模块是如何完成起接口声明的功能。一个具体的功能模块接口是唯一固定的,但是它的实现可能会有很多种。每个实现可能使用不同的算法和数据结构,但是都必须符合接口给出的使用声明。M1M2M6M3M4M5二二. . 模块划分基本准则模块划分基本准则模块的划分以功能为标准,相对独立的功能划分为不同的模块,各模块之间根据需要可以进行数据通信,但是必须保持功能上的相对独立性。 “功能独立”的模块可以降低开发、测试、维护等阶

2、段的代价。但是“功能独立”并不意味着模块之间保持绝对的孤立。一个系统要完成某项任务,需要各个模块相互配合才能实现,此时模块之间就要进行信息交流。 从功能上划分模块,保持“功能独立”是模块化设计的基本原则。三. 模块与接口设计准则 接口是对模块功能的声明,只需要指明客户调用程序可能使用的标识符(包括数据类型,函数原型等)即可,应尽可能隐藏内部细节和算法。 1封装与隐藏封装与隐藏applicationM1 tinterface1M2Mn声明与实现分离 2模块的独立性模块的独立性 为了保证模块的独立封装性,就必须将声明与实现分离开来。C语言对此只提供最基本的支持。在标准C语言中,接口在头文件中声明,

3、头文件的扩展名为.h。 由于标准C语言没有提供命名空间机制,较大规模软件中,不同模块的全局变量和函数也很可能重名,导致重复定义错误。同时被多个模块使用的全局变量,是增大模块间耦合性的重要因素之一,为减少模块间耦合性,应尽量少用或不用多个模块共用的全局变量。内聚是一个模块内部各成分之间相关联程度的度量。根据内聚性的强弱,有以下几种类型: 3强内聚性强内聚性 (1)随机内聚: 如果一个模块的各成分之间毫无关系,则称为随机内聚。 (2)逻辑内聚: 几个逻辑上相关的功能被放在同一模块中,则称为逻辑内聚。如一个模块读取各种不同类型外设的输入。尽管逻辑内聚比偶然内聚合理一些,但逻辑内聚的模块各成分在功能上

4、并无关系,即使局部功能的修改有时也会影响全局,因此这类模块的修改也比较困难。 (3)时间内聚: 如果一个模块完成的功能必须在同一时间内执行(如系统初始化),但这些功能只是因为时间因素关联在一起,则称为时间内聚。 (4)过程内聚: 如果一个模块内部的处理成分是相关的,而且这些处理必须以特定的次序执行,则称为过程内聚。 (5)通信内聚: 如果一个模块的所有成分都操作同一数据集或生成同一数据集,则称为通信内聚。 (6)顺序内聚: 如果一个模块的各个成分和同一个功能密切相关,而且一个成分的输出作为另一个成分的输入,则称为顺序内聚。 (7)功能内聚: 模块的所有成分对于完成单一的功能都是必须的,则称为功

5、能内聚。 m1m4m3m5m6m2Interface nMn防止把没有关联的功能放到一个模块中。 3.1. 设计高内聚性模块设计高内聚性模块 防止函数或模块内出现随机内聚。在编程时,经常遇到在不同函数中使用相同的代码,许多开发人员都会把这些代码提出来,并构成一个新函数。若这些代码关联较大并且是完成一个功能的,那么这种构造是合理的,否则这种构造将产生随机内聚的函数。 例如:例如:很多软件系统在启动的时候都需要初始化一些参数,因此不少系统会把初始化模块做成一个或多个函数。但是要防止把毫不相关的参数初始化放入同一个函数。int init_env()init_graphics.;/初始化图形设置ini

6、t_language.;/初始化语言设置init_connect.;/初始化网络连接. 假如模块A需要使用a语言,无网络连接,而模块B需要使用b语言,有网络连接。则init_env函数无论怎么改动都无法同时满足两个模块要求。此时函数init_env就是随机内聚函数,正确的修改方法应该是根据初始化的模块不同,分解成相对独立的函数。修改init_env()为三个独立的函数:int init_graphics();int init_language();int init_connect();这样不同的功能模块,可以根据自己的需要调用不同的初始化模块。 函数的功能要单一,不要设计“多功能”函数。 函数

7、的功能应该是单一而明确的。不要把不同的功能放入同一个函数,特别是尽量不要用控制开关来让数据处理函数实现不同的功能。#define TYPE_ADD0#define TYPE_SUB1int add_or_sub(int a, int b, int flag)if (TYPE_ADD = flag)return (a + b);elsereturn (a b);例如:如下是不好的函数设计,函数功能不单一,且实现的功能无必然联系。 耦合(Coupling) 是模块之间依赖程度的度量。内聚和耦合是密切相关的,与其它模块存在强耦合的模块通常意味着弱内聚,而强内聚的模块通常意味着与其它模块之间存在弱耦合

8、。模块设计追求强内聚,弱耦合。4弱耦合性弱耦合性耦合的强度依赖于以下几个因素:(1)一个模块对另一个模块的调用;(2)一个模块向另一个模块传递的数据量;(3)一个模块施加到另一个模块的控制的多少;(4)模块之间接口的复杂程度。 耦合按从强到弱的顺序可分为以下几种类型: (1)内容耦合: 当一个模块直接修改或操作另一个模块的数据,或者直接转入另一个模块 时,就发生了内容耦合。此时,被修改的模块完全依赖于修改它的模块。 (2)公共耦合: 两个以上的模块共同引用一个全局数据项就称为公共耦合。 (3)控制耦合: 一个模块在界面上传递一个信号(如开关值、标志量等)控制另一个模 块,接收信号的模块的动作根

9、据信号值进行调整,称为控制耦合。 (4)标记耦合: 模块间通过参数传递复杂的内部数据结构,称为标记耦合。此数据结构 的变化将使相关的模块发生变化。 (5)数据耦合: 模块间通过参数传递基本类型的数据,称为数据耦合。 (6)非直接耦合: 模块间没有信息传递时,属于非直接耦合。减少多个模块间的全局变量 4.1. 设计弱耦合性模块设计弱耦合性模块 多个模块间都能使用的全局变量会增大模块间耦合,给程序的调试和维护带来很大的不便。尽量采用参数传递来取代多个模块间的全局变量。注意:使用static修饰的模块内全局变量不会增加模块间耦合,在参数传递极为频繁的情况下,可以将需要频繁传递的参数提取为模块内部全局

10、变量,以减少函数参数个数和参数传递。多个模块间如果存在全局变量,尽量让该全局变量只被一个模块修改,而其它模块只读。减少模块间接口复杂度,函数参数不得超过7个。 复杂的函数接口,既不便于使用,也会增加调用时的出错几率。如果函数功能复杂,参数过多,可以考虑分解成几个函数来完成,或将有逻辑关联的参数构建成结构体来传递参数。 减少模块间控制参数,模块通信尽量只使用数据通信。 模块控制参数是指,某个模块的功能,部分或完全取决于另一模块传递过来的参数。控制参数会增大模块耦合,使得模块不便于理解和调试,应尽量避免。 extern int control_param_from_B;/控制参数,从B模块传递而来

11、some code;if (control_param_from_B = xxx)/根据传递过来的参数决定功能。do something;elsedo other thing;如果模块间需要进行数据通信,尽量采用文本形式的简单数据格式。不要定义复杂二进制数据格式,除非文本格式无法满足功能要求。 不少模块间需要进行数据通信,通信数据格式尽量采用简单文本形式。二进制通信格式既不便于使用,也不便于调试,不建议使用。 数据格式应尽可能简单,且必须能满足可扩充性。 采用简单格式的文本数据通信,也有助于减少模块间耦合。一般来说,二进制通信协议如果数据格式发生变化,则通信的各个模块编解码都需要相应变化。而文

12、本通信能保持较好的兼容性。 二进制编码一般情况下能提高时间和空间效率,当需要传递大量数据且对性能要求较高,或必须传递二进制数据时,才可以考虑二进制编码协议。普通情况下,二进制编码带来的效率提升甚至无法抵消编解码带来的额外开销。如果一个模块可以被扩充,则称模块具有可扩充性。当着手一个新模块时,很难一次性解决所有问题,应该先纵观问题的一些重要方面,同时作好以后补充的准备。5.可复用性与可扩充性可复用性与可扩充性 5.1. 模块设计过程中,数据结构需适当考虑可扩充性。模块设计过程中,数据结构需适当考虑可扩充性。 例如:结构体可以设置保留字段,为以后的升级或功能扩展做预留空间。 IP数据报格式及首部中

13、的各字段5.2函数的设计,如果相关联的参数较多,可以合并为结构体。函数的设计,如果相关联的参数较多,可以合并为结构体。 结构体是C语言实现面向对象的关键之一,可以很好的体现面向对象思想。同时能给功能升级和扩充带来很大的方便。 例如: 假如某函数对一个数据结构进行处理,可以定义为以下形式: func_name(int option1, int option2, char option3, char *option4) 也可以将四个数据项定义为一个结构体,函数采用结构体做参数typedef structint option1;int option2;char *option4;char optio

14、n3;OPTION, *P_OPTION;func_name(P_OPTION p_option); 假如由于功能升级,需要增加一个功能选项,第一种方案除了修改函数本身,还不得不修改每个函数调用。而第二种方案仅仅需要修改函数本身和结构体的声明。 5.3. 设计和编码的过程中,要有复用性设计和模块化的思维。设计和编码的过程中,要有复用性设计和模块化的思维。 复用性设计,模块化思维就是要程序员在完成任何一个功能模块或函数的时候,要多深入考虑,不要局限在完成当前任务的简单思路上,想想看该模块或函数是否可以脱离这个系统存在,是否可以通过简单的修改参数的方式在其他系统和应用环境下直接引用,这样就能极大避

15、免重复性的开发工作. 如果一个软件研发单位和工作组能够在每一次研发过程中都考虑到这些问题,那么程序员就不会在重复性的工作中耽误太多时间,就会有更多时间和精力投入到创新的代码工作中去。对于软件公司,也无需重复开发同样的功能,避免“重复设计轮子”。 lib_serial.htypedef struct unsigned charbaud_rate; / 波特率 unsigned chardata_len; / 数据位 unsigned charparity_bit; / 校验位 unsigned charstop_bit; / 停止位 . /其它属性COM_OPTION, *P_COM_OPTIO

16、N;/*使用指定属性打开串口*/int open_serial(P_COM_OPTION p_com_option);/*设置串口波特率*/int set_serial_baud_speed(int baud_speed, int serial_fd);.例如: 在公司很多产品中,都有使用串口通信的功能模块。但是虽然已有多个项目使用了串口通信,却没有形成串口通信库。每次打开串口,发送数据等,都是重新编写函数,或拷贝代码后做大量修改,可重用性很低。 应该将串口属性定义为结构体,针对结构体将串口的常用操作如打开串口,设置波特率,收发数据等做成库函数,函数在头文件中声明,并编译成动态或静态库。以后的

17、项目中只需直接包含头文件并链接相关库即可。 模块功能的完善化6. 软件模块结构的改进软件模块结构的改进一个完整的模块应当有以下几部分: 执行规定的功能的部分; 出错处理的部分。当模块不能完成规定的功能时,必须回送出错标志,出现例外情况的原因。 如果需要返回一系列数据给它的调用者,在完成数据加工或结束时, 应当给它的调用者返回一 个结束状态标志。 消除重复功能,改善软件结构1 完全相似:在结构上完全相似,可能只是在数据类型上不一致。此时可以采取完全合并的方法。2 局部相似:找出其相同部分,分离出去,重新定义成一个独立的下一层模块。还可以与它的上 级模块合并。3 模块的作用范围应在控制范围之内 模

18、块的控制范围包括它本身及其所有的从属模块。 模块的作用范围是指模块内一个判定的作用范围,凡是受这个判定影响的所有模块都属于这个判 定的作用范围。 如果一个判定的作用范围包含在这个判定所在模块的控制范围之内,则这种结构是简单的,否 则,它的结构是不简单的。 4 尽可能减少高扇出结构,随着深度增大扇入 如果一个模块的扇出数过大,就意味着该模块过分复杂,需要协调和控制过多的下属模块。应 当适当增加中间层次的控制模块。 避免或减少使用病态联接避免或减少使用病态联接 直接病态联接 即模块A直接从模块B内部取出某些数据,或者把某些数据直接送到模块B内部。 公共数据域病态联接 模块A和模块B通过公共数据域,

19、直接传送或接受数据,而不是通过它们的上级模块。这种方式将使得模块间的耦合程度剧增。它不仅影响模块和模块,而且影响与公共数据域有关联的所有模块。 通信模块联接 即模块A和模块B通过通信模块TABLEIT传送数据。从表面看,这不是病态联接,因为模块和模块B都未涉及通信模块TABLEIT 的内部。然而,它们之间的通信(即数据传送)没有通过它们的上级模块。从这个意义上讲,这种联接是病态的。模块的大小要适中设计功能可预测的模块,但要避免过分受限制的模块 一个功能可预测的模块,不论内部处理细节如何,但对相同的输入数据,总能产生同样的结果。但是,如果模块内部蕴藏有一些特殊的鲜为人知的功能时,这个模块就可能是

20、不可预测的。对于这种模块,如果调用者不小心使用,其结果将不可预测。 为了能够适应将来的变更,软件模块中局部数据结构的大小应当是可控制的,控制流的选择对于调用者来说,应当是可预测的。而与外界的接口应当是灵活的。 软件包应满足设计约束和可移植性,为了使得软件包可以在某些特定的环境下能够安装和运行,对软件包提出了一些设计约束和可移植的要求。 模块的大小要适中 模块的大小,可以用模块中所含语句的数量的多少来衡量。把模块的大小限制在一定的范围之内。通常规定其语句行数在50100左右,保持在一页纸之内,最多不超过500行。 模块的效率,从根本上取决于实现的算法和数据结构。 7.时间与空间效率时间与空间效率

21、 数据结构 与 算法 就是一类数据的表示及其相关的操作。从数据表示的观点来看,存储在数组中的一个有序整数表也是一种数据结构。算法是指对数据结构施加的一些操作。 (1)每一种数据结构与算法都有其时间、空间的开销和收益。当面临一个 新的设计问题时,设计者要彻底地掌握怎样权衡时空开销和算法有效 性的方法。这就需要懂得算法分析的原理,而且还需要了解所使用的 物理介质的特性(例如,数据存储在磁盘上与存储在内存中,就有不 同的考虑)。 (2)与开销和收益有关的是时间空间的权衡。通常可以用更大的时间 开销来换取空间的收益,反之亦然。(3)程序员应该充分地了解一些常用的数据结构与算法,避免不必要的重 复设计工作。 (4)数据结构与算法为应用服务。我们必须先了解应用的需求,再寻找或 设计与实际应用相匹配的数据结构。 根据模块的输入输出依赖程度,构建不同抽象层次。 8. 健壮性与容错性健壮性与容错性 模块的输出依赖性越强(较多的其它模块功能,依赖本模块输出结果或控制信号),则其它模块越

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论