记一次网易云课堂MOOC课程学习经历——《软件工程(C编码实践篇)》
刘东晓 + 原创作品转载请注明出处 + 《软件工程(C编码实践篇)》MOOC课程http://mooc.study.163.com/course/USTC-1000002006
- 一、对课程的简要理解
正如计算机业内的一个非常经典的等式所言:
程序 =算法+数据结构
软件 =程序+软件工程
软件企业 =软件+商业模式
现代软件企业的成功离不开优秀的软件以及杰出的商业模式,同时,作为企业运营核心的软件亦离不开软件工程的指导。
作为商业软件而言,程序是软件的“内功”,“内功”分算法和数据结构两个方面,它们共同决定了程序运行的效率,但无关乎程序的正确与否。除了程序本身之外,软件工程才真正决定了软件的命运。软件工程是指导人们如何构建和开发软件的科学,是最优的组织软件开发的方法。
总的说来,有了软件工程设计思想的“武装”,以后就为开发规范、成功的软件打下了坚实的基础。
- 二、课程中的实践
本课程有相对应的实验课程,在实验楼网站中运行虚拟的Linux环境来进行C语言的编程。
以下是实验的心得与体会,共六篇:
实验一:写一个hello world小程序
实验二:命令行菜单小程序V1.0
实验三:内部模块化的命令行菜单小程序V2.0
实验四:用可重用的链表模块来实现命令行菜单小程序V2.5
实验五:用callback增强链表模块来实现命令行菜单小程序V2.8
实验七:将menu设计为可重用的子系统
也欢迎大家到我的代码仓库查看源代码:http://git.shiyanlou.com/dustcenix/shiyanlou_cs122
- 三、对代码的简要分析
在这个十分简单的“菜单”功能C程序的开发过程中,将会逐渐重现下列软件工程开发思想对软件开发的指导:
-
模块化
-
接口
-
信息隐藏
-
增量开发
-
抽象
-
代码重用
在开始的开始,还有一个十分重要的事情,那就是程序的代码风格。
什么是代码风格?什么是好的代码风格?
通常来说,代码风格就是,缩进、命名、注释等代码编排的风格规范。
好的代码风格的原则是:简明、易读、无二义性。
对于编译器来说,代码风格确实无关紧要,甚至对于一些追求极限性能的程序来说,有时候,考虑代码风格问题甚至会带来程序性能的下降(代码难以理解)。
但是,在实际开发的绝大多数场合,开发者所遇到的性能瓶颈问题,都基本不会是依靠打乱代码风格来解决的。
正相反,良好的代码风格,使程序代码简洁清晰,容易维护,也是开发人员之间的写作具有更高的效率。
(1)最原始的代码
#include#include #include <string.h> #include int main() {char command[128];time_t t;char Name[64]="NULL";while(1){printf("command>>");scanf("%s",command);if(strcmp(command,"help") == 0){printf("commandlist:help,info,echo,exit,time,setName,getName,clearName\n");}else if(strcmp(command,"info") == 0){printf("author:Liu Dongxiao Version:1.0\n");}else if(strcmp(command,"echo") == 0){scanf("%s",command);printf("%s\n",command);}else if(strcmp(command,"exit") == 0){exit(0);}else if(strcmp(command,"time") == 0){printf("Now:%s",ctime(&t));}else if(strcmp(command,"setName") == 0) {printf("Please insert your name!\n");scanf("%s",Name);}else if(strcmp(command,"getName") == 0){printf("%s\n",Name);}else if(strcmp(command,"clearName") == 0){strcpy(Name,"NULL");}else printf("Wrong Command\n"); }return 0; }
程序十分简单!学过C语言的都能看懂!
(2)引入链表,实现代码的业务逻辑和数据存储之间的分离
前文的代码十分简单,同时也存在严重的不足,就是一点用也没有。
我们的目的是写一个可重用的菜单程序,上面那个连边也摸不着。
于是,我们引入了一种数据结构:链表
typedef struct dataNode {char *cmd;char *desc;int (*handler)();struct dataNode *next; }tDataNode; tDataNode * findCMD(tDataNode *head,char *cmd); void showAllCMD(tDataNode *head);
具体实现请参见代码库。
有了链表,再对源程序改造一番!
#include#include #include #include "Linklist.h" #define CMD_MAX_LEN 128 #define DESC_LEN 1024 #define CMD_NUM 10 static time_t t; void help(); void info(); void echo(); void Exit(); void Time(); static tDataNode menu[]= {{"help","List all command in this program",help,&menu[1]},{"info","Show information",info,&menu[2]},{"echo","Repeat your input",echo,&menu[3]},{"exit","Exit this program",Exit,&menu[4]},{"time","Show time now",Time,NULL}, }; int main() {char cmd[CMD_MAX_LEN];tDataNode *command;printf("Program is running\n");while (1){printf("Command>>");scanf("%s",cmd);command=findCMD(menu,cmd);if(command == NULL){printf("Command Not found\n");} else if(command->handler != NULL){command->handler();}} } void help() {showAllCMD(menu); } void info() {printf("Author:Liu Dongxiao\nProgram Version:1.0\n"); } void echo() {char command[CMD_MAX_LEN];scanf("%s",command);printf("%s\n",command); } void Exit() {printf("Program exited\n");exit(0); } void Time() {printf("Now:%s",ctime(&t)); }
这样就将功能与存储分离了开来。同时,也应用了模块化的思想。
(3)改造链表,提升重用性
(2)中的链表很不完善,而且与源程序的耦合性也很强,继续改造之
typedef struct LinkListNode {struct LinkListNode * pNext; }tLinkListNode; typedef struct LinkList {tLinkListNode *pHead;tLinkListNode *pTail;int SumOfNode; }tLinkList; tLinkList * CreateLinkList(); int DeleteLinkList(tLinkList *pLinkList); int AddLinkListNode(tLinkList *pLinkList,tLinkListNode * pNode); int DelLinkListNode(tLinkList *pLinkList,tLinkListNode * pNode); tLinkListNode * GetLinkListHead(tLinkList *pLinkList); tLinkListNode * GetNextLinkListNode(tLinkList *pLinkList,tLinkListNode * pNode);
#include#include #include #include <string.h> #include "LinkList.h" #define CMD_MAX_LEN 128 #define DESC_LEN 1024 #define CMD_NUM 10 void help(); void info(); void echo(); void Exit(); void Time(); typedef struct DataNode {tLinkListNode *pNext;char* cmd;char* desc;void (*handler)();struct DataNode *next; }tDataNode; tDataNode *FindCmd(tLinkList *head, char *cmd) {tDataNode* pNode=(tDataNode*)GetLinkListHead(head);while(pNode !=NULL){if(!strcmp(pNode->cmd,cmd)){return pNode;}pNode=(tDataNode*)GetNextLinkListNode(head,(tLinkListNode*)pNode);}return NULL; } int showAllCMD(tLinkList* head) {tDataNode *pNode=(tDataNode*)GetLinkListHead(head);while(pNode !=NULL){printf("%s - %s\n",pNode->cmd,pNode->desc);pNode =(tDataNode*)GetNextLinkListNode(head,(tLinkListNode*)pNode);}return 0; } int InitMenuData(tLinkList **ppLinkList) {*ppLinkList=CreateLinkList();tDataNode* pNode=(tDataNode*)malloc(sizeof(tDataNode));pNode->cmd="help";pNode->desc="List all command in this program";pNode->handler=help;AddLinkListNode(*ppLinkList,(tLinkListNode *)pNode);pNode=(tDataNode*)malloc(sizeof(tDataNode));pNode->cmd="info";pNode->desc="Show information";pNode->handler=info;AddLinkListNode(*ppLinkList,(tLinkListNode *)pNode);pNode=(tDataNode*)malloc(sizeof(tDataNode));pNode->cmd="echo";pNode->desc="Repeat your input";pNode->handler=echo;AddLinkListNode(*ppLinkList,(tLinkListNode *)pNode);pNode=(tDataNode*)malloc(sizeof(tDataNode));pNode->cmd="exit";pNode->desc="Exit this program";pNode->handler=Exit;AddLinkListNode(*ppLinkList,(tLinkListNode *)pNode);pNode=(tDataNode*)malloc(sizeof(tDataNode));pNode->cmd="time";pNode->desc="Show time now";pNode->handler=Time;AddLinkListNode(*ppLinkList,(tLinkListNode *)pNode);return 0; } tLinkList* head=NULL; int main() {char cmd[CMD_MAX_LEN];InitMenuData(&head);tLinkListNode *command;printf("Program is running\n");while (1){printf("Command>>");scanf("%s",cmd);tDataNode *p = FindCmd(head,cmd);if(p == NULL){printf("Command Not found\n");} else if(p->handler!= NULL){p->handler();}} } void help() {showAllCMD(head); } void info() {printf("Author:Liu Dongxiao\nProgram Version:1.0\n"); } void echo() {char command[CMD_MAX_LEN];scanf("%s",command);printf("%s\n",command); } void Exit() {printf("Program exited\n");exit(0); } void Time() {time_t t;printf("Now:%s",ctime(&t)); }
通过改造,就能在编码时方便的添加menu项啦!
(4)信息的隐藏与封装以及回调函数
可以看出,之前的代码中,我们将链表的结构体定义在LinkList.h中,而总所周知的是,C语言的库函数头文件中,存放的都只是函数和结构的声明,而不将实际的实现写在头文件中,这样做,可以防止其他人查看代码的内部实现,可以有效的隐藏信息。
typedef struct LinkListNode tLinkListNode; typedef struct LinkList tLinkList; tLinkList * CreateLinkList(); int DeleteLinkList(tLinkList *pLinkList); int AddLinkListNode(tLinkList *pLinkList,tLinkListNode * pNode); int DelLinkListNode(tLinkList *pLinkList,tLinkListNode * pNode); tLinkListNode * GetLinkListHead(tLinkList *pLinkList); tLinkListNode * GetNextLinkListNode(tLinkList *pLinkList,tLinkListNode * pNode); tLinkListNode *SearchLinkListNode(tLinkList *pLinkList,int Condition(tLinkListNode * pNode, void * args),void *args);
所谓回调,就是调用者调用被调用者,在被调用者执行的过程中,又去执行调用者代码段的过程逻辑。
我们在本程序中引入回调函数,是为了进一步增强链表的功能。
int SearchCondition(tLinkListNode * pNode, void * args){return (strcmp(((tDataNode*)pNode)->cmd,(char*)args)?0:1); } tDataNode *FindCmd(tLinkList *head, char *cmd) {return (tDataNode*)SearchLinkListNode(head,SearchCondition,cmd); }
tLinkListNode *SearchLinkListNode(tLinkList *pLinkList,int Condition(tLinkListNode * pNode, void * args),void *args) {if(pLinkList == NULL){return NULL;}tLinkListNode *tmp = pLinkList->pHead;while(tmp != NULL){if(Condition(tmp,args) == 1){return tmp;}tmp = tmp->pNext;}return NULL; }
可以看出,在上述两段代码的过程逻辑中,主函数中的FindCMD调用了LinkList中的SearchLinkListNode,而在SearchLinkListNode中又使用了主函数传递过来的SearchCondition函数,这样可以做到,在主程序中定义了搜索条件,而不是预先定义在LinkList中。
通过一系列的改进,我们编写的程序做到了,在主函数中定义menu项,加入队列,自定义查询条件等一系列功能。
任务接近完成!
(5)最终的改造,接口设计
之前一系列的改造之后,menu功能实现的很顺利,是时候将整个代码封装成模块了。
int MenuConfig(char *cmd, char *desc, void (*handler)(int argc, char *argv[])); int ExecuteMenu();
如果我们以后想使用menu模块,就要使用上面这两个接口。第一个用来增加menu项,第二个来启动menu页面。
接口的实现:
int MenuConfig(char *cmd, char *desc, void (*handler)(int argc, char *argv[])) {if(head == NULL){head = CreateLinkList();tDataNode *pNode = (tDataNode*) malloc(sizeof(tDataNode));pNode->cmd = "help";pNode->desc = "List all command in this program";pNode->handler = help;AddLinkListNode(head,(tLinkListNode *)pNode);}tDataNode* pNode=(tDataNode*)malloc(sizeof(tDataNode));pNode->cmd=cmd;pNode->desc=desc;pNode->handler=handler;AddLinkListNode(head,(tLinkListNode *)pNode);return 0; }; int ExecuteMenu() {int argc = 0;char cmd[CMD_MAX_LEN];char *argv[CMD_MAX_ARGV_NUM];char *command = NULL;MenuConfig("info","Show information\n\t'-author' for author information\n\t'-version' for version information",info);MenuConfig("exit","Exit this program",Exit);MenuConfig("time","Show time now",Time);printf("Program is running\n");while (1){argc = 0;command = NULL;printf("Command>>");command=fgets(cmd,CMD_MAX_LEN,stdin);if (command == NULL){continue;}command = strtok(command," ");while (command != NULL && argc < CMD_MAX_ARGV_NUM){argv[argc] = command;argc++;command = strtok(NULL," ");}if(argc == 1){int len = strlen(argv[0]);*(argv[0] + len -1) = '\0';}tDataNode *p = FindCmd(head,argv[0]);if(p == NULL){printf("Command Not found\n");} else if(p->handler!= NULL){p->handler(argc,argv);}} }
menu.c中原来的其他代码也有删改,详情请参见源码及实验报告。本实现中又新增了一个功能,即实现带参数的menu项。
使用示例:
MenuConfig("info","Show information\n\t'-author' for author information\n\t'-version' for version information",info);MenuConfig("exit","Exit this program",Exit);MenuConfig("time","Show time now",Time);
#include "menu.h" int main() {ExecuteMenu();return 0; }
大功告成!
- 四、结课总结
近两个月的学习已经结束了,本门课程的核心在于对软件开发思想的学习,而不是纠结于具体代码的一城一池,学习本课程的最大收获,就是在一个更高的理论层次上,总览全局,对软件的总体进行把握。只有学会了高屋建瓴,才能开发出高质量的软件。
除此之外,还对本课程抱有一丝遗憾:
(有一次课程作业忘记了互评……得分减半……好可惜……本句话删除)
希望可以在课程的学习中,实际动手做出一些更具有实用价值的软件。
感谢阅读!
转载于:https://www.cnblogs.com/dustcenix/p/6081979.html
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
