• 结对编程博客


    项目 内容
    本次作业所属课程 2019BUAA软件工程
    本次作业要求 结对编程作业
    我在本课程的目标 熟悉结对编程流程
    本次作业的帮助 帮助我提升了对项目时间的认识
    本次作业项目 Github项目地址

    1.Github项目地址

    Github项目地址

    2.估计开发时间及实际开发时间

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划 30 20
    · Estimate · 估计这个任务需要多少时间 10 5
    Development 开发 1300 1500
    · Analysis · 需求分析 (包括学习新技术) 280 300
    · Design Spec · 生成设计文档 20 0
    · Design Review · 设计复审 (和同事审核设计文档) 30 0
    · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 30
    · Design · 具体设计 120 180
    · Coding · 具体编码 460 600
    · Code Review · 代码复审 60 40
    · Test · 测试(自我测试,修改代码,提交修改) 300 450
    Reporting 报告 120 180
    · Test Report · 测试报告 50 30
    · Size Measurement · 计算工作量 10 10
    · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 60 140
    合计 1450 1700

    3.结对编程中的接口设计方法介绍

    • 最初我们把所有代码都写在了同一个文件里,也没有建立类。然后到后面测试环节时,我们发现这样的代码很难进行分类的单元测试。然后我们对代码进行了重构,这样代码不仅更方便测试了,也让代码可读性更高,更易于后面阶段的封装。
    • 在对计算模块进行封装时,我们发现gui和命令行都需要对获得的单词进行拆分,因此我就将计算单元中的单词切割也进行了封装。这样命令行和gui都只需要调用dll就能进行大部分操作。
    • 关于高内聚低耦合,我们将单词链转化成图,将图计算方面的函数都归入一个类中。而想要求单词链并不需要知道图的算法是如何运行的,只需要调用计算接口即可。
    • 三个Core.dll 中的接口:
    extern "C" CoreAPI int build_map(char* words[], char* text); \对单词进行拆分
    extern "C" CoreAPI int gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop);\计算最长单词单词链
    extern "C" CoreAPI int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop);\计算最长字母单词链
    

    4.计算模块接口的设计与实现过程。

    有3个类:CoreFindChainInit。18个函数。Init处理原始数据,命令行参数处理、单词切分等任务。FindChainInit的数据进行深加工,抽象出边和图,进行相关的计算。Core将两个类结合在一起,进行必要的调度。判断是否有环是通过是否存在拓扑序进行判断的,无环的最长路是用一个简单的spfa,在处理有环的最长路是,为了提高效率使用邻接链表,并使用了一个剪枝:如果当前最长路已经将一个点的所有出边都覆盖到,那么这个点的最长路一定不会更优。

    5.UML

    6.计算模块接口部分的性能改进

    在计算部分进行性能改进时,我们基本上就在对有环的情况进行优化,我们为了提高效率使用邻接链表,使用了一个剪枝:如果当前最长路已经将一个点的所有出边都覆盖到,那么这个点的最长路一定不会更优。

    我们性能分析后发现主要性能还是消耗在dfs的递归上,因此主要优化目标还是对dfs进行剪枝。

    7.Code Contrac的优缺点

    • 优点 :能极大可能的避免异常崩溃的发生,让编写出来的代码更加安全可靠。同时也是一次对代码的复审。
    • 缺点:很麻烦,增加代码量。
    • 在结对编程中,由于是两个人一个看,一个写,这样的话代码质量本身就比较高了,就可以让这个写入Assert的过程变成人工检查。这样既保证了安全性,又加快了编码效率。

    8.计算模块部分单元测试展示。

    该部分代码测试的是GetWordChain_NoRing函数,即没环的情况。

    	TEST_METHOD(TestMethod8)
    	{
    		char* words[10] = { "abb","bc", "cd", "de", "drrrrr","rrrrr" };
    		char* result[105];
    		FindChain findchain;
    		findchain.BuildMap(words, 6, 1, 0, 0);
    		int len = findchain.GetWordChain_NoRing(result);
    		Assert::AreEqual(len, 5);
            for (int i = 0; i < 3; i++)
    			{
    				int slen = strlen(result[i]);
    				Assert::IsTrue(result[i][slen - 1] == result[i + 1][0]);
    			}
    	}
    

    以上为当自环在这个链的最后时的测试,用来检查自环是否被计算上。

    		TEST_METHOD(TestMethod7)
    		{
    			char* words[10] = { "aa","cc","bb", "dd", "ee", "rr" };
    			char* result[105];
    			FindChain findchain;
    			findchain.BuildMap(words, 6, 1, 'c', 0);
    			int len = findchain.GetWordChain_NoRing(result);
    			Assert::AreEqual(len, 1);
    		}
    

    以上为对每个字母都是自环的情况进行考虑,虽然没有形成链但是一个单词也要输出链。

    该部分代码测试的是GetWordChain_Ring函数,即有环的情况。

    		TEST_METHOD(TestMethod1)
    		{
    			char* words[10] = { "ab","bc","ca", "cd", "de", "dee" };
    			char* result[105];
    			FindChain findchain;
    			findchain.BuildMap(words, 6, 1, 0, 0);
    			int len = findchain.GetWordChain_Ring(result);
    			Assert::AreEqual(len, 5);
    			for (int i = 0; i < 4; i++)
    			{
    				int slen = strlen(result[i]);
    				Assert::IsTrue(result[i][slen - 1] == result[i + 1][0]);
    			}
    		}
    

    测试覆盖率

    9. 计算模块部分异常处理说明

    • 不能识别的参数
    TEST_METHOD(InitError5)
    {
    	Core core;
    	char* argv[10] = { "program", "w" };
    	try
    	{
    		Init* init = core.init_word(2, argv);
    		Assert::Fail();
    	}
    	catch (const char* msg)
    	{
    		Assert::AreEqual("Incorrect command line parameters!", msg);
    	}
    }
    

    对应可能忘记加上-等等场景。测试时保证一定会抛出异常,否则会fail,并且异常信息正确。

    • 同时输入了-w-c指令
    TEST_METHOD(InitError2)
    {
    	Core core;
    	char* argv[10] = { "program", "-w","a.txt","-c","a.txt" };
    	try
    	{
    		Init* init = core.init_word(5, argv);
    		Assert::Fail();
    	}
    	catch (const char* msg)
    	{
    		Assert::AreEqual("Command line arguments include both -w and -c", msg);
    	}
    }
    

    测试时同时输入w和c参数,保证一定会抛出异常,且异常信息正确。

    • 没有输入-w-c指令
    TEST_METHOD(InitError3)
    {
    	Core core;
    	char* argv[10] = { "program" };
    	try
    	{
    		Init* init = core.init_word(1, argv);
    		Assert::Fail();
    	}
    	catch (const char* msg)
    	{
    		Assert::AreEqual("Command line parameters do not contain - w or -c", msg);
    	}
    }
    

    测试时没有输入w和c参数,保证一定会抛出异常,且异常信息正确。

    • -h-t指令后参数格式错误
    TEST_METHOD(InitError4)
    {
    	Core core;
    	char* argv[10] = { "program", "-w", "a.txt", "-h", "0" };
    	try
    	{
    		Init* init = core.init_word(5, argv);
    		Assert::Fail();
    	}
    	catch (const char* msg)
    	{
    		Assert::AreEqual("The parameter should be a letter", msg);
    	}
    }
    

    测试时-h后面的格式错误,保证一定会抛出异常,且异常信息正确。

    • 存在环路且没有-r指令
    TEST_METHOD(LoopError1)
    {
    	HINSTANCE CoreDLL = LoadLibrary("Core.dll");
    	p_gen_chain_word gen_chain_word = (p_gen_chain_word)GetProcAddress(CoreDLL, "gen_chain_word");
    	Core core;
    	char* words[10] = { "ab","bc","cd", "da", "abb", "bcc" };
    	char* result[105];
    	try
    	{
    		int len = gen_chain_word(words, 6, result, 0, 0, 0);
    		Assert::Fail();
    	}
    	catch (const char* msg)
    	{
    		Assert::AreEqual("There is a ring in the word list", msg);
    	}
    }
    

    测试时不输入-r,且单词中存在环路,保证一定会抛出异常,且异常信息正确。

    • 缺失参数
    TEST_METHOD(InitError1)
    {
    	Core core;
    	char* argv[10] = { "program", "-w" };
    	try
    	{
    		Init* init = core.init_word(2, argv);
    		Assert::Fail();
    	}
    	catch (const char* msg)
    	{
    		Assert::AreEqual("Missing parameters", msg);
    	}
    }
    

    测试时-w后未接文件路径,导致缺失参数,保证一定会抛出异常,且异常信息正确。

    • 找不到文件
    TEST_METHOD(BuildError1)
    {
    	Core core;
    	char text[1000];
    	char* argv[10] = { "program","-w","../WordlistProject/zzy.txt" };
    	Init* init = core.init_word(3, argv);
    	try
    	{
    		core.read_file(init, text);
    		Assert::Fail();
    	}
    	catch (const char* msg)
    	{
    		Assert::AreEqual("File not found", msg);
    	}
    }
    

    构造一个不存在的文件,保证一定会抛出异常,且异常信息正确。

    10.界面模块详细设计过程

    界面模块的第一步是选择一个舒适的gui库,我们去github上选择star数最高的imgui。简单的练习后可以上手了,但是发现缺少文件操作的函数,于是去issues找到了需要的函数。

    ShowMainMenuBar(core, init);
    OpenButtonMonitor(core, init, inputText);
    SaveButtonMonitor(core, init, outputText);
    			
    ImGui::Columns(3, "mixed");
    ImGui::Text("Input");
    ImGuiInputTextFlags inputFlags = ImGuiInputTextFlags_AllowTabInput;
    ImGui::InputTextMultiline("##Input", inputText, IM_ARRAYSIZE(inputText), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 32), inputFlags);
    //ImGui::Separator();
    ImGui::NextColumn();
    ImGui::Text("Output");
    ImGuiInputTextFlags outputFlags = ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_ReadOnly;
    ImGui::InputTextMultiline("##output", outputText, IM_ARRAYSIZE(outputText), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 32), outputFlags);
    			
    ImGui::NextColumn();
    FlagCheckBox(core, init, inputText, outputText);
    

    主界面将划分为3列,分别为输入、输出和相关功能的按钮

    static ImGuiFs::Dialog openDlg;
    const char* myPath = openDlg.chooseFileDialog(OpenButtonPressed);
    OpenButtonPressed = false;
    
    if (strlen(openDlg.getChosenPath()) > 0)
    {
    	ImGui::Text("Open file: "%s"", openDlg.getChosenPath());
    }
    else
    {
    	ImGui::Text("Open file: "%s"", "");
    }
    if (strlen(myPath) > 0)
    {
    	FILE* fp = fopen(myPath, "r");
    	int i = 0;
    	while ((text[i] = fgetc(fp)) != EOF && i < MAX_BYTES - 1) i++;
    	text[i] = '';
    	fclose(fp);
    }
    

    在菜单按钮上实现文件打开与导出,根据打开的文件路径,将文件内容读入到输入框中。导出功能类似,将输出框中的内容到处到所选的文件。

    11.界面模块(GUI或命令行模块)与计算模块的对接

    我们将计算模块打包成3个接口,然后GUI模块只需要对这三个接口进行调用即可。

    extern "C" CoreAPI int build_map(char* words[], char* text); \对单词进行拆分
    
    extern "C" CoreAPI int gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop);\计算最长单词单词链
    
    extern "C" CoreAPI int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop);\计算最长字母单词链
    
    

    计算功能

    直接从文本框输入后计算,右边是可选选项。

    文件功能

    从导入文件处输入后计算。

    与王冰小组耦合

    我们与王冰小组进行了gui的耦合。

    • 我们的gui和他们的计算模块耦合,没什么需要修改的地方,只需要调用他们计算模块的两个计算链的函数。
    • 我们的计算模块和他们gui耦合,也没什么问题,直接调用接口就能计算。
    • 这是与他们gui耦合的截图
    • 王冰学号:16061155 谢静芬学号:16061093

    12.描述结对的过程

    我们坐到一个电脑前,坐在一个凳子上挤在一起,一个看一个写。

    13. 结对编程与队友优缺点

    结对编程确实能够提高代码的质量,但是有些事情还是分工做比较合适。因此在选择编程方法时我认为要因地制宜。另结对编程确实得找个大点的地方,寝室里挤着不是很舒服。

    优点 缺点
    张朝阳 (1)对搜索算法提出了很多改进,思维活跃。(2)代码能力强。(3) 熟悉STL库 喜欢摸鱼
    余宸狄 (1)思维严谨,谋定后动(2)熟悉多种算法,更好地提出思路(3)喜欢使用基本类型,回归c本质 不够细心,是个瞎子

    14.实际花费时间

    见第2条

  • 相关阅读:
    Charles下载和使用
    C# mvc读取模板并修改上传到web
    nginx 安装
    python 测试:wraps
    Linux下MySQL数据库常用基本操作 一
    myeclipse新建maven项目
    java 数据导入xls
    tomcat允许跨域请求:
    Import Projects from git
    c# DataTable 序列化json
  • 原文地址:https://www.cnblogs.com/chuizi000/p/10533158.html
Copyright © 2020-2023  润新知