结对编程博客

项目 内容
本次作业所属课程 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] = '\0';
	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条

posted @ 2019-03-14 20:29  梦游的锤子  阅读(309)  评论(1编辑  收藏  举报