https://github.com/swearitagain/wordlistnode
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 10 | 20 |
· Estimate | · 估计这个任务须要多少时间 | 10 | 20 |
Development | 开发 | 870 | 1470 |
· Analysis | · 需求分析 (包括学习新技术) | 60 | 120 |
· Design Spec | · 生成设计文档 | 40 | 40 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
· Design | · 具体设计 | 60 | 100 |
· Coding | · 具体编码 | 400 | 1000 |
· Code Review | · 代码复审 | 60 | 40 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 120 |
Reporting | 报告 | 70 | 120 |
· Test Report | · 测试报告 | 20 | 30 |
· Size Measurement | · 计算工做量 | 20 | 30 |
· Postmortem & Process Improvement Plan | · 过后总结, 并提出过程改进计划 | 30 | 60 |
合计 | 950 | 1610 |
Information Hiding,Interface Design, Loose Couplinggit
首先参考wikipedia定义:程序员
information hiding is the principle of segregation of the design decisions in a computer program that are most likely to change, thus protecting other parts of the program from extensive modification if the design decision is changed
就是把数据封装起来,防止变化的部分对于原有数据的破坏,在结对编程中就是面向对象的实现,好比把输入输出封装为input_output类,将读入的数据暴露一个input函数,对外返回的是vector<string>。github
接口按照https://edu.cnblogs.com/campus/buaa/BUAA_SE_2019_LJ/homework/2638 设计编程
static int gen_chain(char* words[], int len, char* result[]); static int gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop); static int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop);
松耦合是指在编程的时候让一个部分尽量少地依赖其余部分的组件,这样就算由于需求更改而重写之前的函数,也能避免对其余没有变化的部分形成影响。数组
松耦合在本次结对编程项目中主要体如今对于函数以及.cpp的封装上,保证每一个模块的功能独立性,好比input_output.cpp只是对于输入输出的处理,calculate只是对于计算的处理。app
首先定义了一个接口类做为基类,暴露出调用者须要的方法,其中get_result()函数返回计算模块计算的结果:ide
class calculateInterface { public: virtual ~calculateInterface(); virtual vector<string> *get_result()=0; };
其次设计了calculate子类来完成最基本的计算功能: 这个类实现了接口类中定义的get_result方法,并新增了一个构造方法,以及一些私有的成员和方法。构造方法传入单词文本,以及-c参数。 私有方法中chain_find_next方法做用为找到当前单词结点的全部可以成链的下一个单词,而check_current_chain判断当前链是不是找到的最大链。
class calculate : public calculateInterface { public: calculate(vector<string> words, bool more_letter); ~calculate(); vector<string> *get_result() override; protected: vector<word_node> word_map[ALPHA_COUNT]; bool has_circle = false; bool more_letter; int longest_letter_count = 0; int current_letter_count = 0; vector<string> longest_word_chain; vector<string> current_word_chain; virtual bool chain_find_next(word_node prev_node); virtual void check_current_chain(); };
再而后设计了specified_calculate类,该类继承calculate类,支持了指定链首尾字母的功能。 该类重写了calculate类的get_result方法和check_current_chain方法,保留使用了父类的chain_find_next方法。
class specified_calculate : public calculate { public: //构造函数四个参数: //1. 字符串数组,由全部单词构成 //2. 布尔变量,是否按照字母最多计算单词链 //3. 整型,指定首字母,-1为不指定,0-26对应26个字母 //4. 整型,指定尾字母,-1为不指定,0-26对应26个字母 specified_calculate(vector<string> words, bool more_letter, int assigned_initail, int assigned_tail); ~specified_calculate(); vector<string> *get_result() override; void check_current_chain() override; protected: int assigned_initial; int assigned_tail; };
最后设计了circle_calculate类,该类继承specified_calculate类,支持了容许单词文本中隐含单词环功能。 该类重写了specified_calculate类的check_current_chain方法,保留使用了父类的其余全部方法。
class circle_calculate : public specified_calculate { public: //构造函数五个参数: //1. 字符串数组,由全部单词构成 //2. 布尔变量,是否按照字母最多计算单词链 //3. 整型,指定首字母,-1为不指定,0-26对应26个字母 //4. 整型,指定尾字母,-1为不指定,0-26对应26个字母 //5. 布尔类型,是否容许文本隐含单词环 circle_calculate(vector<string> words, bool more_letter, int assigned_initail, int assigned_tail, int circle); ~circle_calculate(); bool chain_find_next(word_node prev_node) override; protected: bool circle; };
类之间的关系能够参考下文的UML图。
咱们对项目进行了性能分析,发现项目的性能瓶颈在于递归迭代的深度过大。所以咱们优化了递归函数的结构,减小了其没必要要的操做,性能稍有提高。
函数
最开始不是很了解Design By Contract的概念,在Wikipedia上获得的标准定义以下:oop
//Design by contract Design by contract (DbC), also known as contract programming, programming by contract and design-by-contract programming, is an approach for designing software. It prescribes that software designers should define formal, precise and verifiable interface specifications for software components, which extend the ordinary definition of abstract data types with preconditions, postconditions and invariants. //code contract The contracts take the form of pre-conditions, post-conditions, and object invariants. Contracts act as checked documentation of your external and internal APIs. The contracts are used to improve testing via runtime checking, enable static contract verification, and documentation generation.
Design By Contract也就是契约式设计,Code Contract规定了接口的数据类型,接口执行以前的条件(precondition)和接口执行以后的条件(postcondition)。
我认为这种编程方式的优势:
缺点:
项目中如何使用:
因为在单元测试中不能使用命令行输入,因此只须要设计函数的输入,鉴定所须要的输出便可,测试案例以下:
TEST_METHOD(test_gen_chain_w) { char *result[4]; char *words[4] = { "END", "OF", "THE", "WORLD" }; Assert::AreEqual(2, gen_chain(words, 4, result)); }
测试覆盖率时使用OpenCppCoverage-0.9.6.1 VS插件进行,将单元测试模块迁移到main函数中测试以后,测试覆盖率图以下:
异常类型 | 设计目标 |
---|---|
对于传入的单词文本为空时的报错 | |
单词文本隐含单词环 | 对于没有-r参数时出现隐含单词环的报错 |
首尾字母约束不合法 | 对于单词首/尾字母指定不合法的报错 |
文本中某个单词为空 | 对于某个单词为空时的报错 |
文本中某单词首为非字母 | 对于首字母为非字母状况的报错 |
文本中某单词尾为非字母 | 对于尾字母为非字母状况的报错 |
关于命令行输入的异常以下:
抛出异常 | 说明 |
---|---|
-w param repeat | w参数重复 |
-c param repeat | c参数重复 |
-h param repeat | h参数重复 |
-t param repeat | t参数重复 |
-r param repeat | r参数重复 |
invalid param | 无效参数 |
非法输入:文件不存在 | 非法输入:文件不存在 |
本次只实现了命令行模块。
在结对编程项目中构建了一个input_output类,专门处理从文本的输入和将结果输出到文本。
首先是从命令行的输入,核心模块是处理命令行的输入:
while (i < in.size()) { if (in.at(i) == '-') { i++; //get next char char cur = in.at(i); if (cur == 'w') { if (is_w) { throw exception("-w param repeat"); } is_w = true; } else if (cur == 'c') { if (is_c) { throw exception("-c param repeat"); } is_c = true; } else if (cur == 'h') { if (is_h != 0) { throw exception("-h param repeat"); } i+=2; //get the blank char is_h = in.at(i); } else if (cur == 't') { if (is_t != 0) { throw exception("-t param repeat"); } i += 2; //get the blank char is_t = in.at(i); } else if (cur == 'r') { if (is_r) { throw exception("-r param repeat"); } is_r = true; } else { throw exception("invalid param"); } } else if (in.at(i) != ' ') { //read the absolute path of input file break; } i++; }
其中对于不符合规定的部分使用异常抛出,在main函数中接受异常。
本次只实现了命令行模块。
根据解耦合的思想,设计了一个专门的input_output
class input_output { public: input_output(); ~input_output(); vector<string> input(); void output(vector<string> words); vector<string> words; bool is_w; //word-按单词数量统计 bool is_c; //count-按字母数量统计 char is_h; //head-指定首字母 char is_t; //tail-指定尾字母 bool is_r; //round-是否成环 string in_path; //输入文件路径 string out_path; //输出文件路径 string err_msg; //错误日志 };
首先,在拿到题目后,咱们迅速阅读了项目的整个要求。在对项目的总体轮廓有大体的了解后,咱们开始讨论分析了项目的结构。仅仅浮于口上的讨论是不够的,也不利于后续实现。所以咱们草拟了一个文档初稿来规定了具体分工、接口设计、代码规范等技术细节问题。
在具体分工方面,虽然是结对编程,但咱们的工做仍有不一样的侧重。根据分工,我主要负责计算核心模块的开发和异常处理,队友主要负责测试工做和界面模块开发。
在接口设计方面,咱们遵守项目要求的接口设计,计算核心模块和界面模块都遵守项目要求中的三个接口进行设计。
在代码技术规范方面,咱们采用《百度C++编程规范》中的要求和建议,做为咱们的代码设计规范。
在前期的预备工做准备完毕后,咱们开始上手工做。首先设计了约定的接口并做出简单的测试,以后我负责开发核心计算模块,队友则负责编写界面模块和测试用例。因为事先约定清晰,咱们分别完成首个版本计算模块和交互模块后就当即展开了对接,没有多余的消耗。
此后我进行了几轮迭代,完善了计算模块的全部功能;队友跟进单元测试和回归测试,保证了计算模块的正确性。最后咱们做了性能分析等后续工做,完成项目。
优势:可以使代码处于一种一直在被复审的状态,程序员不断审核对方的代码,能够提升编码质量以及及时发现问题[大几率上]。
缺点:对于迭代快的项目开发,人力资源可能会很紧张, 须要团队成员独自开发本身的模块,结对编程对时间的整体利用率极可能不高。
结对编程最终的效果如何无非就是取决于1. 两我的的编程水平 2. 两我的合做的效率。至于博客要求至少列出每一个人三个优势和一个缺点,我以为没啥可写的。若是两我的都不鸽对面,而且尽量推动项目的进展,对于结对编程的目的来讲,就够了。