0
点赞
收藏
分享

微信扫一扫

结对编程项目——单词链的前因后果

杨小羊_ba17 2022-04-05 阅读 59

结对编程项目

——单词要成链

文章目录

一、项目背景

项目内容
这个作业属于哪个课程2022春季软件工程(罗杰 任健)
这个作业的要求在哪里结对编程项目-最长英语单词链-CSDN社区
我在这个课程的目标是学习软工的项目合作管理知识,通过高效的团队合作,建立完整敏捷开发流程,实现一个中大型软件工程项目。
这个作业在哪个具体方面帮助我实现目标通过结对编程,完成一个具有参数需求处理的应用程序,并且设计UI对其进行包装。学习架构设计,并在双人的结对编程中相互合作,提升效率和工作经验
教学班级周二班
项目地址https://github.com/Aaronhuang-778/PPPair_Programming
队友博客

二、预估-实际开发时间(PSP表格)

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

三、接口设计学习阶段

参考来源:

接口设计六大原则

[Information Hiding------wiki]([Information hiding - Wikipedia](https://en.wikipedia.org/wiki/Information_hiding#:~:text=In computer science%2C information hiding is the principle,extensive modification if the design decision is changed.))

[Loose Coupling------wiki](Loose coupling - Wikipedia)

  • 接口设计——单一职责原则

    定义:

    大体来说就是对类的定义是明确且单向的,也就是说要使类的职责做到单一;

    在设计之初,我们并没有考虑的很完善类的单一职责划分,出现了单个类多项职责的问题,在VS 2019代码评测工具中的代码维护性有部分内容不够完善,于是我们在开始之初便有了对封装类时职责的划分的教训。大体上我们封装了:参数读入、参数处理、读入处理、对应指令调用、对应算法调用、结果返回和文件输出、错误异常处理等过程。

  • 接口设计——里氏替换原则

    定义:

    总结来说就是所有引用基类的地方必须能透明地使用其子类的对象;

    这一点在一年之前面向对象的实践过程中就已经大量应用了,此次又由于C#语言的面向对象性较强,我们应用起来也较为得心应手;如在错误处理阶段,我们会根据错误处理类型来进行继承和多态的设计,他们都继承于ErrorType这一父级类,下设有参数错误、文件错误等多种错误类型

  • Information Hiding

    定义:

    在设计模块式,为了安全和防止泄露我们会将模块内的信息进行“隐藏”,也就是对私有属性和数据结构进行private标签表述,在访问时通过get类型函数或者其他特定接口进行访问

  • Loose Coupling

    定义:

    松耦合的大体定义:内部模块(类、接口、数据、服务等)之间相互的耦合程度要低,也就是说在某个模块进行修改或者迭代的时候,要最大化减小对其它模块的修改;并且某个模块对其它模块的子内容利用要少,即模块间的数据、服务调用少。

四、接口设计实现过程

上层模块及系统框架设计

根据上文中对接口设计的学习和体验,我们将上层接口大致分为七个部分,作为上层封装,在内部又具有不同的功能类和属性类进行相应操作,这种横向和纵向的设计基本符合高内聚、低耦合的需求。

整体上来说有七个上层模块:
在这里插入图片描述

从功能和任务来上分就是:顶层调用;解析参数;单词读取;指令调用;对应算法调用;结果返回和文件输出;错误异常处理等。下方给出了整个上层模块的调用示意图,以及相关关系,也算作是整个项目的小型系统架构图。

在这里插入图片描述

这里列举几个上层模块的内部函数和内部类的简要说明

内部细节举例说明
  • 全局参数

    GlobalPara(类):解读后的信息

  • DealWords(类)

    dealWords(函数)

    checkWords(函数)

  • DealParas(类)

    analyseParas(函数)

    checkParas(函数)

    checkInputFile(函数)

  • Word(属性类)

  • Graph(属性类)

    getWordList(函数)

    getStartList(函数)

    getEndList(函数)

  • ErroeType(属性类)InvalidInputException(功能类)

    CircleException(功能类)

    ChainErrorType(功能类)

    ChainNotFoundException(功能类)

关键算法之:拓扑排序+DFS图算法

由于读入单词图的特殊性质,我们将会在Graph当中形成一个完备的单词图,根据指令调用的特性选取较佳的算法。在这其中我们尽可能地减小搜算的消耗以及次数我们将在下面的部分详细介绍一些数据设计、预处理、算法部分。步骤大概如下:

  1. 建立有向图
  2. 去除孤立点
  3. 判断成环性
  4. 对症下药(选择合适算法,其中无环约束一律采用拓扑+DFS降低搜索次数)
  • Graph的构建

    在完成顶层模块中对于单词的读入之后我们将会在构成图的部分建立以下数据结构。其中:

    word_list:是我们保存整个读入单词的无序链表

    start_list:是一个字典,其中以字母作为索引key,内容是所有以key开头的Word类组成的列表

    end_list:是一个字典,其中以字母作为索引key,内容是所有以key结尾的Word类组成的列表

    adj:是一个二维数组,用以保存有向边信息,这里我们构建了具有权重的边属性,在后续算法中会有体现

    public static ArrayList word_list = new ArrayList();
    private static Dictionary<char, ArrayList> start_list = new Dictionary<char, ArrayList>();
    private static Dictionary<char, ArrayList> end_list = new Dictionary<char, ArrayList>();
    //权重边
    public static int [,] adj = null;
    
  • 孤立点删除

    这里的孤立点就是出入度均为0的单词,在单词图中是孤立的,也不会参与构成单词链所以我们将它删除,减少图搜索的次数。

    ArrayList next_list = G.getNextWordList((Word)Graph.word_list[i]);
    ArrayList last_list = G.getLastNode((Word)Graph.word_list[i]);
        if (next_list == null && last_list == null)
        {
                        Graph.word_list.RemoveAt(i);
                        i--;
                        continue;
         }
    
  • -n类型算法:难度中上

    算法核心:拓扑排序;DFS;动态维护

    维护动态单词链集合live_set,结果单词链集合link_set

    1. 从一个入度为0的起始点开始进行DFS

    2. DFS到单词结点n时,将set中所有单词链末尾追加n的单词

      若当前边的权重为1,将n的单词加入set,将权重置为0

    3. 输出动态单词链集合到结果单词链集合中

    4. 当DFS结束时,从另一个初始起始点继续第3步,直至遍历所有的初始起始点

在这里插入图片描述

算法函数:
class n_Method {
	public bool startTopo()
 
	public void startDFS()
    
	public void DFS()
}    
  • -m类型算法:难度中下

    算法核心:拓扑排序;DFS;动态维护头字母访问集合

    1. DFS 维护一个数组,记录访问过的单词首字母
    class m_Method {
        public bool startTopo()
            
        public void startDFS()
            
        public void m_DFS()    
    }
    
  • -w -h -t无环情况算法:难度中

    算法核心:拓扑排序;DFS;无权无环;反图问题

    1. 通过-h的约束指定起始点。进行DFS
    2. 在单纯-t条件下使用反图,增强代码复用
    class w_Method {
        public bool TopologicalSort()
        
        public void longestPathDAG()    
    }
    
  • -c -h -t无环情况算法:难度中

    算法核心:拓扑排序;DFS;有权无环;反图问题

    1. 通过-h的约束指定起始点。进行DFS
    2. 在单纯-t条件下使用反图,增强代码复用
    3. 建立有向加权边,权重为边目的单词的长度

在这里插入图片描述

```c#
class c_Method {
    //相比w_Method多传递有权边
    public bool TopologicalSort()
    
    public void longestPathDAG()    
}
```
  • w/-c -r`有环问题:难度中

    算法核心:DFS;有权有环;反图问题

    1. 通过-h的约束指定起始点。进行DFS
    2. 在单纯-t条件下使用反图,增强代码复用
    3. 针对不同类型输入相应需求
    4. 进行朴素DFS算法
    class rc_Method {
        public void startDFS()
            
        public void rc_DFS()   
    }
    class rw_Method {
        public void startDFS()
            
        public void rw_DFS()   
    }
    

五、计算模块UML图示

下方给出了计算模块的UML关系图,同样在上文中已经逻辑描述了他们之间的依赖关系
在这里插入图片描述

六、计算模块接口性能改进

  • 非极端情况下算法的时间复杂度为 O ( n 2 ) O_{(n^2)} O(n2),由于有环情况下复杂情况的单词链问题为NPC问题,很难优化,但是我们尽力使得性能消耗最小

  • 清除孤立点:由于单词图当中的孤立点是不参与到任何参数计算当中的,为了减少图遍历的点数目我们会提前清楚所有的孤立点(出入度都为0)

  • 无环情况下的拓扑优化:由于在无环情况下不管是四个指令当中的哪一个,我们的最佳输出一定是从入读为0的点进行;同样在-n操作中我们也只需要通过拓扑点开始进行DFS,设置好访问数组来进行所有单词链的输出

  • 有环情况下的空间优化:因为有环情况下我们要进行大量的DFS过程,所以我们最初的想法是对每个结点生成两个ArrayList来保存出度目标和入度来源,但是这种操作就需要开辟2n个数据结构,如果图的连通性强,那么其中的对象数目又会很多,所以我们采用了,字母字典存储,即以开头字母为key,保存所有到达这个字母的单词对象;结尾字母为key,保存所有可到达的单词对象。这种设计下我们就算有成千上万的单词,我们最终的图信息保存也只需要两个字典,最多2*26个数据结构。

    private static Dictionary<char, ArrayList> start_list 
                = new Dictionary<char, ArrayList>();
            private static Dictionary<char, ArrayList> end_list 
                = new Dictionary<char, ArrayList>();
    
  • 性能分析图

下图可见整体性能使用当中由于会进行读取所以IO的占比仅次于我们的计算处理模块

在这里插入图片描述

整体函数使用性能来看还是两个和图计算相关的算法模块占用较大

在这里插入图片描述

在这里插入图片描述

七、Design by Contract 以及 Code Contract

Design by Contract:
Code Contract:

不论是前者还是后者,上述概念的核心就是开发之初软件设计者对软件组件的正式定义,采用契约式的软件设计和编码过程,核心就是前置条件、后置条件、对象不变量这三点需求。其目的在于;设计阶段对API进行精确的设计,这种设计方式是具有逻辑验证性的,更加严谨和科学。初步了解后,北航的同学们可能觉得似曾相识,这一点在曾经的面向对象过程中体现在了Java接口设计的JML当中。

  • 优点:

    1. 接口功能和定义具有逻辑验证性,更加明确直接,无二义性
    2. 有利于开发者对整体架构的把握和对目标任务的理解
    3. 在软件工程项目中更容易进行对接和分工
  • 缺点:

    1. 友好性较差,用户理解难度大,需要数理逻辑知识铺垫
    2. 增大了工作量,契约的设计本身具有一定工程量
    3. 契约不具有不变性,可能会因为项目的迭代和实际需求的变化而被弃用
  • 结对项目的使用:

    在我们的结对项目中,由于时间较为紧迫,任务量大我们并没有专门设计严谨的契约接口,而是采用了一种通过讨论产生的“口头契约”,通过前期的开会交流,一起设计代码框架,明确顶层模块之间的关系,通过定义模块接口之间调用规范来分配任务。定义了函数的作用和层级关系,明确输入输出。

八、计算模块部分单元测试展示

测试覆盖率:

由于版本限制原因,我们选择了下载企业版VS进行覆盖率测试,由于最初代码结构并未进行管理,测试结果一般,我们调整代码格式之后进行测试,测试覆盖率结果理想,在Core的调用部分经过两天的优化使得覆盖率达到了100%。
在这里插入图片描述

测试结构:
  • 我们按照整体框架对计算模块进行了测试,我们使用脚本生成了大量的测试文件。其中每个指令类型包含15个测试文件。

在这里插入图片描述

在这里插入图片描述

举例:-n test0
inducibly
uncia
spaebook
flivver
darkies

由于单词环的复杂性,以及有环无环的问题,我们对测试文件进行了手工挑选,并且更改,确保了测试过程中能够覆盖所有的指令类型,以及相应的有环无环状态,同时还考虑到了headtail的组合情况。

 	    [TestMethod]
        public void TestMethod1()
        {
            string[] test = { "-w", "w_no_circle/test0.txt", "-h", "a" };
            Core.RunCMD.Main(test);

            Core.GlobalPara.clearGlobal();
            Core.Graph.clearGraph();
        }
        [TestMethod]
        public void TestMethod2()
        {
            string[] test = { "-w", "w_no_circle/test1.txt", "-t", "b" };
            Core.RunCMD.Main(test);
            Core.GlobalPara.clearGlobal();

            Core.Graph.clearGraph();

        }
        [TestMethod]
        public void TestMethod3()
        {
            string[] test = { "-w", "w_no_circle/test2.txt", "-h", "a", "-t", "b" };

            Core.RunCMD.Main(test);
            Core.GlobalPara.clearGlobal();
            Core.Graph.clearGraph();

        }
  • 我们采用了平行式的计算模块测试

在这里插入图片描述

  • 测试结果与预期结果相符合:下图展示了关于-n指令的测试结果
    在这里插入图片描述

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

异常设计方案:

通过继承调用,使用Exception进行错误处理部分整体设计,整体分类如下:

  1. InvalidInputException:异常输入部分错误处理

    输入文件不合法、文件不存在、文件类型不合法、文件路径不合法错误等

    输入参数不存在、参数组合不合法、参数格式不规范等

  2. CircleException:异常单词环部分错误处理

    -r条件下出现单词环

  3. ChainNotFoundException

    所求的单词环不存在

异常处理及其测试:

参数类型——

不支持的参数组合

Core.exe -n -h q aaa.txt

输入参数不支持:

Core.exe -b aaa.txt

输入参数格式错误

Core.exe -w h aaa.txt

参数重复出现

Core.exe -w -w -h -h aaa.txt

-h参数后面跟的字母不合法

Core.exe -w -h aaa aaa.txt

-t参数后面跟的字母不t合法

Core.exe -w -t aaa aaa.txt

在这里插入图片描述

输入文件错误——

文件后缀错误

Core.exe -w aaa.py

文件路径不合法

Core.exe -w ?????.py

单词文件不存在

Core.exe -w aq.txt

在这里插入图片描述

默认模式下单词链成环——

Core.exe -n aaa.txt
Core.exe -w aaa.txt

在这里插入图片描述

单词链算法异常——

单词链中不存在以-h要求开头的字母

Core.exe -w -h o aaa.txt

Apple
Zoo
Elephan
Under
Fox
Dog
Moon
Leaf
Trick
Pseudopseudohypoparathyroidism

在这里插入图片描述

单词链中不存在以-t要求结尾的字母

Core.exe -w -t i aaa.txtt

Apple
Zoo
Elephan
Under
Fox
Dog
Moon
Leaf
Trick
Pseudopseudohypoparathyroidism

在这里插入图片描述

不存在单词链

Core.exe -w  aaa.txt
Core.exe -n aaa.txt

Apple
Zoo
Under
Fox
Dog
Moon

在这里插入图片描述

输出文件错误——

输出文件solution.txt不存在

Core.exe -w aaa.txt

在这里插入图片描述

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

我们的GUI也使用C#进行开发,使用WPF(Windows Presentation Foundation)应用设计UI界面,它提供了统一的编程模型、语言和框架,真正做到了分离界面设计人员与开发人员的工作;同时它提供了全新的多媒体交互用户图形界面,是很多桌面应用开发的首选工具。这也是我们最初选择使用C#开发Core的原因——方便GUI的扩展和连接。

界面设计:

我们导入了几个组件库进行UI的设计和美化

在这里插入图片描述

通过WPF的xaml控件设计对整体界面进行调整

在这里插入图片描述

最终界面展示如下:我们认为在如此短的时间内设计一款样式简洁且美观的UI较为不易,所以也尽可能地精简界面,突出更多的用户友好性。

在这里插入图片描述

界面特点:
  • 颜色搭配美观和谐,功能一目了然,用户友好性强
  • 支持文件导入,用户只需要点击浏览,将自己的文件导入到UI当中就能进行单词文件的自动读取
  • 文本输入,用户可以通过文本输入单词表,后台将自动读入单词
  • 命令选择,提供所有命令组合方便用户进行选择
  • 特殊指令组合,用户可以通过一键点击是否增添环判断以及headtail的约束,通过输入字母进行
  • 一键式生成,用户完成指令点击之后可以点击生成单词链
使用注意:
  • 每次只能生成一条单词输出类型,如果需要更换指令,请更换后重新点击生成单词链
部分代码说明:

WPF通过映射的方式来对应当前的程序集语法:xmlns[:必选映射前缀]=“clr-namespace:[命名空间]”,通过对命令的绑定支持交互,其中WPF支持五个静态类:

ApplicationCommands 该类提供通用命令,包括Copy、Cut、Paste等等。

NavigationCommands 该类提供了导航的命令。

EditingCommands 该类提供了很多主要的文档编辑命令 如MoveToLineEnd、MoveLeftByWord等等。

CommponentCommands 该类提供了用户界面元素使用的命令。

MediaCommands 该类提供了一组用于处理多媒体的命令。

1、按钮组件设计举例

这里举例生成单词链的按钮设计样式

<Button x:Name="buttonCal"
                                Click="startCal"
                                Style="{StaticResource MaterialDesignRaisedLightButton}"
                                VerticalAlignment="Center"
                                HorizontalAlignment="Center"
                                Margin="0,30,0,0"
                                Content="生成单词链"
                                IsEnabled="{Binding DataContext.ControlsEnabled, 
                                RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" 
                                FontFamily="宋体" FontSize="20" FontWeight="SemiBold" Foreground="White"/>
                            </materialDesign:Badged>
2、文本框单词输入设计举例

这里给出了输入单词文本框的设计样式

<TextBox x:Name="textBoxInput" Grid.Row="2"
                                    Style="{StaticResource MaterialDesignOutlinedTextBox}"
                                    VerticalAlignment="Top"
                                    HorizontalAlignment="Center" 
                                    Margin="0,10,0,0" 
                                    AcceptsReturn="True"
                                    TextWrapping="Wrap"
                                    VerticalScrollBarVisibility="Auto"
                                    materialDesign:HintAssist.Hint="This is a text area" 
                                     MinWidth="350" MinHeight="200" 
                                   FontFamily="宋体" FontSize="20" 
                                     IsEnabled="{Binding Path=IsChecked, ElementName=radioButtonText}"/>
3、文件导入

这里给出了文件导入的前后端设计

前端:设计样式+事件点击响应

 <Button x:Name="buttonBrowse"
                                            Style="{StaticResource MaterialDesignRaisedLightButton}"
                                            VerticalAlignment="Center"
                                            HorizontalAlignment="Left"
                                            Margin="60,0,0,0"
                                            Content="浏览..."
                                            Click="getInputFile"
                                            IsEnabled="{Binding Path=IsChecked, ElementName=radioButtonFile}"
                                            FontFamily="宋体" FontSize="20" FontWeight="Medium" Foreground="White"/>

后端:事件点击响应反馈

public void getInputFile(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "TXT Files|*.txt";
            string path = Directory.GetCurrentDirectory();
            if (Directory.Exists(path))
            {
                openFileDialog.InitialDirectory = path;
            }
            else
            {
                openFileDialog.InitialDirectory = @"C:\";
            }

            if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                useFileInput = true;
                string filePath = openFileDialog.FileName;
                inputSource = filePath;
                textBoxInput.Text = filePath;
                Console.WriteLine(filePath);
            }
        }
4、单词链结果展示

结果展示UI设计

<TextBox x:Name="textBoxResult" Grid.Row="2"
                                    Style="{StaticResource MaterialDesignOutlinedTextBox}"
                                    VerticalAlignment="Top"
                                    HorizontalAlignment="Center" 
                                    Margin="0,20,40,0" 
                                    AcceptsReturn="True"
                                    TextWrapping="Wrap"
                                    VerticalScrollBarVisibility="Auto"
                                     Text="待输入"
                                     Foreground="Purple"
                                    materialDesign:HintAssist.Hint="计算结果"
                                     materialDesign:HintAssist.FontFamily="宋体"
                                     materialDesign:HintAssist.HelperTextFontSize="18"
                                     MinWidth="350" MinHeight="180" 
                                   FontFamily="宋体" FontSize="20" 
                                     IsEnabled="False"/>

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

1、中间件预处理

因为UI界面输入的内容是离散的,所以我们在GUI和Core之间设置了一个中间件用来收集前端所传递的参数信息以及单词信息;如果是文本输入单词那么这里直接输入单词数组,如果是文件调用则传递文件路径。

public void startCal(object sender, RoutedEventArgs e)
        {
            if ((bool) radioButtonFile.IsChecked)
                useFileInput = true;
            if ((bool) radioButtonText.IsChecked)
                useFileInput = false;

            if ((bool)radioButton_paraN.IsChecked) 
                calType = 'n';
            else if ((bool)radioButton_paraM.IsChecked)
                calType = 'm';
            else if ((bool)radioButton_paraW.IsChecked)
            {
                calType = 'w';
                isR = (bool)radioButton_paraWR.IsChecked;
                if ((bool)radioButton_paraWH.IsChecked)
                {
                    if (textBoxWH.Text.Length == 0)
                        charH = '!';
                    else if (Char.IsLetter((textBoxWH.Text)[0]))
                        charH = (textBoxWH.Text)[0];
                    else
                        charH = '!';
                }
                if ((bool)radioButton_paraWT.IsChecked)
                {
                    if (textBoxWT.Text.Length == 0)
                        charT = '!';
                    else if (Char.IsLetter((textBoxWT.Text)[0]))
                        charT = (textBoxWT.Text)[0];
                    else
                        charT = '!';
                }
            }
            else if ((bool)radioButton_paraC.IsChecked)
            {
                calType = 'c';
                isR = (bool)radioButton_paraCR.IsChecked;
                if ((bool)radioButton_paraCH.IsChecked)
                {
                    if (textBoxCH.Text.Length == 0)
                        charH = '!';
                    else if (Char.IsLetter((textBoxCH.Text)[0]))
                        charH = (textBoxCH.Text)[0];
                    else
                        charH = '!';
                }
                if ((bool)radioButton_paraCT.IsChecked)
                {
                    if (textBoxCT.Text.Length == 0)
                        charT = '!';
                    else if (Char.IsLetter((textBoxCT.Text)[0]))
                        charT = (textBoxCT.Text)[0];
                    else
                        charT = '!';
                }
            }

            if ((bool)radioButtonText.IsChecked)
                inputSource = textBoxInput.Text;
2、调用Core的DLL过程

由于C#的便利性我们只需要将Core生成后导入GUI的引用中即可

在这里插入图片描述

然后通过我们定义的GUI接口gen_for_gui_para输入我们的前端参数就可以进行调用了

 try
            {
                //Chain.test(useFileInput, inputSource, calType, isR, charH, charT, ref result);
                Chain.gen_for_gui_para(useFileInput, inputSource, calType, isR, charH, charT, ref result);


                string resultText = "";
                for (int i = 0; i < result.Length && result[i] != null && result[i].Length > 0; i++)
                    resultText += result[i] + "\n";

                textBoxResult.Text = resultText;
                Console.WriteLine("result=" + resultText);
            }
            catch (InvalidInputException ex)
            {
                Console.WriteLine("[get in gui]");
                Console.WriteLine(ex.Message);
                textBoxResult.Text = "错误提示:\n" + ex.Message;
            }
            catch (CircleException ex)
            {
                Console.WriteLine("[get in gui]");
                Console.WriteLine(ex.Message);
                textBoxResult.Text = "错误提示:\n" + ex.Message;
            }
            catch (ChainNotFoundException ex)
            {
                Console.WriteLine("[get in gui]");
                Console.WriteLine(ex.Message);
                textBoxResult.Text = "错误提示:\n" + ex.Message;
            }
            catch (Exception ex)
            {
                Console.WriteLine("[get in gui]");
                Console.WriteLine(ex.Message);
                textBoxResult.Text = "错误提示:\n" + ex.Message;
            }

总的来说我认为我们的前后端接口设计是相对规范的,因为这一层中间件处理的存在,我们将前端的复杂信息可以进行整合以及处理,当我们调用计算模块Core的时候只需要用一个接口传递好我们在中间件处理的信息集合即可,在后续我们进行松耦合测试的时候其他组的小伙伴们也可以直接通过调用gen_for_gui_para这一个接口来进行交互。

3、前端演示

在这里插入图片描述

在这里插入图片描述

错误提示在前端的输出:

在这里插入图片描述

十二、结对过程的描述

工程时间表:
时间任务
3.22确定程序语言以及代码管理软件,本来打算使用C++但是考虑到UI设计的便利性,我们还是选择使用C#进行代码编写;在代码托管平台上最终决定使用github;
3.23各自阅读博客进行需求分析,进行解读并且交流构思基础算法,设计整体框架
3.24完成项目初始化并且开始着手进行输入处理
3.25~3.26进行复杂部分的图算法编写以及性能处理
3.26~3.27改进接口设计以及UI设计
3.28处理异常,测试BUG;设计完成动态链接库
3.29处理异常,测试BUG;进行单元覆盖性测试
3.30~4.5博客撰写;松耦合测试(与其他小组)
沟通记录:

沟通方式主要以微信沟通和几次线下会面进行工程设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

GitHub平台的Commit记录:

在这里插入图片描述

团队工具使用:Notion

我们使用了Notion来记录关于算法的一些伪代码和画图思路;并将算法步骤记录在上,两人同时编辑更新。

在这里插入图片描述

十三、结对编程的感悟

结对编程的体验:
  • 优点:
    1. 两人相互讨论,可以快速解决问题及进行知识共享,错误解决效率很高
    2. 提高了产品质量,两人之间能进行想法、设计、编码的互补
    3. 对个人能力的锻炼体现充分,因为时间紧、任务重,两个人的工作量都不小,所以会在高压中进行任任务推进,对个人能力的锻炼较为充分
    4. 有助于项目进度的推进,因为两人即使有效时间错开但是任务推进也不会停止
  • 缺点:
    1. 开发者之间可能会产生架构和细节上的分歧
    2. 如果两人都不够积极或者两人水平相差过大,会影响团队和谐以及任务进度推进
    3. “一看一写”的结对形式可能会降低编码的效率
本人在结对编程中的表现:
  • 优点:
    1. 能够主动承担任务,并战斗数个小时
    2. 计划性和任务推进的紧张度较强
    3. 主动积极地交流与沟通
  • 缺点:
    1. 相比于队友的细腻设计,在编码阶段丢失了细节和架构设计
    2. 容易在任务过程中对博客内容理解产生偏差
队友在结对编程中的表现:
  • 优点:
    1. 能够主动承担任务,并战斗数个小时
    2. 认真负责分析任务内容,记录设计思路
    3. 架构设计能力好,心思细腻
    4. 善于交流沟通,提前沟通好进行松耦合测试的小组
  • 缺点:
    1. 基本没有缺点,硬要说的话就是早上沟通回复较慢,但是极大可能因为前一天晚上为了任务推进熬夜太迟,过于认真负责,不注意劳逸结合。

十四、实际工程时间(详见PSP2.1)

十五、UI模块和Core模块松耦合测试

  • 我们组对接的B组是由19373573和18373466两位同学组成的结对小组

  • 我们两组很早就确定了合作关系,因为很巧我们都选择了使用C#来开发,同样在GUI部分都使用了WPF框架进行设计,于是我们提前订好了GUI和Core的调用结构命名和参数格式,最后的耦合过程较为成功

    下方代码为GUI调用Core的接口函数:

    gen_for_gui_para(useFileInput, inputSource, calType, isR, charH, charT, ref result);
    
  • 只需要将另一组同学的Core生成的dll导入到C#,然后重新生成一遍就可以了

    B组的Core我们命名为HYCore

在这里插入图片描述

  • 为了更加方便的展示另一小组的内核我们将我们的GUI进行了拓展,使得另一小组的效果能很好地展示,我们的Core的运行效果在B组同学的博客当中。
举报

相关推荐

0 条评论