软件构造lab1

 

实验目标概述

本次实验通过求解三个问题,训练基本 Java 编程技能,能够利用 Java OO

发基本的功能模块,能够阅读理解已有代码框架并根据功能需求补全代码,能够

为所开发的代码编写基本的测试程序并完成测试,初步保证所开发代码的正确性。

另一方面,利用 Git 作为代码配置管理的工具,学会 Git 的基本使用方法。

l 基本的 Java OO 编程

l 基于 Eclipse IDE 进行 Java 编程

l 基于 JUnit 的测试

l 基于 Git 的代码配置管理

实验环境配置

 

简要陈述你配置本次实验所需开发、测试、运行环境的过程,必要时可以给出屏幕截图。

特别是要记录配置过程中遇到的问题和困难,以及如何解决的。

 

在这里给出你的GitHub Lab1仓库的URL地址。

https://github.com/ComputerScienceHIT/HIT-Lab1-120L020509

实验过程

请仔细对照实验手册,针对四个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但无需把你的源代码全部粘贴过来!)

为了条理清晰,可根据需要在各节增加三级标题。

3.1 Magic Squares

此程序需要根据给出的文件路径读取文件,并按规定的输入格式进行拆分处理,判断输入是否满足格式。在几次循环中判断各个行、列、对角线之和是否相等,返回表示是否为magic square的boolean变量。

3.1.1 isLegalMagicSquare()

首先创建一个BufferReader对象,用bufferedReader.readLine()方法按行读取文件,用.split("\t")分割数据,并通过正则表达式匹配判断是否符合输入格式。若符合格式,则使用Integer.valueOf()将输入转换为整数,储存在一维动态数组line中,每读取完一行,将这一行储存在二维动态数组matrix中,从而实现了从文件到二维数组matrix之间的转化。

然后对矩阵matrix进行遍历,在几次循环中判断各个行、列、对角线之和是否相等,是否有重复数字,矩阵大小是否为n*n

 

3.1.2 generateMagicSquare()

流程图:

 

 

解释:程序根据n的大小生成一个二维数组,并在for循环中遍历n*n次,给每个位置赋值。遍历之前会将开始位置赋初值。在第i次遍历中,先将当前位置赋值为i,然后对i的大小进行判断,如果in的整数倍,则下移一行;否则上移一行并右移一列,若超过边界则移动到对侧。

ArrayIndexOutOfBoundsException:用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。

产生该异常原因:当n为偶数时,存在i % n == 0row == n - 1同时成立的情况,此时执行到if (i % n == 0)row++;会导致row = n,从而出现数组越界。

 

NegativeArraySizeException:如果应用程序试图创建大小为负的数组,则抛出该异常。

产生该异常原因:magic[][]初始化时的大小为n*n,如果输入的n为负数,将会尝试创建一个大小为负数的数组,显然不合法。

 

 

3.2 Turtle Graphics

需要按照网站上的实验手册逐步完善turtle的各个方法并执行测试,构建一个完整的Turtle类。

3.2.1 Problem 1: Clone and import

在获取P1txt文件时已经通过git clone https://github.com/rainywang/Spring2022_HITCS_SC_Lab1命令克隆了包括P1P2的整个文件夹。

Git仓库已在完成P1时创建完成,故只需要将clone下来的P2文件夹复制到src文件夹下。

使用git管理本地开发主要使用git statusgit add .git commit -m “”git push四个指令。

 

3.2.2 Problem 3: Turtle graphics and drawSquare

要求调用forwardturn画正方形,只需要画四条直线,四次转向90°(第四次转向可有可无),代码如下:

turtle.forward(sideLength);
turtle.turn(90);
turtle.forward(sideLength);
turtle.turn(90);
turtle.forward(sideLength);
turtle.turn(90);
turtle.forward(sideLength);

turtle.turn(90);

也可以通过循环来实现。

for (int i = 0; i < 4; i++){

turtle.forward(sideLength);
turtle.turn(90);

}

3.2.3 Problem 5: Drawing polygons

  1. 只需要返回多边形内角公式计算后的结果,需要注意返回值的类型是double。代码如下:return (double)180 * (sides - 2) / sides;
  2. 说明中要求右键TurtleSoupTest.java选择Run As,但IDEA中没有这个选项,所以我打开了TurtleSoupTest.java,在calculateRegularPolygonAngleTest方法左侧点击运行按钮,结果如下:

 

  1. 要求破坏写好的calculateRegularPolygonAngle方法,故将代码修改为return 180 * (sides - 2) / sides;,再次运行calculateRegularPolygonAngleTest方法,出现以下错误提示信息:

 

 

  1. 恢复calculateRegularPolygonAngle方法。再次执行测试以验证正确性:

 

  1. 调用calculateRegularPolygonAngle获得转向角度,通过for循环画出sides条边。代码如下:

double angle = 180 - calculateRegularPolygonAngle(sides);
for (int i = 0; i < sides; i++){
    turtle.forward(sideLength);
    turtle.turn(angle);
}

main方法中通过drawRegularPolygon(turtle,8,40);尝试画边长为40的正八边形,结果如下:

 

 

3.2.4 Problem 6: Calculating Bearings

a.通过向量夹角公式计算相应的转角,代码如下:

int dx = targetX - currentX;
int dy = targetY - currentY;
double vortexBearing = Math.toDegrees(Math.acos((double)dy / Math.sqrt((double)(dx * dx + dy * dy))));
if (dx < 0) {
     vortexBearing = 360.0 - vortexBearing;
}
return (vortexBearing - currentBearing + 360.0) % 360.0;

需要注意的是要根据dx的大小、vortexBearing - currentBearing的正负对结果进行一些处理。

 

b.调用calculateBearingToPoint计算转角,代码如下:

int size = xCoords.size();
double currentBearing = 0;
int currentX = xCoords.get(0),currentY = yCoords.get(0);
List<Double> ans = new ArrayList<Double>();
for (int i = 1; i < size; i++){
     int targetX = xCoords.get(i),targetY = yCoords.get(i);
     double temp = calculateBearingToPoint(currentBearing,currentX,currentY,targetX,targetY);
    ans.add(temp);
    currentX = targetX;
    currentY = targetY;
    currentBearing = (currentBearing + temp) % 360;

c.测试结果如下:

 

 

3.2.5 Problem 7: Convex Hulls

根据对题意的分析,结合测试用例,当点数小于4时,直接返回输入的点。

使用gift-wrapping algorithm来计算凸包,核心代码如下:

public static double cross(Point p, Point q, Point r) {
    return (q.x() - p.x()) * (r.y() - q.y()) - (q.y() - p.y()) * (r.x() - q.x());
}

public static Set<Point> convexHull(Set<Point> points) {
    Set<Point> ans = new HashSet<Point>();

    int n = points.size();
    if (n < 4) return points;//若无法构成封闭图形或构成三角形,返回points

    List<Point> temp = new ArrayList<Point>();//将points中的point保存到ArrayList中,方便遍历
    for (Point t : points){
        temp.add(t);
    }

    int leftMost = 0;//找出最左下侧的点作为起点
    for (int i = 0; i < n; i++) {
        if (temp.get(i).x() < temp.get(leftMost).x() || temp.get(i).x() == temp.get(leftMost).x() && temp.get(i).y() < temp.get(leftMost).y()) {
            leftMost = i;
        }
    }

    ans.add(temp.get(leftMost));
    //Set<Integer> visited = new HashSet<Integer>();

    int p = leftMost;
    do {
        int q = (p + 1) % n;
        for (int r = 0; r < n; r++) {
            //if (visited.contains((r))) continue;
            //如果r在pq的右侧,或pqr共线且r距离p更远,则q = r
            if (cross(temp.get(p), temp.get(q), temp.get(r)) < 0 ||
                    cross(temp.get(p), temp.get(q), temp.get(r)) == 0 &&
                            Math.pow(temp.get(r).x() - temp.get(p).x(),2) + Math.pow(temp.get(r).y() - temp.get(p).y(),2) > Math.pow(temp.get(q).x() - temp.get(p).x(),2) + Math.pow(temp.get(q).y() - temp.get(p).y(),2)) {
                q = r;
            }
        }
        //visited.add(q);
        ans.add(temp.get(q));
        p = q;
    }while (p != leftMost);
    return ans;
}

 

3.2.6 Problem 8: Personal art

选择用多种颜色画多个圆。代码如下:

for (int i = 0; i < 10; i++){
     turtle.color(PenColor.values()[i]) ;
    for (int j = 0; j < 10; j++){
        drawRegularPolygon(turtle,360,1);
        turtle.turn(3.6);
    }
}

效果如下:

 

 

 

3.2.7 Submitting

在实验开始时已经通过git remote add命令添加远程库。要通过Git提交当前版本到GitHubLab1仓库只需要执行git add .  +  git commit -m “” + git push这三条指令即可。

3.3 Social Network

任务要求实现FriendshipGraphPerson类,使其满足给定的main方法的调用。

3.3.1 设计/实现FriendshipGraph

需要按要求编写addVertexaddEdgegetDistance方法。

使用HashMap<Person,ArrayList<Person>>这种数据结构,可以同时用来保存顶点和边。addVertexaddEdge都通过对(人,出边)的键值对的修改来实现。

getDistance方法按照要求,使用广度优先搜索进行遍历,判断两点之间距离。

异常处理:

在addVertex方法中判断对应的名字是否已经添加,如果已经添加,则打印错误提示信息。

在addEdge方法中判断对应的边是否已经添加,如果已经添加,则打印错误提示信息。

在getDistance方法中判断对应的两个人是否已经添加,如果有人未添加添加,则打印错误提示信息。

 

 

3.3.2 设计/实现Person

Person类中只需要保存每个人的名字,所以代码如下:

class Person {
     private String name;
     Person(String name){
         this.name = name;
     }
}

 

3.3.3 设计/实现客户端代码main()

main的代码已在实验手册中给出。

3.3.4 设计/实现测试用例

addVertex主要测试加入重复的人名是否按设计打印错误提示

addEdge主要测试缺少顶点或加入重复边的异常处理

getDistance主要测试是否能够正常计算距离,包括0-1等特殊情况

 

要获取打印的错误提示,需要通过System.setOut(new PrintStream(bytes))来实现。

 

结果:

 

 

实验进度记录

日期

时间段

任务

实际完成情况

2022-04-30

23:00-24:00

编写问题1的isLegalMagicSquare核心功能,理解generateMagicSquare的功能

延期半小时完成

2022-05-01

17:00-18:30

完善了问题1的异常处理等部分并编写报告

按计划完成

2022-05-01

19:00-24:00

完成问题2并编写报告

基本按计划完成,但没有加异常处理

2022-05-02

13:00-15:40

完善问题2,完成问题3的核心部分

按计划完成

2022-05-04

14:00-16:00

完善问题3的异常处理等部分

按计划完成

2022-05-05

16:00-16:45

编写问题3测试

按计划完成

2022-05-08

16:00-17:00

完善报告

按计划完成

实验过程中遇到的困难与解决途径

遇到的困难

解决途径

 

使用常用的FileInputStream难以实现文件的按行读取

 

参考博客https://blog.csdn.net/weixin_45865491/article/details/117619612,使用BufferedReader解决

 

 

问题3getDistance

方法中出现了一个bug,导致程序陷入死循环

经过不断地调试和修改,发现在广度优先搜索初始化中间变量的时候,将其初始化为对边的引用,导致后续对栈操作时,对边进行了修改,进入死循环。

修改:将其初始化为一个空数组,并赋初值。

ArrayList<Person> reach = new ArrayList<Person>();
reach.add(person1);

无法通过junit中的方法直接判断控制台输出是否正确

参考https://www.cnblogs.com/owczhlol/p/12655046.html,通过System.setOut(new PrintStream(bytes));

获取到控制台输出。

实验过程中收获的经验教训、感想

6.1 实验过程中收获的经验教训(必答)

经验:原来Java使用的较少,经过本次试验,对Java相关的知识更加熟练。熟悉了git的相关操作。经过实践学习了junit的测试方法。

 

教训:不要轻易把一个对象赋值为另一个对象的引用,尤其是当需要修改这个对象时,否则很容易出bug

 

6.2 针对以下方面的感受(必答)

(1) Java编程语言是否对你的口味?与你熟悉的其他编程语言相比,Java有何优势和不足?

Java比较合我口味。

c语言相比,java没有指针不够灵活,性能较差,占用内存较大;但Java是面向对象的语言,适合大型软件开发,安全性好,可移植性好,而c语言可移植性不好,在不同平台上的宏定义不同,程序要根据机器的指令集、位宽等进行调整。

python相比,java性能强很多,但没有python方便,开发效率较低。

(2) 关于EclipseIntelliJ IDEA,它们作为IDE的优势和不足;

我平时使用较多的是VS Code。与之比较而言,IDEA整合程度更高,集成了丰富的功能,能够提高开发效率,但功能丰富也带来了不足:启动时间较长。

(3) 关于GitGitHub,是否感受到了它在版本控制方面的价值;

感受到了。在一次代码修改中发现修改的不好,通过git checkout命令很方便地将修改撤回。

(4) 关于CMUMIT的作业,你有何感受;

实验要求很清晰,相关文档直接放在实验指导的对应位置,方便查看资料。

(5) 关于本实验的工作量、难度、deadline

工作量较大,难度中等,这周恰好有考试,deadline比较紧。

(6) 关于初接触“软件构造”课程;

目前对软件构造的认识还不是很深刻,前几节课主要讲了概念方面的内容,希望在几次实验之后能把学到的概念应用在实践中。

posted @   gtz123456  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示