软件工程结对编程博客

结对编程博客

一、GitHub项目地址

项目GitHub地址前端

二、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(小时) 实际耗时(小时)
Planning 计划 0.5
· Estimate · 估计这个任务须要多少时间 20.4
Development 开发 18.6
· Analysis · 需求分析 (包括学习新技术) 5
· Design Spec · 生成设计文档 0.5
· Design Review · 设计复审 (和同事审核设计文档) 0.5
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 0.1
· Design · 具体设计 0.5
· Coding · 具体编码 10
· Code Review · 代码复审 1
· Test · 测试(自我测试,修改代码,提交修改) 1
Reporting 报告 1
· Test Report · 测试报告 0.2
· Size Measurement · 计算工做量 0.1
· Postmortem & Process Improvement Plan · 过后总结, 并提出过程改进计划 0.5
合计 21.9

三、看教科书和其它资料中关于Information Hiding, Interface Design, Loose Coupling的章节,说明大家在结对编程中是如何利用这些方法对接口进行设计的

  • Information Hiding:信息的隐藏和封装,其中信息的隐藏是目的,而封装是方法,这一行为是为了保证接口的安全性,避免在他人使用接口时修改了意料以外的数据,而出现问题。在结对编程中,类属性、方法等尽可能定义为私有,只有其余类须要调用的方法才设置为公有。
  • Interface Design:接口设计。接口做为不一样模块之间相链接的部分,其设计的合理性与安全性直接关系到不一样模块接合时的难易度与安全性。好的接口设计应符合诸多原则如接口隔离原则、迪米特法则、开闭原则等,其目的都是为了保证接口的安全性与实用性。结对编程中,经过事前规定、参照做业要求等,接口设计基本合理。
  • Loose Coupling:松耦合,简单地说就是下降功能、函数之间的依赖程度,即一个方法尽可能只完成一个功能,方面修改,避免出现一个函数修改,全部函数都须要再次修改,这样不免致使每次修改时工做量变大,且正确性存疑。可在接下来的模块中看到,结对编程中尽可能符合了该要求。

四、计算模块接口的设计与实现过程

计算模块主要有6个类,Core类DFS类Error类Input类PreProcess类RingDfs类ios

Input类用于从输入中提取所须要的信息,如模式、开头结尾、路径等。git

PreProcess类用于处理数据,包括分割单词,并依据分割出的单词生成图,输出结果等。github

DFS类用于寻找图中是否有环。算法

RingDfs类用于根据生成的图进行搜索,寻找符合要求的最长路径。编程

Core类用于封装。后端

Error类用于异常时报错。安全

Input类

方法名 做用 调用方法
Input(int num, char * paras[]) 根据程序参数,提取相应信息,如各种参数,文件路径等 本类私有方法,Error(string str)方法
Input(string str); 根据输入字符串提取相应信息 本类私有方法
int getMode() 返回指定的单词链类型,1为单词数,2为字母数
int getIfRing() 返回是否容许环
int getHead() 返回指定的单词链开头,0为无限制,不然为字母的ASCII码值
int getTail() 返回指定单词链结尾,规则同上
string getPath() 返回输入的文件路径
string getInput() 返回输入的完整字符串,用于调试

PreProcess类

方法名 做用 调用方法
PreProcess(char* wordss[], int len, int r); 根据输入的单词表、长度、是否有环,判断是否合法并生成图 本地私有方法,Error(string str)方法
PreProcess(string str, int n, char* words[], int r) 根据输入的包含全部单词的字符串、长度、是否有环,生成单词表与图。单词表char* []是为了知足接口要求。 本地私有方法
PreProcess(string str, int r) 根据输入的路径与是否有环,生成单词表与图 本地私有方法
void printGraph() 打印合并同首尾单词后的图
void printRingGraph() 答应元素
void print(vector ary) 根据输入的容器,将其中单词按顺序输出至solution.txt
void VecToStr(char* result[], vector ans) 将图转化为单词表格式,以知足同接口要求 Error(string str)方法

DFS类

方法名 做用 调用方法
DFS(PreProcess pp) 生成实例
void getGraph() 获取已生成好的图
bool hasRing() 判断图中是否有除了自环之外的环

RingDFS类

方法名 做用 调用方法
RingDFS(PreProcess pp) 生成实例,pp中含有图等信息
vector initDFS(int mode, int head, int tail, int ring) 根据输入的各种参数,以及获取到的图,搜索符合要求的单词链,并返回结果 本地私有方法,Error(string str)方法

Core类

方法名 做用 调用方法
int gen_chain_word(char* words[], int len, char* result[], char h ead, char tail, bool enable_loop) 根据输入的单词表、长度、参数等,搜索单词数量最多的符合要求的单词链 Input::Input
int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop) 根据输入的单词表、长度、参数等,搜索字母数量最多的符合要求的单词链 同上

Error类

方法名 做用 调用方法
void Error(std::string str) 根据输入的报错信息抛出异常

算法基于bfs的改进(改进原理见第6部分),基本原理简单易懂。对于无环的状况下,建图时合并了同首尾单词,只保留字母数量最大的单词。数据结构

五、画出UML图显示计算模块部分各个实体之间的关系

六、计算模块接口部分的性能改进。 记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展现一张性能分析图(由VS 2015/2017的性能分析工具自动生成),并展现你程序中消耗最大的函数

如下为无环,单词数量9900的运行状况:

可见,大部分时间花在了建图时的各类关于内存的操做上。这是由于在优化了无环的搜索算法后,将搜索部分的复杂度限制在了O(26x26),速度很快,所以关于内存的各种操做成为了最占用时间的操做。

对于算法的具体优化以下

对于无环的搜索,按以下方法建图:由于无环,因此以26个字母为节点,单词为边,以a开头b结尾的单词,表现为由节点a指向节点b的一条边。

在搜索算法上,以递归BFS搜索为基础,在节点b的递归退出时,便可确保b日后的全部路径均已遍历,所以能够获得以b为开头,能搜索到的最长符合要求的单词链长度,保存该长度,记为L(b)。以后若其余节点访问节点b时,好比上述例子中的a节点访问b节点,不须要再次进入b递归,只须要比较(L(b))与(由a指向b的边长度)之和,与本来的L(a)的大小,并将大者保存为新的L(a)便可。

这样,在全部节点递归结束后,获得了每一个节点日后所能达到的符合要求的单词链的最大长度。根据此信息输出最长路径便可。

该算法确保了每条边只走一次,不会重复走同一条边(同一个单词),所以,在无环的基础上其复杂度为O(26x26)。

可是,在有环的问题中,由于环的存在以及该方法没法判断L(b)中有哪些节点,所以可能会致使重复走同一条边,该算法没法使用。

七、看Design by Contract, Code Contract的内容 ,描述这些作法的优缺点, 说明你是如何把它们融入结对做业中的

契约式设计的优势是,义务、责任分工明确,各自负责各自部分的正确性,简化了开发流程,明确了各自任务。契约的存在保障了双方的利益,对需求者来讲,不须要担忧所需部分的正确性,只须要按照契约使用便可。对于供应者来讲,不须要担忧提供的模块如何使用等, 只须要按照契约完成模块并确保正确性便可。

在结对编程中,咱们会规定不一样模块之间的接口,在发现bug后将bug交给相应模块的实现者负责修复,另外一方只在实现者遇到困难时提供帮助。

八、计算模块部分单元测试展现。 展现出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路

部分单元测试代码以下:

这两部分测试数据,测试的是方法Input Input(string str),方法Input Input(int num, char* paras[])。两个方法的做用分别是根据输入的字符串,或程序的参数提取相应的设置信息,文件路径等。

测试的思路是构造符合被测方法输入数据结构要求的数据,并尽可能复杂,以达到更高的分支覆盖率。

以图中的测试用例为例,由于测试的是处理输入的方法,所以在构造输入数据时尽可能构造较为复杂的输入,即尽可能输入更多的参数,以覆盖较多的分支。

对于异常测试,测试代码以下:

catch方法中经过判断异常信息是否符合预期的方式,来判断是否触发了所要测试的异常。

同时,在应该触发异常的代码下一行加入一条永假的断言Assert::AreEqual("Error Test Didn't Throw Error!", " ");,以确保经过的测试,是触发了正确的异常,而不是没有触发异常致使没有进入catch,根本没有运行catch中的断言。

测试的覆盖率以下:

由于VS只有企业版、专业版才能查看单元测试的分支覆盖率,因此该分支覆盖率是将大部分测试中的代码转移到main函数中运行后查看的,其覆盖率低于实际的单元测试分支覆盖率但也达到了90%。

九、计算模块部分异常处理说明

全部的异常以下:

  • 输入错误

    • Chain Type Parameter Error

      在已经输入过-w-c参数,即指定过单词链类型的状况下,再次输入这两个参数。

      单元测试:

      try 
      {
          Input input("-w -c ..\Pair_Programming\DFSTest2.txt");
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Chain Type Parameter Error";
          Assert::AreEqual(s, str);
      }
    • Head Duplicate Definition Error

      在已经使用-h指令规定过开头的状况下,再次指定开头。

      单元测试:

      try
      {
          Input input("-w -h a -h a ..\\Pair_Programming\\DFSTest2.txt");
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Head Duplicate Definition Error";
          Assert::AreEqual(s, str);
      }
    • Lack Space Error

      在一次性输入全部参数时,参数之间、-h指令与制定的开头字母之间应有空格。没有空格时抛出次异常。

      单元测试:

      try
      {
          Input input("-w -ha ..\\Pair_Programming\\DFSTest2.txt");
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Lack Space Error";
          Assert::AreEqual(s, str);
      }
    • Head Letter Error

      指定的开头字符不是字母。

      单元测试:

      try
      {
          Input input("-w -h + ..\\Pair_Programming\\DFSTest2.txt");
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Head Letter Error";
          Assert::AreEqual(s, str);
      }
    • Tail Duplicate Definition Error

      使用-t指令指定过结尾的状况下再次使用该指令。

      单元测试:

      try
      {
          Input input("-w -t a -t a ..\\Pair_Programming\\DFSTest2.txt");
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Tail Duplicate Definition Error";
          Assert::AreEqual(s, str);
      }
    • Tail Letter Error

      制定的结尾字符不是字母。

      单元测试:

      try
      {
          Input input("-w -t + ..\\Pair_Programming\\DFSTest2.txt");
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Tail Letter Error";
          Assert::AreEqual(s, str);
      }
    • Ring Parameter Duplicate

      屡次使用-r参数。

      单元测试:

      try
      {
          Input input("-w -r -r ..\\Pair_Programming\\DFSTest2.txt");
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Ring Parameter Duplicate";
          Assert::AreEqual(s, str);
      }
    • Parameter Type Error

      输入的参数不符合规定。

      单元测试:

      try
      {
          Input input("-w -z ..\\Pair_Programming\\DFSTest2.txt");
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Parameter Type Error";
          Assert::AreEqual(s, str);
      }
    • Lack Chain Type Parameter

      在输入中没有使用-w-c制定指定单词链类型。

      单元测试:

      try
      {
          Input input("-h a ..\\Pair_Programming\\DFSTest2.txt");
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Lack Chain Type Parameter";
          Assert::AreEqual(s, str);
      }
  • 数据错误

    • Input File Doesn't Exit

      输入的文件路径不存在。

      单元测试:

      try
      {
          PreProcess pp ("noFile.txt", 0);
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Input File Doesn't Exit";
          Assert::AreEqual(s, str);
      }
    • Multiple Self Ring Found

      在不容许有环的状况下,出现了多个自环。

      单元测试:

      try
      {
          PreProcess pp("..\\Pair_Programming\\PPErrorTest1.txt", 0);
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Multiple Self Ring Found";
          Assert::AreEqual(s, str);
      }

      其中,PPPErrorTest1.txt的内容以下:

      bc
      cc
      cd
      df
      ff
      beee
      eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
      eeeeeeeee
      ef
      fg
      gb
    • No Word Chain in File

      输入的文件中单次数量小于2,不构成单词链。

      单元测试:

      try
      {
          PreProcess pp("..\\Pair_Programming\\PPErrorTest2.txt", 0);
      }
      catch (string str)
      {
          string s = "No Word Chain in File";
          Assert::AreEqual(s, str);
      }

      其中,PPErrortEST2.TXT文件内容以下:

      bc
    • Ring Detected When not Allowed

      在没有使用-r参数,的状况下,单词链中出现了环。

      单元测试:

      try
      {
          PreProcess pp("..\\Pair_Programming\\DFSTest1.txt", 0);
          DFS dfs(pp);
          dfs.getGraph();
          dfs.hasRing(0);
          Assert::AreEqual("Error Test Didn't Throw Error!", " ");
      }
      catch (string str)
      {
          string s = "Ring Detected When not Allowed";
          Assert::AreEqual(s, str);
      }

      其中,DFSTest1.txt文件的内容以下:

      bc
      cc
      cd
      df
      ff
      beee
      eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
      ef
      fg
      gb

十、界面模块的详细设计过程

开发平台

使用的是Qt Creator 3.4.2进行开发的,因为Qt自带了前端设计的功能,开发起来比较简单。

一、设计界面

使用的是Qt Creator自带的设计面板,操做简单只须要拖拽便可,如下是具体设计。

二、编写后端

在设计好了界面以后,只须要编写好slots函数就可在设计面板中简单的关联(代码未所有贴出)

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    bool H;
    int wc;
    bool R;
    bool T;
    QString tmp;
    bool fFile;
private slots:
    void setC(bool b);
};

#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
#include "qtextbrowser.h"
#include <string>
#include <QFile>
#include <QDir>
#include <QTextStream>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //ui->hideButton->hide();
}

MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::setC(bool b){
    this->wc = b?2:1;
}

编写完成后,须要作的就是将函数与组件关联起来,一样在设计面板中操做。

关于Qt的信号槽机制,具体能够看这篇博客,因为是在设计模板中关联的,因此并不须要手动编写connect函数。

十一、界面模块与计算模块的对接

首先,将计算模块打包成dll文件,为了使用方便,将统一的接口gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop)与接口gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop)定义为函数而非类方法。

打包好后,将生成的Core.dll文件复制到工程的debug文件夹或release文件夹中,经过如下代码导入两个上述接口:

typedef int(*ptr_word)(char* words[], int len, char* result[], char head, char tail, bool enable_loop);
typedef int(*ptr_char)(char* words[], int len, char* result[], char head, char tail, bool enable_loop);
HINSTANCE CoreDLL = LoadLibrary("Core.dll");
ptr_word gen_chain_word = (ptr_word)GetProcAddress(CoreDLL, "gen_chain_word");
ptr_char gen_chain_char = (ptr_word)GetProcAddress(CoreDLL, "gen_chain_char");

导入后,即可以直接调用两个函数。

十二、描述结对的过程

在结对的过程当中,首先咱们在一块儿预估各个难度的复杂度,以后对于部分逻辑极为简单且不重要的部分如读入等,进行分工,并行开发提升速度。对于核心的部分,如搜索算法的完成、优化等,咱们按照结对编程的原则,一人写一人检查,确保没有BUG以及算法的正确性。

1三、看教科书和其它参考书,网站中关于结对编程的章节, 明结对编程的优势和缺点。结对的每个人的优势和缺点在哪里

正如《构建之法》中所述

既然代码复审能发现这么多问题,有这么好的效 果,若是咱们每时每刻都处在代码复审的状态, 那不是很好么?

结对编程是将代码复审发挥到极致后的形式,也就是说结对编程彻底继承了代码复审的全部优势:加强了代码的规范性,减小了bug出现的可能性,提升了代码的可维护性等等。在合理的使用方法下,结对编程的效率甚至能超过并行开发的效率,考虑到测试、维护等等问题的时间成本。可是,这样的“极限”形式也有必定的局限,即对于程序中很是简单,逻辑复杂度极地的部分,进行实时的代码复审就有了“杀鸡用牛刀”的意味,在这些部分上,结对编程显然有矫枉过正的嫌疑,其开发效率在程序的简单部分中显然没有并行开发高。

结对中个人优缺点:

优势:承担任务主动,投入时间长、任务完成的较为完整,进行了许多优化。

缺点:与同伴沟通不足。

个人同伴的优缺点:

优势:配合良好,完成任务优秀、负责,测试完整。

缺点:没有及时反馈进度与问题。

1四、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(小时) 实际耗时(小时)
Planning 计划 0.5 0.5
· Estimate · 估计这个任务须要多少时间 20.4 55+
Development 开发 18.6 50+
· Analysis · 需求分析 (包括学习新技术) 5 1
· Design Spec · 生成设计文档 0.5 0.2
· Design Review · 设计复审 (和同事审核设计文档) 0.5 0.1
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 0.1 0.1
· Design · 具体设计 0.5 1
· Coding · 具体编码 10 40+
· Code Review · 代码复审 1 1
· Test · 测试(自我测试,修改代码,提交修改) 1 10+
Reporting 报告 1 2
· Test Report · 测试报告 0.2 0.5
· Size Measurement · 计算工做量 0.1 0.1
· Postmortem & Process Improvement Plan · 过后总结, 并提出过程改进计划 0.5 0.5
合计 21.9 55+

1五、松耦合

咱们与学号1606109三、16061155的队伍进行了松耦合测试,结果正确,对方使用我方Core.dll的运行结果截图以下:

在刚开始耦合时出现了问题,由于在对于head的定义中,咱们组的逻辑是当head不为0即认为是规定的开头字母,但接口内部没有检查其正确性,而对方认为head不规定时为'0'
出现问题后,咱们组在接口内部加2上了对于参数的合法性判断,以后没有其余问题。

以后咱们还与1606116七、16061170组进行了耦合,运行正确,没有问题。

相关文章
相关标签/搜索