基于Menu学软件工程

通过高级软件工程孟宁老师关于Menu项目的讲解,使得我对项目的开发过程有了一定的了解和感悟。本文基于Menu项目根据课上老师讲解以及自己的分析展开,着重体现项目中所蕴涵的软件工程思想。

 参考文献:代码中的软件工程

      读行学视频教程


 (一)环境配置

1、首先下载VSCode提供的C/C++扩展包

2、下载MinGW并配置环境详情参考如下博文:

https://blog.csdn.net/wxh0000mm/article/details/100666329

3、配置C++环境,分别创建launch.json和tasks.json文件。

    "version": "0.2.0",
    "configurations": [
        
        {
            "name": "(gdb) 启动",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}\\${fileBasenameNoExtension}.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": true,
            "MIMode": "gdb",
            "miDebuggerPath": "D:\\MinGW\\mingw64\\bin\\gdb.exe",  
            "setupCommands": [
                {
                    "description": "为 gdb 启用整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],"preLaunchTask": "task g++"
        }
    ]
}
{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "shell",
            "label": "task g++",    
            "command": "D:\\MinGW\\mingw64\\bin\\g++.exe",
            "args": [
                "-g",
                "${file}",
                "-o",
                "${fileDirname}\\${fileBasenameNoExtension}.exe"
            ],
            "options": {
                 "cwd": "D:\\MinGW\\mingw64\\bin"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": "build"
         }
     ]
}
 

      至此,环境配置完毕。

(二)Menu项目的开发进程的分析

se_code文件夹下对应lab1-lab7.2,对应项目开发的各种版本。下面分析每个版本的内容以及使用到的软件工程方法。

1、lab1中的menu.c文件是菜单的简要说明。

2、lab2中的menu.c文件对功能进行了扩展,使得能够不断处理不同的命令。

3、lab3体现了软件工程中模块化设计的方法。

每新增一个功能,只要在函数链表上新增一个结点即可,体现了开闭原则--软件实体对扩展是开放的,但对修改是关闭的,即在不修改一个软件实体的基础上去扩展其功能。

将数据结构的定义和功能实现分别放在.h和.c文件中,符合模块化设计思想。

4、lab4设计了linktable模块,用于用户进行自定义操作。

5、lab5.1中设计了可重用接口,定义了一个SearchCondition函数,接着就在FindCmd函数中调用了SearchLinkTableNode函数,

在lab5.2中将全局变量改为参数传入,使FindCmd和SearchCondition两个模块耦合性降低

6、lab6进行线程安全分析,对于链表节点进行写操作的时候,需要对临界区进行加锁,写完毕后进行解锁操作。而对于链表节点进行读操作时,表面看不会影响链表的结构。但是当某个线程进行读操作时,可能有另外的线程进行写操作,若进行写操作时对链表进行了增加或者删除,那么读链表的线程可能会出现不一致的情况。如图所示,删除节点时需要进行加锁和解锁操作。

7、 lab7给Menu定义一系列可重用接口。下图是menu子系统的接口函数:MenuConfig函数用来配置菜单的命令名字、命令描述和命令操作函数,而ExecuteMenu函数用来执行函数。

(三)软件工程思想分析

1、模块化设计

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

(1) 扩展新功能时,不需要修改项目的主函数。

static tDataNode head[] = 
{
    {"help", "this is help cmd!", Help,&head[1]},
    {"version", "menu program v1.0", NULL, &head[2]},
    {"quit", "Quit from menu", Quit, NULL}

};
 

(2) 将数据结构的定义和功能实现分别放在.h和.c文件中。

 
 //linklist.h用来存放数据结构的定义
  
  typedef struct DataNode
  {
      char*   cmd;
      char*   desc;
      int     (*handler)();
      struct  DataNode *next;
  } tDataNode;
 
 /* find a cmd in the linklist and return the datanode pointer */
 tDataNode* FindCmd(tDataNode * head, char * cmd);
 /* show all cmd in listlist */
 int ShowAllCmd(tDataNode * head);
 
 
 
//linklist.c用来存放对数据结构操作的具体实现
#include <stdio.h>
#include <stdlib.h>
#include "linklist.h"


tDataNode* FindCmd(tDataNode * head, char * cmd)
{
    if(head == NULL || cmd == NULL)
    {
        return NULL;        
    }
    tDataNode *p = head;
    while(p != NULL)
    {
        if(!strcmp(p->cmd, cmd))
        {
            return p;
        }
        p = p->next;
    }
    return NULL;
}

int ShowAllCmd(tDataNode * head)
{
    printf("Menu List:\n");
    tDataNode *p = head;
    while(p != NULL)
    {
        printf("%s - %s\n", p->cmd, p->desc);
        p = p->next;
    }
    return 0; 
}
 

2、可重用接口

模块化设计后需要对接口进行细化,一旦一个接口太大,则需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。使用多个专门的接口,而不使用单一的总接口。每一个接口应该承担一种相对独立的角色必须满足单一职责原则,将一组相关的操作定义在一个接口中,且在满足高内聚的前提下,接口中的方法越少越好。

接口就是互相联系的双方共同遵守的一种协议规范,在我们软件系统内部一般的接口方式是通过定义一组API函数来约定软件模块之间的沟通方式。换句话说,接口具体定义了软件模块对系统的其他部分提供了怎样的服务,以及系统的其他部分如何访问所提供的服务。接口具体定义了软件模块对系统的其他部分提供了怎样的服务,以及系统的其他部分如何访问所提供的服务。在面向过程的编程中,接口一般定义了数据结构及操作这些数据结构的函数;而在面向对象的编程中,接口是对象对外开放的一组属性和方法的集合。

(1) lab5.1可重用接口设计

 
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode))
{
    if(pLinkTable == NULL || Conditon == NULL)
    {
        return NULL;
    }
    tLinkTableNode * pNode = pLinkTable->pHead;
    while(pNode != pLinkTable->pTail)
    {    
        if(Conditon(pNode) == SUCCESS)
        {
            return pNode;				    
        }
        pNode = pNode->pNext;
    }
    return NULL;
}

 

(2) menu子系统的接口

 
/* add cmd to menu */
int MenuConfig(char * cmd, char * desc, int (*handler)());

/* Menu Engine Execute */
int ExecuteMenu();
 

3、线程安全

可重入函数和不可重入函数是线程安全相关的重要的概念,可重入函数可以由多于一个任务并发使用,可以在任意时刻被中断,稍后再继续运行,不会丢失数据。相反,不可重入函数不能由超过一个任务所共享,除非能确保函数的互斥。

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

函数的可重入性和线程安全之间的关系:可重入的函数不一定是线程安全的,可能是线程安全的也可能不是线程安全的;可重入的函数在多个线程中并发使用时是线程安全的,但不同的可重入函数(共享全局变量及静态变量)在多个线程中并发使用时会有线程安全问题; 不可重入的函数一定不是线程安全的。

(1)linktable在结构体中定义线程安全的互斥锁

 
struct LinkTable
{
    tLinkTableNode *pHead;
    tLinkTableNode *pTail;
    int            SumOfNode;
    pthread_mutex_t mutex; //用于线程安全的互斥锁
};
 

(2)DeleteLinkTable删除节点前后需要进行加锁和解锁。

 
int DeleteLinkTable(tLinkTable *pLinkTable)
{
    if(pLinkTable == NULL)
    {
        return FAILURE;
    }
    while(pLinkTable->pHead != NULL)
    {
        tLinkTableNode * p = pLinkTable->pHead;
        pthread_mutex_lock(&(pLinkTable->mutex));
        pLinkTable->pHead = pLinkTable->pHead->pNext;
        pLinkTable->SumOfNode -= 1 ;
        pthread_mutex_unlock(&(pLinkTable->mutex));
        free(p);
    }
    pLinkTable->pHead = NULL;
    pLinkTable->pTail = NULL;
    pLinkTable->SumOfNode = 0;
    pthread_mutex_destroy(&(pLinkTable->mutex));
    free(pLinkTable);
    return SUCCESS;		
}
 

(四)心得体会

经过孟宁老师高级软件工程课的学习,课下通过跟踪Menu的开发过程,利用软件工程思想分析,深刻理解了模块化设计、可重用接口、线程安全等内容。模块化设计的方法能够使得项目各个模块结构更加清晰,提高项目的可维护性。可重用接口降低了软件模块和软件模块之间的耦合度,能够实现可维护性复用;线程安全让我们的程序可以并发执行却不会发生数据错误。同时通过项目的学习,了解到编程规范对于项目的协同合作起到很重要的作用。不仅能使得代码风格更加规范,同时进行合理的注释有利于推动项目的开发。相信在以后的软件项目开发中,孟老师所传达的软件工程思想能够很好的为我所用。

posted @ 2020-11-10 11:33  姚劲嵩  阅读(214)  评论(0编辑  收藏  举报