搜索

C语言--“高内聚,低耦合”编程思想

[复制链接]
254|13
 楼主 | 2020-5-31 17:33 | 显示全部楼层 |阅读模式
1、定义

高内聚低耦合,是软件工程中的概念,是判断设计好坏的标准,主要是面向对象的设计,主要是看类的内聚性是否高,耦合度是否低。


使用特权

评论回复
 楼主 | 2020-5-31 17:34 | 显示全部楼层
2、概念
耦合性:也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息。

内聚性:又称块内联系。指模块的功能强度的度量,即一个模块内部各个元素彼此结合的紧密程度的度量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则它的内聚性就越高。

所谓高内聚是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常说的单一责任原则。

对于低耦合,粗浅的理解是:一个完整的系统,模块与模块之间,尽可能的使其独立存在。也就是说,让每个模块,尽可能的独立完成某个特定的子功能。模块与模块之间的接口,尽量的少而简单。如果某两个模块间的关系比较复杂的话,最好首先考虑进一步的模块划分。这样有利于修改和组合。

使用特权

评论回复
 楼主 | 2020-5-31 17:35 | 显示全部楼层
3、如何实现
c语言面向过程,通常使用回调的方法。c++面向对象,要实现高内聚、低耦合,需要使用接口技术。

要做到高内聚低耦合,设计模块时需要做到:
      (1)各个模块之间的功能必须明确;
      (2)各个功能模块间实现的功能不可以有交叉;
      (3)不允许出现模块间的相互调用;
      (4)如果必须出现模块间调用,那么只允许单向调用,即A可以调用B,B不可以调用A。

使用特权

评论回复
 楼主 | 2020-5-31 17:36 | 显示全部楼层
    C语言中常见的形式(1)--函数接口:

尽量避免使用全局变量,不使用全局变量那又如何进行模块间的数据传递呢?例如在别的模块中(例如LED显示)想要知道按键的状态或设置按键的状态,一般的思想是不是定义一个keyFlag全局变量呢?然后再LED显示模块和按键模块中都去直接操作keyFlag,这样就提高了模块间的耦合度;

这样做还有很多弊端,假设哪天需要按键模块引入到别的工程中使用,但是工程中已经定义了全局按键变量为keyStatus或是没有定义,那这个时候我们又怎样去获取按键的状态呢?是改全局名称或重新定义全局变量,是不是都很麻烦?

所以在按键模块中定义两个函数供外部调用就可以了:
  1. u8_t  get_key_status(void)
  2. {
  3.     return  key_flag;
  4. }

  5. void  get_key_status(u8_t  flag)
  6. {
  7.     key_flag = flag;
  8. }
复制代码
需要知道按键状态的模块只需要调用以上两个函数就可以了,并不需要关心按键模块中定义的按键状态的变量名是什么,这样就减少了全局变量的使用!

使用特权

评论回复
 楼主 | 2020-5-31 17:37 | 显示全部楼层

C语言中常见的形式(2)--函数指针:

软件通常有后台日志的记录功能,用log函数实现,主业务用business函数表示:

  1. void log()
  2. {
  3.         printf("Logging...\n");
  4. }


  5. void business()
  6. {
  7.         while(1)
  8.         {
  9.                 sleep(1);
  10.                 printf("Deal Business...\n");
  11.                 log();
  12.         }
  13. }


  14. int main()
  15. {
  16.         business();
  17.         return 0;
  18. }
复制代码


使用特权

评论回复
 楼主 | 2020-5-31 17:38 | 显示全部楼层

现在需要对后台日志功能进行升级,该如何实现?

一般人的想法是这样:再写一个函数log2,然后business中log改为log2,这样不就可以了?

但是你想想,主业务代码怎能轻易改动?因为一个小小的功能而要改变主要的业务代码,这样不是显得智商很捉急?

换一种思路,使用回调:

  1. #include <stdio.h>
  2. #include <unistd.h>

  3. void log1()
  4. {
  5.         printf("1 Logging...\n");
  6. }

  7. void log2()
  8. {
  9.         printf("2 Logging...\n");       
  10. }

  11. void business( void (*f)() )
  12. {
  13.         while(1)
  14.         {
  15.                 sleep(1);
  16.                 printf("Deal Business...\n");
  17.                 f();
  18.         }
  19. }

  20. int main()
  21. {
  22.         business(log1);
  23.         return 0;
  24. }
复制代码

business函数接受一个函数指针,该指针指向的函数没有参数,返回值为void,符合log函数的原型。business中只要f()即可调用相应的函数。


当需要使用log1时,向business传log1、要使用升级后的log2时,传入log2即可。


这样做就business函数并不需要所传入数据的变化,只关心自身的功能就行,具体传入什么数据待用户调用的时候让用户决定,这样就提高了函数的通用性和灵活性。


使用特权

评论回复
| 2020-6-3 15:39 | 显示全部楼层
非常感谢楼主分享

使用特权

评论回复
| 2020-6-3 15:40 | 显示全部楼层
第一次接触这个思想

使用特权

评论回复
| 2020-6-3 15:40 | 显示全部楼层
感谢分享 非常不错

使用特权

评论回复
| 2020-6-3 15:40 | 显示全部楼层
确实可以这样描述

使用特权

评论回复
| 2020-6-3 20:57 | 显示全部楼层
高内聚低耦合  这种思维 在大项目 重要性 是很明显.
谢谢分享!

使用特权

评论回复
| 2020-6-4 16:28 | 显示全部楼层
传递函数指针,干得漂亮

使用特权

评论回复
| 2020-6-21 14:15 | 显示全部楼层
谢谢,学习了

使用特权

评论回复
| 2020-6-21 17:25 | 显示全部楼层
跟JAVA里面构造函数有点类似,一般在构造函数里面,做的第一件事情就是变量的数据传输。不过,偶还是喜欢用全局变量,实在不放心的话就加volatile修饰一下,用起来直观,即使遇到问题,查找原因的话也很方便。

使用特权

评论回复
扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

我要发帖 投诉建议 创建版块 申请版主

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式

论坛热帖

在线客服 快速回复 返回顶部 返回列表