C语言代码质量与架构调整(一)

我的技术理想

1、最高自己开源一个软件,最低的是在GitHub上的开源软件项目有过贡献。

2、出一本书,最低的是一本书籍做过贡献。

3、理解业务、市场、客户-》理解当前代码架构、逻辑-》需求分解、设计-》代码编写-》测试-》发布-》持续业务迭代及重构,减少因设计失误而导致徒劳无益的反复重构

相关书籍   

第一阶段:《代码大全》、《C现代编程》、《重构-改善现有代码》、《代码整洁之道》、《编写可读代码的艺术》

第二阶段:《修改代码的艺术》《重构与模式》、《敏捷软件开发》、《UNIX编程艺术》、《scrum敏捷软件开发》、《C++代码整洁之道》

软件设计:《C程序设计新思维》、《程序设计实践》、《设计模式解析第二版》、《设计模式的艺术之道》入门

软件工程:《人月神话》、《人件》、《大教堂和集市》、《google软件工程》、《现代软件工程-如何高效构建软件》

测试书籍:《测试驱动的嵌入式C语言开发》、《单元测试的艺术》、《C++程序设计与实践:测试驱动开发》、《effective debugging:软件和系统调试的66个有效方法》

操作系统:《操作系统之哲学原理》、《操作系统导论》

科普:《编码-隐匿在计算机软硬件背后的语言》、《程序员修炼之道:从小工到专家》、《程序员的自我修养》、《编程人生》

架构:《架构模式、特征及实践指南》,《架构整洁之道》,《演进式架构》,架构之美,《从程序员到架构师》,软件架构,架构师应该知道的37件事,从零开始学架构。

       编程必须遵循一定的代码规范,毕竟代码虽然写的是给机器运行,但是看的是人,需要人去维护。虽然一开始代码做了很好的架构调整,但是随着时间和业务的发展,一些代码已经不符合原有的规范,维护起来也非常苦难。需要有一些可衡量的手段对代码质量进行提供和架构调整。

       这里讲述的调整并不涉及到内存和运行性能的优化,或者涉及到算法和数据结构的调整。

一、代码质量-圈复杂度

1.1 、背景

当项目规模达到一定的程度,比如达到十万行的代码量。那么项目肯定存在有些类特别大,方法特别多、特别长。

以上因素会导致什么后果呢?

  • 一个类没有做到单一指责,后期对这个类改动会导致其他功能出现Bug。
  • 代码阅读性较差,维护困难。

利用圈复杂度可以衡量出函数的复杂程度,从而利用一些手段对函数进行优化。

1.2、手段

可以直接降低圈复杂度的9种重构技术(针对结构化编程,C语言是属于结构化编程,结构化编程指的就是if for while等):
•Composing Methods(重新组织你的函数)

Extract Method(提炼函数)

Substitute Algorithm(替换你的算法)

•Simplifying Conditional Expressions(简化条件表达式)

Decompose Conditional(分解条件式)

Consolidate Conditional Expression(合并条件式)

Consolidate Duplicate Conditional Fragments(合并重复的条件片断)

Remove Control Flag(移除控制标记)

•Making Method Calls Simpler(简化函数调用)

Separate Query from Modifier(将查询函数和修改函数分离)

Parameterize Method(令函数携带参数)

Replace Parameter with Explicit Methods(以明确函数取代参数)

1.3、总结

  • if else ,switch case 等判断语句会增加圈复杂度,导致代码复杂,不方便维护。
  • 以后写代码按照制定的代码规范来,同时避免写 if,for 多层嵌套的代码。
  • 写完代码可以用圈复杂度来检测代码的质量,圈复杂度高的代码需要重构。
  • 圈复杂度只是衡量代码判定结构的复杂程度,不代表圈复杂度低的代码质量就好,但是圈复杂度高的代码肯定不好。

参考网址

https://www.jianshu.com/p/a21cc7579691

二、代码架构调整

      如同一份好的代码首先要有很好的可读性,并且能够达到松耦合,高内聚。这样一份代码就是好的代码。同样,一个好的代码架构首先要能够便于阅读理解,只需要知道模块名称就知道是做什么的,什么东西应该放在哪里,其次,一个好的代码架构应该能够通过自身的规则,帮助开发人员更容易的写出松耦合,高内聚的代码。
       由此可见,对于一个项目,一个工程来说,无论什么样的代码架构设计,只要能够达到上面的目标就可以了。并且随着项目的膨胀,代码架构也会有着不断地调整以适应现有的项目规模。

       还有一些原则比如:模块化、轻耦合、无共享架构;减少各个组件之前的依懒、注意服务之间依赖所有造成的链式失败及影响等。即便如此,我们也不可能完全避免耦合,它总是会出现在某些场景下。这就需要我们提取一些抽象层将服务之间的交互定在契约上来避免复杂,提升灵活性。这就需要我们有一种辨别能力,能够找到那些必须放在一起来做处理而不能拆解的功能。如果这些功能是值得放在一起的,那我们就可以将它独立成一个微服务,遵循高聚合的设计原因。

参考网址

代码架构设计-2.常用的两种web service代码架构_慕课手记

2.1、C语言设计模式:

实际C也可以实现面向对象编程,只要能够活用设计模式,就可以极大的改善程序结构,使程序各部分解耦,完全独立工作。主要是使用结构体实现C++里面面向对象的继承、封装、多态

从软件设计层面来说,一般来说主要包括三个方面:

    (1)软件的设计受众,是小孩子、老人、女性,还是专业人士等等;
    (2)软件的基本设计原则,以人为本、模块分离、层次清晰、简约至上、适用为先、抽象基本业务等等;
    (3)软件编写模式,比如装饰模式、责任链、单件模式等等。

C语言和设计模式(总结篇)_嵌入式-老费的博客-CSDN博客_c语言设计模式

三、命名技巧

1、变量取名一键直达——Codelf

CODELF

参考网址:变量取名一键直达——Codelf - 简书

四、C语言高级编程用法

        利用下面所述的方法,可以是C语言更加具备面向对象编程,但同时也会使代码的阅读难度增加。因此在考虑重构时,应该小心使用下面的技巧,一般建议使用的场景是在可以对一部分的功能进行了很高的抽象封装,对外可以隐藏这些细节。

其实这里说到的C语言高级用法,其实也是类似于结构体,或者类似于sizeof一样的函数。

参考来源:C语言笔记(结构体与offsetof、container_of之前的关系) - 小清奈 - 博客园

4.1、typeof

typeof关键字是C语言中的一个新扩展,这个特性在linux内核中应用非常广泛。

1,表达式的的例子:
        typeof(x[0](1)
        这里假设x是一个函数指针数组,这样就可以得到这个函数返回值的类型了。
        如果将typeof用于表达式,则该表达式不会执行。只会得到该表达式的类型。
        以下示例声明了int类型的var变量,因为表达式foo()是int类型的。由于表达式不会被执行,所以不会调用foo函数。
            extern int foo();
            typeof(foo()) var;

2,参数的例子:
        typeof(int *) a,b;
            等价于:
            int *a,*b;

4.2、offsetof

offsetof宏的分析: #define offsetof(TYPE, MEMBER)  ((int) &((TYPE *)0)->MEMBER)
 1、功能:返回结构体元素的相对结构体首地址的偏移
 2、参数:TYPE是结构体类型,MEMBER是结构体中一个元素的元素名

 3、分析:
    (1) (TYPE *)0;  将0转换成结构体指针;
    (2) ((TYPE *)0)->MEMBER; 使用指针方式访问结构体中元素
    (3) &(((TYPE *)0)->MEMBER);  取结构体中元素的地址
    (4) (int) &(((TYPE *)0)->MEMBER); 转换成int型返回

#define offsetof(TYPE, MEMBER)  ((int) &((TYPE *)0)->MEMBER)

struct mystruct
{
    int a;
    char b;
    double c;
};
int adr_a = offsetof(struct mystruct, b); // adr_a = 4

4.3、container_of

详解:

#define container_of(ptr, type, member) ({  \
   const typeof(((type *)0)->member) * __mptr = (ptr); \
   (type *)((char *)__mptr - offsetof(type, member));})

 1、功能:返回整个结构体变量的指针
 2、参数:ptr是指向结构体中一个元素的指针;type是结构体类型;member是结构体中一个元素的元素名

 3、分析:
    (1) typeof(((type *)0)->member); 获取结构体中一个元素的类型;s1.c 的类型是double
    (2) const typeof(((type *)0)->member) * __mptr = (ptr); 
        就可以理解为:
        const double * __mptr = (ptr);//__mptr指向ptr处
    (3) (char *)__mptr - offsetof(type, member); // 结构体其中一个元素的地址 - 该元素相对结构体首地址的偏移
    (4) (type *)((char *)__mptr - offsetof(type, member)); // 转换成该结构体类型的指针返回

#define container_of(ptr, type, member) ({  \
   const typeof(((type *)0)->member) * __mptr = (ptr); \
   (type *)((char *)__mptr - offsetof(type, member));})

typedef struct mystruct
{
    int a;
    char b;
    double c;
}MyS1;
struct mystruct s1;
MyS1 *ps = NULL;
double *p = &s1.c;
printf("&s1 = %p.\n" ,&s1);

ps = container_of(p, MyS1, c);
printf("ps  = %p.\n" ,ps);

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值