分析Menu项目中的软件工程

本博客基于孟宁老师的Menu项目,项目源码地址:https://github.com/mengning/menu

参考资料:https://gitee.com/mengning997/se/blob/master/README.md#代码中的软件工程](https://gitee.com/mengning997/se/blob/master/README.md#代码中的软件工程)

一、环境配置

进行由于这个项目是c++编写的,所以我们要先配置c++编译环境。

1.安装配置VsCode

  1. 从官网下载VsCode,下载地址:https://code.visualstudio.com/Download
  2. 安装VsCode
  3. 下载C/C++插件,位置如下图

  1. 配置lanuch.json

    // https://code.visualstudio.com/docs/cpp/launch-json-reference
    {
        "version": "0.2.0",
        "configurations": [{
            "name": "(gdb) Launch", // 配置名称,将会在启动配置的下拉菜单中显示
            "type": "cppdbg", // 配置类型,cppdbg对应cpptools提供的调试功能;可以认为此处只能是cppdbg
            "request": "launch", // 请求配置类型,可以为launch(启动)或attach(附加)
            "program": "${fileDirname}/${fileBasenameNoExtension}.exe", // 将要进行调试的程序的路径
            "args": [], // 程序调试时传递给程序的命令行参数,一般设为空即可
            "stopAtEntry": false, // 设为true时程序将暂停在程序入口处,相当于在main上打断点
            "cwd": "${workspaceFolder}", // 调试程序时的工作目录,此为工作区文件夹;改成${fileDirname}可变为文件所在目录
            "environment": [], // 环境变量
            "externalConsole": true, // 使用单独的cmd窗口,与其它IDE一致;为false时使用内置终端
            "internalConsoleOptions": "neverOpen", // 如果不设为neverOpen,调试时会跳到“调试控制台”选项卡,你应该不需要对gdb手动输命令吧?
            "MIMode": "gdb", // 指定连接的调试器,可以为gdb或lldb。但我没试过lldb
            "miDebuggerPath": "D:\\Environment\\MinGW\\mingw64\\bin\\gdb.exe", // 调试器路径,Windows下后缀不能省略,Linux下则不要
            "setupCommands": [
                { // 模板自带,好像可以更好地显示STL容器的内容,具体作用自行Google
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": false
                }
            ],
            "preLaunchTask": "Compile" // 调试会话开始前执行的任务,一般为编译程序。与tasks.json的label相对应
        }]
    }
    
  2. 配置tasks.json

    // https://code.visualstudio.com/docs/editor/tasks
    {
        "version": "2.0.0",
        "tasks": [
            {
                "label": "Compile", // 任务名称,与launch.json的preLaunchTask相对应
                "command": "gcc", // 要使用的编译器,C++用g++
                "args": [
    				"${fileDirname}\\menu.c",
    				"${fileDirname}\\linktable.c",
    				"${fileDirname}\\menu.h",
    				"${fileDirname}\\linktable.h",
    				"${fileDirname}\\test.c",
                    "-o", // 指定输出文件名,不加该参数则默认输出a.exe,Linux下默认a.out
                    "${fileDirname}/${fileBasenameNoExtension}.exe",
                    "-g", // 生成和调试有关的信息
                    "-m64", // 不知为何有时会生成16位应用而无法运行,加上此条可强制生成64位的
                    "-Wall", // 开启额外警告
                    "-static-libgcc", // 静态链接libgcc,一般都会加上
                    "-fexec-charset=GBK" // 生成的程序使用GBK编码,不加这条会导致Win下输出中文乱码;繁体系统改成BIG5
                    // "-std=c11", // 要用的语言标准,根据自己的需要修改。c++可用c++14
                ], // 编译的命令,其实相当于VSC帮你在终端中输了这些东西
                "type": "process", // process是把预定义变量和转义解析后直接全部传给command;shell相当于先打开shell再输入命令,所以args还会经过shell再解析一遍
                "group": "build",
                "presentation": {
                    "echo": true,
                    "reveal": "always", // 执行任务时是否跳转到终端面板,可以为always,silent,never。具体参见VSC的文档
                    "focus": false, // 设为true后可以使执行task时焦点聚集在终端,但对编译C/C++来说,设为true没有意义
                    "panel": "shared" // 不同的文件的编译信息共享一个终端面板
                },
                "problemMatcher": "$gcc" // 捕捉编译时终端里的报错信息到问题面板中,修改代码后需要重新编译才会再次触发
                // 本来有Lint,再开problemMatcher就有双重报错,但MinGW的Lint效果实在太差了;用Clang可以注释掉
            },
            {
                "type": "shell",
                "label": "C/C++: gcc.exe build active file",
                "command": "D:\\Environment\\MinGW\\mingw64\\bin\\gcc.exe",
                "args": [
                    "-g",
                    "${file}",
                    "-o",
                    "${fileDirname}\\${fileBasenameNoExtension}.exe"
                ],
                "options": {
                    "cwd": "${workspaceFolder}"
                },
                "problemMatcher": [
                    "$gcc"
                ],
                "group": {
                    "kind": "build",
                    "isDefault": true
                }
            }
        ]
    }
    

2.下载配置MinGW

  1. MinGW下载地址:https://sourceforge.net/projects/mingw-w64/files/

  2. 下载好后解压到你想放的位置,如:D:\MinGW

  3. 配置环境变量,在Path变量后面加上:D:\MinGW\mingw64\bin

  4. 打开控制台,在其中输入c++指令,如果提示参数不足,则说明配置成功。否则再到前面步骤中看看哪里配置错了。

二、对Menu项目的分析

项目运行截图:

1.代码风格

  1. 注释风格

    代码中注释整洁易读,信息完善。

​ 如上图,给出了版权、作者、版本、描述等相关信息。同时保持了美观,让人看的十分舒服。

​ 且项目中的所有注释都是英文的,这就使得项目的编码保持为ASCII码,防止编码错误出现乱码。

  1. 命名

    代码中的命名十分规范,简洁易懂,即便没有注释,也能通过函数、变量等的命名直接理解代码。

  2. 错误处理

    程序的主要功能(80%的工作)大约仅用20%时间,而错误处理(20%的工作)却要80%的时间。

2.模块化

​ 模块化设计,简单地说就是程序的编写不是开始就逐条录入计算机语句和指令,而是首先用主程序、子程序、子过程等框架把软件的主要结构和流程描述出来,并定义和调试好各个框架之间的输入、输出链接关系。逐步求精的结果是得到一系列以功能块为单位的算法描述。以功能块为单位进行程序设计,实现其求解算法的方法称为模块化。模块化的目的是为了降低程序复杂度,使程序设计、调试和维护等操作简单化。改变某个子功能只需相应改变相应模块即可。

  软件设计中的模块化程度便成为了软件设计有多好的一个重要指标,一般我们使用耦合度(Coupling)和内聚度(Cohesion)来衡量软件模块化的程度。耦合度是指软件模块之间的依赖程度,一般可以分为紧密耦合(Tightly Coupled)、松散耦合(Loosely Coupled)和无耦合(Uncoupled)。一般在软件设计中我们追求松散耦合。内聚度是指一个软件模块内部各种元素之间互相依赖的紧密程度。理想的内聚是功能内聚,也就是一个软件模块只做一件事,只完成一个主要功能点或者一个软件特性(Features)。

在本项目中,整个项目被分为三个模块:linkable, menu, test。且数据结构和它的业务处理分离开了,并设计了合适的接口供不同模块之间相互调用。

3.接口

​ 接口就是互相联系的双方共同遵守的一种协议规范,在我们软件系统内部一般的接口方式是通过定义一组API函数来约定软件模块之间的沟通方式。换句话说,接口具体定义了软件模块对系统的其他部分提供了怎样的服务,以及系统的其他部分如何访问所提供的服务。

本项目中使用的大量接口供各模块调用,如下图:

4.线程安全

​ 线程(thread)是操作系统能够进行运算调度的最小单位。它包含在进程之中,是进程中的实际运作单位。一个线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。一般默认一个进程中只包含一个线程。

​ 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行读写操作,一般都需要考虑线程同步,否则就可能影响线程安全。

在本项目中,通过对信号量mutex加锁和解锁,保证了在删除节点的过程中的线程安全。

三、总结

好的代码需要尽量遵守代码规范,使得代码能比较容易被读懂,也能让以后自己在改代码的时候更加轻松。

posted @ 2020-11-09 21:01  dyc123  阅读(116)  评论(0编辑  收藏  举报