标签:写代码 路径 竞争 win 维护 下载 进一步 环境配置 鼓励
这学期非常有幸选了科大软院孟宁老师的高级软件工程课程。作为具有工程师背景的授课老师,孟宁老师对软件工程拥有非常丰富的工程经验,在课堂上结合Menu源码带我们深入浅出地探讨了此中蕴含的软件工程方法、规范以及软件工程思想。本文就结合自己学习的知识,对Menu源码进行阅读,来浅显地概述我对软件工程方法、规范和思想的理解。
VSCode是一个轻量且强大的编辑代码的神器,强大之处在于提供一个编码界面,以插件的形式提供对各种语言的代码的兼容,本身不带编译工具通过配置调用系统安装的编译工具。本人以VSCode + GCC工具集为主要环境编译调试课程项目Menu案例(源码地址:https://github.com/mengning/menu) ,内容包括:
(1)完成编译和调试环境配置
(2)对模块化设计、可重用接口、线程安全等议题结合代码进行理解和分析
一、VSCode + GCC工具集的编译和调试环境配置
在本文中,本人选择以VSCode + GCC工具集为主要环境编译调试课程项目Menu案例,因此需要VSCode、C/C++插件以及GCC和GDB工具,步骤分别如下:
(一)安装vscode
Windows系统可直接去VSCode官网(地址:https://code.visualstudio.com/#alt-downloads)下载安装即可;
Linux平台可直接打开终端运行命令安装:
sudo apt install ./<file>.deb
(二)安装C/C++插件以支持C/C++代码格式
直接打开已安装好的VSCode,点击左侧的扩展件按钮,然后搜索C/C++,点击第一个的插件的install
(三)安装C/C++的编译调试工具
C/C++插件仅仅是VSCode用来支持C/C++格式的插件,并不具备编译调试功能,需要额外安装编译调试工具,Linux下有GCC、Mac OS下有CLang、Windows下有开源的MinGM以及微软自家Visual Studio系列自带的编译器。我选择在Windows下安装MinGw,下载链接如下:
https://sourceforge.net/projects/mingw-w64/files/Toolchains targetting Win32/Personal Builds/mingw-builds/installer/mingw-w64-install.exe
下载安装后在shell窗口输入gcc -v看看是否安装成功
gcc -v
(四)配置json文件将VSCode和GCC工具连接
打开一个C++工程,运行一下,就会自动创建三个json文件:tasks.json、launch.json、c_cpp_properties.json,分别用于编译配置、调试配置以及编译路径和智能感知设置
(1)tasks.json
{ "version": "2.0.0", "tasks": [{ "label": "gcc build active file", "type": "shell", "command": "gcc", "args": [ "-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}" ], "group": { "kind": "build", "isDefault": true } }], }
(2)launch.json
{ "version": "0.2.0", "configurations": [{ "name": "(gdb) 启动", "type": "cppdbg", "request": "launch", "program": "${fileDirname}/${fileBasenameNoExtension}", "cwd": "${workspaceFolder}", "preLaunchTask": "gcc build active file", }] }
(3)c_cpp_properties.json(不是很重要,会自动配置的)
{ "configurations": [ { "name": "Win32", "includePath": [ "${workspaceFolder}/**", "C:/Software_sys/mingw-w64/x86_64-8.1.0-posix-seh-rt_v6-rev0/mingw64/x86_64-w64-mingw32/include" ], "defines": [ "_DEBUG", "UNICODE", "_UNICODE" ], "windowsSdkVersion": "10.0.17763.0", "compilerPath": "C:/Software_user/Microsoft Visual Studio/2019/Enterprise/VC/Tools/MSVC/14.20.27508/bin/Hostx64/x64/cl.exe", // "compilerPath": "C:/Software_sys/mingw-w64/x86_64-8.1.0-posix-seh-rt_v6-rev0/mingw64bin/gcc.exe", "cStandard": "c11", "cppStandard": "c++17", "intelliSenseMode": "msvc-x64" } ], "version": 4 }
二、Menu源码理解和分析
Menu项目介绍
实现一个命令行的菜单小程序,最终目标是完成一个通用的命令行菜单子系统便于在不同项目中重用。从源码来看,并不是直接展示一个大工程,而是划分为一个个文件夹,后一个文件夹是前一个文件夹的代码内容的迭代,具体是从helloworld开始不断迭代调试使代码越来越像一个命令行菜单小程序。孟老师的观点是做实际项目并不鼓励一开始就从头开始写代码,而是找已有类似项目做对比分析,对开源代码做逆向工程和再工程,对项目有深刻理解的基础上,再考虑是从头构建还是维护一个已有的项目来达成目标。对孟老师的观点,我是非常赞同的,结合我自身的学习经历,发现初学者基本是经验不足的、写的代码既不美观也很难重用、经常犯重复造轮子的错误,此时应去参考别人的优秀代码、先学习进步才去创新会更容易成长。
(一)从hello world到menu的while循环读取打印信息
左边hello.c不停打印hello world,这证明添加的命令行菜单小程序的主循环结构是可以工作的,就这样每添加一点代码就要编译运行一下,不断迭代及时发现编码错误,而不是一次写很多代码积累很多编码错误。然后扩展到menu.c,给我们的小程序一个接收命令的变量 cmd。为了能够有更直观的效果,我们将输出打印换为我们所输入的变量。
(二)给menu添加命令
通过条件语句判断输入的命令内容,跟不同字符串常量匹配,来判断进入哪一个分支。
(三)添加必要的注释,比如文件头部注释,并养成习惯
代码风格的原则:简明、易读、无二义性。编写代码,尤其是团队合作编写代码时,尤其要注重代码规范,例如起的函数名、变量名要通俗易懂、代码结构约定统一、在必要的地方要加上注释。文件头部注释是有不同风格的,一般是精细程度越高、应用范围越窄,尤其是工程教学中,注释的内容非常详细。这是因为工程教学要尽可能注释更多让学生等初学者不对代码产生二义性理解,同时也使学生养成良好习惯,严格学习才能在职业竞争中有优势。
(四)尽量不要在main函数写次级功能代码,而是封装函数
在编写main函数逻辑时,任何只要觉得功能太繁杂都尽量不要写在main函数里面。main函数就是执行主逻辑的地方,不要有太多其他次级逻辑。我个人认为,main函数里面代码行数不要超过10行,尽可能地在main外面封装成一个个函数模块,尤其是那些需要复用的功能更需要封装函数。
(五)不要让一个代码文件内代码行数过多,必要时编写自己的头文件进一步模块化
menu.c是主函数文件,只负责主逻辑;linklist.h是封装函数以及其他一些变量之类的声明,一般不会具体定义封装函数的;linklist.c是对linklist.h声明的函数的具体实现。menu.c和linklist.c就是通过linklist.h连接起来的。这种方法其实是软件模块化的高级实现,广泛运用于大型的软件工程里面。
模块化软件设计的方法如果应用的比较好,最终每一个软件模块都将只有一个单一的功能目标,并相对独立于其他软件模块,使得每一个软件模块都容易理解容易开发。从而整个软件系统也更容易定位软件缺陷bug,因为每一个软件缺陷bug都局限在很少的一两个软件模块内。而且整个系统的变更和维护也更容易,因为一个软件模块内的变更只影响很少的几个软件模块。因此,软件设计中的模块化程度便成为了软件设计有多好的一个重要指标,一般我们使用耦合度(Coupling)和内聚度(Cohesion)来衡量软件模块化的程度。
(六)当不方便直接运行整个工程而是仅仅想测试其中某些功能是否正常时,适合且有必要编写测试程序
(七)给Linktable增加Callback方式的接口
给Linktable增加Callback方式的接口,需要两个函数接口,一个是call-in方式函数,如SearchLinkTableNode函数,其中有一个函数作为参数,这个作为参数的函数就是callback函数,如代码中Conditon函数:
(1)具体调用时,传进去的Callback函数是SearchConditon函数:
(2)SearchConditon函数具体定义如下:
(3)而相应的形参Callback函数是Condition函数
(八)可重入函数与线程安全
可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误。相反,不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在代码的关键部分禁用中断)。可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。可重入函数要么使用局部变量,要么在使用全局变量时保护自己的数据。
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行读写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
可重入的函数不一定是线程安全的,可能是线程安全的也可能不是线程安全的;可重入的函数在多个线程中并发使用时是线程安全的,但不同的可重入函数(共享全局变量及静态变量)在多个线程中并发使用时会有线程安全问题;不可重入的函数一定不是线程安全的
(九)大规模软件工程中,常常需要划分为一个个子系统并为此子系统留出接口
menu作为一个子系统会用在不同项目中,应该给menu子系统设计可重用的接口,但menu子系统不像Linktable链表,Linktable链表是一个非常基础的软件模块,重用的机会非常多,应用的场景也非常多,而menu子系统的重用机会和应用场景都比较有限,我们没有必要花非常多的心思把接口定义的太通用,通用往往意味着接口的使用不够直接明了。所以在menu子系统的接口设计我们的原则是“够用就好——不要太具体,也不要太通用”。
此时应把main函数修改为接口形式,并编写测试程序来确保以此为接口的子系统功能正常。
(十)每个子系统为了相对的封装与独立,有必要自带Makefile工程文件
Makefile工程文件是在Unix类操作系统环境下非常常见,是用于工程项目组织的一种方式。很多IDE集成开发环境一般会自动生成一个类似的工程文件。Makefile使用起来非常灵活,可以像写Shell脚本一样手写,也可以使用autoconf和automake自动生成。Makefile一般从第一个目标还是执行,第一个目标一般定义为all,在项目目录下执行make命令即会自动从Makefile的目标all开始执行,即是执行gcc这一条命令。
三、总结
通过对孟老师的Menu源码案例的学习,我主要学习到了如何在大型软件工程中注意代码规范、代码增长、代码注释、代码模块化、代码测试、代码接口等方法与工具,总结如下:
(一)函数名变量名要简明易懂、括号缩进要统一、留出必要的空格与空行、养成多加规范注释的习惯等
(二)代码内容过多可函数封装、模块化,不仅显得有层次还能功能复用
(三)常常编写测试程序以保证模块不报bug、封装模块留出接口以模块复用
以上就是本人浅显之见,若有错误还望各位指出并纠正。再次感谢孟老师的授课,让我学到了很多软件工程的思想,我将继续学习,继续进步!
心得体会-02-Menu源码案例蕴含的软件工程方法、规范及思想
标签:写代码 路径 竞争 win 维护 下载 进一步 环境配置 鼓励
原文地址:https://www.cnblogs.com/wkq-blog/p/13952312.html