学习了孟老师的这几节课程,我学习了如何搭建一个简单的命令行menu小程序,从最简单的程序开始,一步步的根据软件工程的一般规律,进行逐步开发、完善,最终实现了一个比较通用的menu程序,可以让别的开发者进行作为内嵌子程序调用执行。经过这段时间的学习,收益匪浅,于是将一些学到的东西总结如下。

  为了menu小程序的开发,首先需要配置VSCOde 的C++环境,因为VScode不带C++的编译器。

1 编译和环境配置

  首先从官网下载MinGW-w64,下载Install安装程序,安装过程十分简单,一路next即可。安装完毕之后通过cmd执行gcc -v查看安装是否成功:

  安装完成之后我们打开VScode,在扩展部分添加C/C++插件:

  之后打开文件夹,在项目根目录下调用终端程序,输入code.命令,即可生成三个配置文件task.json,launch.json,c_cpp_propertities.json,我们修改task.json中的command项和option,将之前的mingw64的目录复制到如下部分即可:

"command": "D:\\2Software\\mingw64\\bin\\g++.exe",
"options": {
"cwd": "D:\\2Software\\mingw64\\bin"
}

  至此,环境配置成功,可以运行menu小程序。

2 软件工程一般原理分析

  2.1  模块化设计

  模块化设计是软件工程开发中的指导思想,是指把整个项目分成相对独立的模块,使每一个模块可以被独立的进行设计和开发。它的本质是软件开发中的关注点分离,把大问题分解成小问题,再逐个击破,实质就是分而治之的思想。

  模块化设计可以降低系统中的耦合度,可以进行更好的扩展和可重用。体现在menu项目中,就是数据结构和菜单业务进行分离处理,在逻辑上进行了切分,同时将接口的声明和实现放在不同的文件中,具体的分析详见代码头部的注释:

linklist.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接口的具体实现过程,可以不关注,直接调用即可

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;
}
//main函数,易于扩展,只需要在head数组添加新功能即可,同时在业务逻辑不涉及具体的数据结构,只传入指针,那么下次不使用链表的时候也可以重用。p->handler也有多态的思想在里面:

static tDataNode head[] =
{
{"help", "this is help cmd!", Help,&head[1]},
{"version", "menu program v1.0", NULL, NULL}
}; main()
{
/* cmd line begins */
while(1)
{
char cmd[CMD_MAX_LEN];
printf("Input a cmd number > ");
scanf("%s", cmd);
tDataNode *p = FindCmd(head, cmd);
if( p == NULL)
{
printf("This is a wrong cmd!\n ");
continue;
}
printf("%s - %s\n", p->cmd, p->desc);
if(p->handler != NULL)
{
p->handler();
} }
}

  2.2  可重用接口

  可重用接口这部分是使接口进行通用化,把linklist接口改为更为通用的listable接口,增加了代码的可重用性。分析见代码的顶层注释:

//linktable.h文件
//LinktableNode结构体只保留了最基本的遍历功能,具体的data数据并没有包含,这是因为用户可以自己添加自己所需要的数据,而linktable.h这个通用接口只需要实现最基本的遍历功能即可,无需关心数据,只需关心遍历这一个逻辑,这样就使接口更通用,可重用性更高。 typedef struct LinkTableNode
{
struct LinkTableNode * pNext;
}tLinkTableNode; /*
* LinkTable Type
*/
typedef struct LinkTable
{
tLinkTableNode *pHead;
tLinkTableNode *pTail;
int SumOfNode;
pthread_mutex_t mutex;
}tLinkTable; /*
* Create a LinkTable
*/
tLinkTable * CreateLinkTable();
/*
* Delete a LinkTable
*/
int DeleteLinkTable(tLinkTable *pLinkTable);
/*
* Add a LinkTableNode to LinkTable
*/
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
* Delete a LinkTableNode from LinkTable
*/
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
* get LinkTableHead
*/
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);
/*
* get next LinkTableNode
*/
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
//主程序调用更加一般的接口来实现

main()
{
InitMenuData(&head);
/* cmd line begins */
while(1)
{
char cmd[CMD_MAX_LEN];
printf("Input a cmd number > ");
scanf("%s", cmd);
tDataNode *p = FindCmd(head, cmd);
if( p == NULL)
{
printf("This is a wrong cmd!\n ");
continue;
}
printf("%s - %s\n", p->cmd, p->desc);
if(p->handler != NULL)
{
p->handler();
} }
}
//使用callback方式进行回调,有点像lamb表达式

int SearchCondition(tLinkTableNode * pLinkTableNode)
{
tDataNode * pNode = (tDataNode *)pLinkTableNode;
if(strcmp(pNode->cmd, cmd) == 0)
{
return SUCCESS;
}
return FAILURE;
} /* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
return (tDataNode*)SearchLinkTableNode(head,SearchCondition);
}
//为了更加通用,可以修改cmd数组,使其变为局部变量,同时增加一个args参数

tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode, void * args), void * args);

int SearchCondition(tLinkTableNode * pLinkTableNode, void * args)
{
char * cmd = (char*) args;
tDataNode * pNode = (tDataNode *)pLinkTableNode;
if(strcmp(pNode->cmd, cmd) == 0)
{
return SUCCESS;
}
return FAILURE;
}

  2.3  线程安全

线程安全主要是使用锁机制,在多个进程同时写的时候或者一个读一个写的时候容易发生。可重入的函数不一定是线程安全的,可重入的函数在多个线程中并发使用时是线程安全的,但不同的可重用函数(共享全局变量及静态变量)在多个线程中并发使用时会有线程安全问题。不可重用的函数一定不是线程安全的。详细结合代码分析,见代码首行注释

//这个方法一般不会引发线程安全问题,因为创造table会开辟不同的空间
tLinkTable * CreateLinkTable()
{
tLinkTable * pLinkTable = (tLinkTable *)malloc(sizeof(tLinkTable));
if(pLinkTable == NULL)
{
return NULL;
}
pLinkTable->pHead = NULL;
pLinkTable->pTail = NULL;
pLinkTable->SumOfNode = 0;
pthread_mutex_init(&(pLinkTable->mutex), NULL);
return pLinkTable;
} /*
* 删除可能会引发线程安全问题,主要是在free操作上会引发安全问题
*/
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;
} /*
* 不会引发线程安全问题
*/
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return FAILURE;
}
pNode->pNext = NULL;
pthread_mutex_lock(&(pLinkTable->mutex));
if(pLinkTable->pHead == NULL)
{
pLinkTable->pHead = pNode;
}
if(pLinkTable->pTail == NULL)
{
pLinkTable->pTail = pNode;
}
else
{
pLinkTable->pTail->pNext = pNode;
pLinkTable->pTail = pNode;
}
pLinkTable->SumOfNode += 1 ;
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
} /*
* 一般不会有线程安全问题,但是如果一个进程创建,一个删除,可能会引发线程安全问题
*/
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return FAILURE;
}
pthread_mutex_lock(&(pLinkTable->mutex));
if(pLinkTable->pHead == pNode)
{
pLinkTable->pHead = pLinkTable->pHead->pNext;
pLinkTable->SumOfNode -= 1 ;
if(pLinkTable->SumOfNode == 0)
{
pLinkTable->pTail = NULL;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
tLinkTableNode * pTempNode = pLinkTable->pHead;
while(pTempNode != NULL)
{
if(pTempNode->pNext == pNode)
{
pTempNode->pNext = pTempNode->pNext->pNext;
pLinkTable->SumOfNode -= 1 ;
if(pLinkTable->SumOfNode == 0)
{
pLinkTable->pTail = NULL;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
pTempNode = pTempNode->pNext;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return FAILURE;
} /*
读操作,不会引发线程安全问题
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode, void * args), void * args)
{
if(pLinkTable == NULL || Conditon == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
while(pNode != NULL)
{
if(Conditon(pNode,args) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
} /*
* 读操作,不会引发线程安全问题
*/
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable)
{
if(pLinkTable == NULL)
{
return NULL;
}
return pLinkTable->pHead;
} /*
* 读操作,不会引发线程安全问题
*/
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return NULL;
}
tLinkTableNode * pTempNode = pLinkTable->pHead;
while(pTempNode != NULL)
{
if(pTempNode == pNode)
{
return pTempNode->pNext;
}
pTempNode = pTempNode->pNext;
}
return NULL;
}

总结:

我们开发软件的时候应该按照软件工程的一般规律,在模块化、可重用接口的设计、以及线程安全的问题上多下文章,尽可能的提高软件开发的效率和软件的质量。

由Menu小项目所引发的对软件工程的思考的更多相关文章

  1. 小项目特供 贪吃蛇游戏(基于C语言)

    C语言写贪吃蛇本来是打算去年暑假写的,结果因为ACM集训给耽搁了,因此借寒假的两天功夫写了这个贪吃蛇小项目,顺带把C语言重温了一次. 是发表博客的前一天开始写的,一共写了三个版本,第一天写了第一版,第 ...

  2. java小项目之:扫雷,这游戏没有你想的那么简单!

    扫雷 我之前分享的小项目和小游戏,电影购票.坦克大战.捕鱼达人.贪吃蛇等,虽然已经是耳熟能详人尽皆知的项目和游戏,但是保不齐真的有人没接触过. 今天分享的这个项目,我不相信没人接触过(仅限80后-00 ...

  3. 用struts2标签如何从数据库获取数据并在查询页面显示。最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变量。

    最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变 ...

  4. IOS-小项目(饿了么 网络部分 简单实现)

    在介绍小项目之前,在此说明一下此代码并非本人所写,我只是随笔的整理者. 在介绍之前先展现一下效果图. 看过效果图大家应该很熟悉了,就是饿了么的一个界面而已,值得注意的是,实现时并没有采用本地连接,而是 ...

  5. Andriod小项目——在线音乐播放器

    转载自: http://blog.csdn.net/sunkes/article/details/51189189 Andriod小项目——在线音乐播放器 Android在线音乐播放器 从大一开始就已 ...

  6. 模拟XShell的小项目

    不知道大家有没有用过XShell这款工具,这款工具通过windows可以远程操作处于开机状态的linux操作系统,也就是说把你的电脑和一台服务器连入网络,你通过输入服务器所在的IP地址建立一个会话就可 ...

  7. 【PHP小项目使用MVC架构】

    小项目名称是雇员管理系统. mvc是一种项目的开发模式,中文名称为模式视图控制器,是强制程序员将数据的输入.处理.输出分开的一种开发模式. 在这个小项目中,控制器使用service作为后缀名. 项目u ...

  8. MOGRE学习笔记(3)--MOGRE小项目练习

    学习OGRE有一段时间了,领导为了检测学习效果,根据已有C++项目,弄一个类似的用c#语言编写的小项目. 配置:win7,DirectX2009,vs2010. 项目要求: 1.有Ogre窗口(尺寸1 ...

  9. Web前端开发:SQL Jsp小项目(一)

    Jsp的学习算是告一段落,针对这段时间的学习,写了一个Jsp小项目来巩固学到的知识. 框架示意图 User list process UserAdd process 需要的界面效果: 需要工具:Ecl ...

随机推荐

  1. 自定义springboot - starter 实现日志打印,并支持动态可插拔

    1. starter 命名规则: springboot项目有很多专一功能的starter组件,命名都是spring-boot-starter-xx,如spring-boot-starter-loggi ...

  2. 2020 Java开发者数据分析:中国已成为 Java 第一大国

    最近知名开发工具供应商Jetbrains在Java 25周年之际,对开发群体做了一次有意思的数据分析. 全文内容可见:https://blog.jetbrains.com/idea/2020/09/a ...

  3. 2020我终于成功搭建了Metasploitable3靶机

    0x00前言 在学习metasploit时我们往往需要一个靶场,下面为大家介绍一下如何在虚拟机中安装metasploitable 3靶场.Metasploitable3是Metasploitable2 ...

  4. DoModal 函数的用法

    转载:https://blog.csdn.net/mpp_king/article/details/79707728                        https://www.cnblog ...

  5. C/C++的二分查找

    假设有一种温度传感器,已经测得它的电压和温度的对应关系,将电压值以ADC转换后的数字量的值表示,形成温度-AD值的对照表,如下. 大致成一条反比关系的曲线. ADC的底层驱动已经写好,对外有一个接口可 ...

  6. hosts文件的内容

    C:\Windows\System32\drivers\etc\hosts 1 # Copyright (c) 1993-2009 Microsoft Corp. 2 # 3 # This is a ...

  7. 111 01 Android 零基础入门 02 Java面向对象 04 Java继承(上)02 继承的实现 01 继承的实现

    111 01 Android 零基础入门 02 Java面向对象 04 Java继承(上)02 继承的实现 01 继承的实现 本文知识点: 继承的实现 说明:因为时间紧张,本人写博客过程中只是对知识点 ...

  8. ASP。netcore,Angular2 CRUD动画使用模板包,WEB API和EF 1.0.1

    下载Angular2ASPCORE.zip - 1 MB 介绍 在本文中,让我们看看如何创建一个ASP.NET Core CRUD web应用程序与Angular2动画使用模板包,web API和EF ...

  9. 汕尾6397.7539(薇)xiaojie:汕尾哪里有xiaomei

    汕尾哪里有小姐服务大保健[微信:6397.7539倩儿小妹[汕尾叫小姐服务√o服务微信:6397.7539倩儿小妹[汕尾叫小姐服务][十微信:6397.7539倩儿小妹][汕尾叫小姐包夜服务][十微信 ...

  10. day04 Pyhton学习

    一.上节课内容回顾 字符串 由','','''',""'"括起来的内容是字符串 字符:单一文字符号 字符串:把字符连成串(有顺序的) 索引和切片 s[start: end ...