我现在正在MOOC中学习Java,这是我练习成绩统计的解决方案。
这是一项工作:
在本练习中,我们创建了一个用于打印课程中点数的统计数据的程序。该程序接收点数(整数从0到100)作为输入,并在此基础上打印有关年级的统计数据。当用户输入数字-1时,输入的读取停止。在计算统计数据时,不应考虑不在间隔0-100内的数字。第1部分:平均值,编写一个程序,读取来自用户的表示课程点数总和的整数。0-100之间的数字是可以接受的,数字-1结束输入的读取.其他数字是错误的输入,应该忽略。当用户输入数字-1时,程序应该打印输入点总数的平均值。第二部分给出及格分数的点平均值扩展了程序,这样除了给出所有总分的点平均值外,还提供了给出及格分数的点平均值。一个及格的分数是通过获得至少50个课程的分数。您可以假设用户总是在0-100之间提供至少一个整数。如果没有给出及格分数的数字,程序应该打印一行"-“在那里的平均值。第3部分传递百分比扩展了程序的前一部分,因此它也打印了通过率。通过率是使用公式100 *传递/参与者计算的。第4部分扩展了程序的等级分布,使得它还打印出了基于上表的年级distribution.Each点数总数转换为一个等级。如果一个点总数不在0-100中,则应该忽略它。等级分布被打印成星星。如果五年级有一个总分,那么它应该打印第5行:*。如果没有一个特定等级的点数总数,那么就不应该为它打印任何星星。在下面的样本中,这对四年级的学生来说是正确的。
这是我的解决办法:
Main.java:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Write your program here -- consider breaking the program into
// multiple classes.
UserInterface ui = new UserInterface(scanner);
ui.start();
}
}PointsStatistics.java:
import java.util.ArrayList;
public class PointsStatistics {
private ArrayList<Integer> allpoints;
private ArrayList<Integer> passingpoints;
public PointsStatistics() {
this.allpoints = new ArrayList<>();
this.passingpoints = new ArrayList<>();
}
public void add(int points) {
if (points > 0 && points >= 50 && points <= 100) {
this.passingpoints.add(points);
}
if (points > 0 && points <= 100) {
this.allpoints.add(points);
}
}
public double pointsAverage() {
double sum = 0.0;
for (Integer points : this.allpoints) {
sum += points;
}
return sum / this.allpoints.size();
}
public double possingPointsAverage() {
double sum = 0.0;
for (Integer points : this.passingpoints) {
sum += points;
}
return sum / this.passingpoints.size();
}
public double passingPercentage() {
double percentage = (this.passingpoints.size() * 1.0) / this.allpoints.size();
return percentage * 100;
}
public void gradeDistribution() {
ArrayList<Integer> grades = new ArrayList<>();
for (Integer points : this.allpoints) {
if (points < 50) {
grades.add(0);
} else if (points < 60) {
grades.add(1);
} else if (points < 70) {
grades.add(2);
} else if (points < 80) {
grades.add(3);
} else if (points < 90) {
grades.add(4);
} else if (points <= 100) {
grades.add(5);
}
}
for (int i = 5; i >= 0; i--) {
System.out.print(i + ": ");
for(Integer grade : grades) {
if (grade == i) {
System.out.print("*");
}
}
System.out.println("");
}
}
}UserInterface.java
import java.util.Scanner;
public class UserInterface {
private PointsStatistics points;
private Scanner scanner;
public UserInterface(Scanner scanner) {
this.points = new PointsStatistics();
this.scanner = scanner;
}
public void start() {
System.out.println("Enter point totals, -1 stops: ");
while(true) {
int enterPoints = Integer.valueOf(scanner.nextLine());
if( enterPoints == -1) {
break;
}
this.points.add(enterPoints);
}
System.out.println("Point average (all): " + this.points.pointsAverage());
System.out.println("Points average (passing): " + this.points.possingPointsAverage());
System.out.println("Pass percentage: " + this.points.passingPercentage());
points.gradeDistribution();
}
}你怎么看?它有效,但它是一个最佳的解决方案吗?谢谢你。
发布于 2021-12-01 17:58:38
Java代码应该放在一个名为“避免与其他开发人员冲突”的包中。最好的做法是对你拥有的一些域/帐户/电子邮件进行反向读取。
package com.stackexchange.codereview.srxxx.grading;对遵循典型的Java命名约定表示赞赏。
在引入变量名称时要非常小心,使它们尽可能地自我解释.
一个改进:在passingPercentage()中,您有一个名为percentage的变量,但它不在与该单词关联的0..100范围内,而是在0到1之间。所以,我将它称为比率,而不是百分比。
这听起来像是吹毛求疵,但相信我,无论是在一个团队工作,还是在一个长期存在的软件项目中,这都是最重要的技能。
您主要遵循计算(类PointsStatistics)和用户界面(类UserInterface)之间的关注点分离。
只对gradeDistribution()来说,您可以在PointsStatistics中执行System.out.print()。您应该更改方法以返回年级列表(grades变量),并从UserInterface类执行打印循环。
为公共类和方法编写文档注释(Javadoc样式),记录他们的任务(而不是他们如何完成任务)。
要获得好的Javadoc示例,请看一下Java类,您肯定会在Java8 8平台规范这样的网页上使用HTML生成的文档结果。在Java开发的20+年中,我很少需要研究Java源代码,Javadocs很好地解释了所有预先存在的Java类和方法。
声明变量
你应该换衣服
ArrayList<Integer> grades = new ArrayList<>();成为
List<Integer> grades = new ArrayList<>();一般规则是使用提供所需方法和行为的最一般类型(或接口)声明变量,并使用您(当前)希望使用的具体类对其进行初始化。
为什么?要么相信我,这是最好的做法,或阅读以下冗长的解释。
所有代码都将用于任何类型的List,可能是LinkedList或包装为List的数组(使用Arrays.asList())。也许,稍后您会发现一个超酷的新List实现,您希望将ArrayList与该类交换。然后,您只需将初始化部分交换到现在读取。
List<Integer> grades = new SuperCoolList<>();一切都是一样的,只是很酷。
您可能会问,为什么不同时进行初始化和声明:
SuperCoolList<Integer> grades = new SuperCoolList<>();风险在于,使用初始声明ArrayList<Integer> grades,没有什么可以阻止您调用例如grades.trimToSize(),它不是List接口的一部分,而是特定于ArrayList。有两种可能的情况:
SuperCoolList不会有这样的方法,从而导致编译错误。您会看到错误,花费一些时间来替换方法调用,或者发现您无法使用SuperCoolList并返回到ArrayList。SuperCoolList可能有这样的方法,但却在做一些不同的事情。然后,程序将在没有得到编译器任何提示的情况下不正常运行。使用
List<Integer> grades = new ArrayList<>();这是不可能的。在grades变量上调用的每个方法都将出现在每种List实现中,并且行为都是相同的。您不能意外地使用grades.trimToSize()方法,因为这不是List接口的一部分。这样,编译器就可以保证以后更改为不同的List实现的自由。
https://codereview.stackexchange.com/questions/270585
复制相似问题