第二次blog作业
一、前言:
题目集八、九相较于之前的电梯调度问题在难度上要小了不少,在算法上的要求不算很高,但对结构的设计要求更高,往往有了构筑好了结构便能够轻松的解决问题,但新概念的引入,还是为我带来了一些困难,接下来我将展示我在这两次题目集中的历程。
二、问题分析:
题目集八的作业 1、2:
作业1让我做一个点、线、面的图形系统,主要学的是抽象类和继承。老师给了个Element抽象类,要求我们写Point、Line、Plane三个子类,分别实现显示坐标、计算线段长度和展示颜色的功能。输代码大概80行,不算多,但输出格式必须严格按题目要求,一个小点不对都会错,调了好久才搞定。作业2难度明显上来了,要做一个调速系统,得用接口设计两种方法。比如控制杆和旋钮的位置变化会触发不同速度计算,这里用了很多switch嵌套,写的时候容易漏case,特别是高级策略多两个档位,测试的时候得一个个试。写代码要求,边写边理清类之间的关系,比如Agent类怎么协调策略和组件状态,一开始没想通就卡住了好久。两次作业从基础类设计到策略切换,感觉对抽象的理解更深了,但作业2的switch确实有点重复,另外测试用例没覆盖到边界情况,下次注意这些细节。
题目集九的作业 1、2:
第四次作业是做几何体计算,比如立方体和正四棱锥的面积体积,再搞一个魔方类组合这些几何体,根据层数放大边长。一开始被数学公式搞懵了,比如正四棱锥体积公式里的根号2,照着公式硬写出来结果总是不对,后来发现是括号没打全。魔方类的设计也不容易,得把几何体对象传进去,还要算放大后的总边长,写的时候老忘记乘层数。第五次作业是动态管理几何对象系统,可以随时添加点、线、面,还能删指定位置的元素。这次用了集合存所有图形对象,输入指令1到4对应不同操作,比如输1加一个点,输4删第几个元素。最头疼的是输入处理,操作指令和参数得按固定顺序输,少一个就报错,删元素的时候下标从1开始,但代码里集合从0开始,调试时忘记减1直接越界崩溃。两次业都用了继承和多态,但第四次数学公式容易写错,第五次要边读输入边改集合,稍不留神就出bug,尤其是删元素后遍历集合可能会漏掉后面的项,后来用了临时列表才解决。
第一次航空货运管理系统:
点击查看代码
import java.util.*;
class Person{
private String id;
private String name;
private String number;
private String dizhi;
public Person(String id, String name, String number, String dizhi){
this.id=id;
this.name=name;
this.number=number;
this.dizhi= dizhi;
}
public String getName(){
return name;
}
public String getNumber(){
return number;
}
public void setId(String id){
this.id=id;
}
public void setName(String name){
this.name=name;
}
public void setNumber(String number){
this.number=number;
}
public void setDizhi(String dizhi){
this.dizhi =dizhi;
}
}
class Huowu{
private String id;
private String name;
private double length;
private double width;
private double height;
private double grossweight;
public Huowu(String id,String name,double length,double width,double height, double grossweight) {
this.id=id;
this.name=name;
this.length=length;
this.width=width;
this.height=height;
this.grossweight=grossweight;
}
private double getVolumeWeight(){
return (length*width*height)/6000;
}
public double getMweight(){
return Math.max(getVolumeWeight(),grossweight);
}
public double getRate(){
return getMweight()>=50?25:30;
}
public double getMoney(){
return getMweight()*getRate();
}
public String getName(){
return name;
}
public void setId(String id){
this.id=id;
}
public void setName(String name){
this.name=name;
}
public void setLength(double length){
this.length=length;
}
public void setWidth(double width){
this.width=width;
}
public void setHeight(double height){
this.height=height;
}
public void setGrossweight(double grossweight){
this.grossweight=grossweight;
}
}
class Fly {
private String fNumber;
private double maxWeight;
public Fly(String fNumber,double maxWeight){
this.fNumber =fNumber;
this.maxWeight=maxWeight;
}
public String getFNumber(){
return fNumber;
}
public double getMaxWeight(){
return maxWeight;
}
public void setFNumber(String fNumber){
this.fNumber=fNumber;
}
public void setMaxWeight(double maxWeight){
this.maxWeight=maxWeight;
}
}
public class Main{
public static void main(String[] args){
Scanner scanner=new Scanner(System.in);
String Id=scanner.nextLine();
String Name=scanner.nextLine();
String Number=scanner.nextLine();
String Dizhi=scanner.nextLine();
Person person=new Person(Id, Name, Number, Dizhi);
int HuowuCount=Integer.parseInt(scanner.nextLine());
List<Huowu> huowus=new ArrayList<>();
for (int i=0;i<HuowuCount;i++){
String HuoId=scanner.nextLine();
String name=scanner.nextLine();
double length=Double.parseDouble(scanner.nextLine());
double width=Double.parseDouble(scanner.nextLine());
double height=Double.parseDouble(scanner.nextLine());
double Grossweight=Double.parseDouble(scanner.nextLine());
huowus.add(new Huowu(HuoId,name,length,width,height,Grossweight));
}
String flNumber=scanner.nextLine();
scanner.nextLine();//起飞
scanner.nextLine();//降落
scanner.nextLine();//日期
double maxWeight=Double.parseDouble(scanner.nextLine());
Fly fly=new Fly(flNumber,maxWeight);
String FaId=scanner.nextLine();
String FaDate=scanner.nextLine();
String FaDizhi=scanner.nextLine();
String FaName=scanner.nextLine();
String FaNumber=scanner.nextLine();
String ShouDizhi=scanner.nextLine();
String ShouName= scanner.nextLine();
String ShouNumber=scanner.nextLine();
double totalWeight=0;
for (Huowu huowu:huowus){
totalWeight+=huowu.getMweight();
}
if (totalWeight>fly.getMaxWeight()){
System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.\n", fly.getFNumber());
return;
}
System.out.printf("客户:%s(%s)订单信息如下:\n",person.getName(), person.getNumber());
System.out.println("-----------------------------------------");
System.out.printf("航班号:%s\n",fly.getFNumber());
System.out.printf("订单号:%s\n",FaId);
System.out.printf("订单日期:%s\n",FaDate);
System.out.printf("发件人姓名:%s\n",FaName);
System.out.printf("发件人电话:%s\n",FaNumber);
System.out.printf("发件人地址:%s\n",FaDizhi);
System.out.printf("收件人姓名:%s\n",ShouName);
System.out.printf("收件人电话:%s\n",ShouNumber);
System.out.printf("收件人地址:%s\n",ShouDizhi);
System.out.printf("订单总重量(kg):%.1f\n",totalWeight);
double totalMoney=0;
for (Huowu huowu:huowus){
totalMoney+=huowu.getMoney();
}
System.out.printf("微信支付金额:%.1f\n\n",totalMoney);
System.out.println("货物明细如下:");
System.out.println("-----------------------------------------");
System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
int index=1;
for (Huowu huowu : huowus){
System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n",index++,huowu.getName(),huowu.getMweight(),huowu.getRate(),huowu.getMoney());
}
}
}
类图设计:

写题历程:
一、输入处理不够好,中间有一行输错了顺序,我之前把货物数量填到客户地址的位置,一直无法通过测试。忘记检查输入合法性,导致输入程序时,会生成一堆负数重量的货物。以致于我的代码拿不全分数。
二、运费费率的分段条件写反了。题目要求超过50公斤的货物用更便宜的费率,但我一开始代码里写成“大于等于50”就降价,后来测试发现50公斤的货物运费算错了,赶紧改成“严格大于50”,但改的时候漏了一处地方,有个三元运算符还是用了>=,导致部分50公斤货物还是按低价算,最后无法拿到群发。这种条件判断的细节一不留神就出错,尤其是代码里多个地方用到费率计算的时候。
解决这些问题的时候,一开始到处补漏洞。比如输入问题,最开始只是在读数据后加了一行if判断货物数量不能为负,但后来发现用户可能输字母,又手忙脚乱地加try-catch包住所有数值解析。后来学聪明了,把输入校验抽成一个方法,专门处理正数检查和格式转换,这才让代码稍微能看。不过时间不够,运费计算部分还是留着double,只能祈祷测试用例别出极端小数。
分析SourceMonitor报表

优点:
代码行数为 170,相对简洁。
总行数 170(行业参考值 <=200),规模可控,无明显冗余代码,整体结构紧凑。
方法平均语句数为 6.25,方法长度较为适中。
方法平均语句数 6.25(参考值 <=8),逻辑块拆分合理,单个方法代码量少,符合简洁性原则,便于维护和调试。
分支语句占比低,逻辑清晰。
分支语句占比 3.2%(参考值 <=15%),条件判断少,代码结构简单,减少嵌套复杂度,提升可读性,降低维护成本。
最大复杂度低,执行路径清晰。
最大复杂度为 2(参考值<=5),逻辑简单,无多层嵌套循环或条件,代码执行路径明确,调试和理解成本低。
缺点:
注释覆盖率极低,可读性差。
仅 2.4% 的代码行含注释(参考值>=20%),关键业务逻辑(如货物重量计算、运费累加规则、订单输出格式)缺乏说明后续维护者难以快速理解代码意图,尤其是复杂业务场景(如多货物明细的循环输出)未标注,增加理解成本。
第二次航空货运管理系统:
点击查看代码
import java.util.*;
abstract class User {
protected Person person;
public User(Person person) {
this.person = person;
}
public abstract double getDiscountRate();
public String getCustomerInfo() {
return person.getName() + "(" + person.getNumber() + ")";
}
}
class PersonalUser extends User {
public PersonalUser(Person person) {
super(person);
}
@Override
public double getDiscountRate() {
return 0.9;
}
}
class CorporateUser extends User {
public CorporateUser(Person person) {
super(person);
}
@Override
public double getDiscountRate() {
return 0.8;
}
}
abstract class Cargo {
protected String id;
protected String name;
protected double length;
protected double width;
protected double height;
protected double grossWeight;
public Cargo(String id, String name, double length, double width,
double height, double grossWeight) {
this.id = id;
this.name = name;
this.length = length;
this.width = width;
this.height = height;
this.grossWeight = grossWeight;
}
public double getVolumeWeight() {
return (length * width * height) / 6000;
}
public double getChargeableWeight() {
return Math.max(getVolumeWeight(), grossWeight);
}
public abstract double getRate();
public double getFreight() {
return getChargeableWeight() * getRate();
}
public String getName() {
return name;
}
}
class GeneralCargo extends Cargo {
public GeneralCargo(String id, String name, double length, double width,
double height, double grossWeight) {
super(id, name, length, width, height, grossWeight);
}
@Override
public double getRate() {
double weight = getChargeableWeight();
if (weight < 20) return 35;
if (weight < 50) return 30;
if (weight < 100) return 25;
return 15;
}
}
class DangerousCargo extends Cargo {
public DangerousCargo(String id, String name, double length, double width,
double height, double grossWeight) {
super(id, name, length, width, height, grossWeight);
}
@Override
public double getRate() {
double weight = getChargeableWeight();
if (weight < 20) return 80;
if (weight < 50) return 50;
if (weight < 100) return 30;
return 20;
}
}
class ExpressCargo extends Cargo {
public ExpressCargo(String id, String name, double length, double width,
double height, double grossWeight) {
super(id, name, length, width, height, grossWeight);
}
@Override
public double getRate() {
double weight = getChargeableWeight();
if (weight < 20) return 60;
if (weight < 50) return 50;
if (weight < 100) return 40;
return 30;
}
}
class Flight {
private String number;
private double maxLoad;
public Flight(String number, double maxLoad) {
this.number = number;
this.maxLoad = maxLoad;
}
public String getNumber() {
return number;
}
public double getMaxLoad() {
return maxLoad;
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Person customer = new Person(
sc.nextLine(), sc.nextLine(),
sc.nextLine(), sc.nextLine()
);
User user = createUser(sc.nextLine(), customer);
List<Cargo> cargos = new ArrayList<>();
int cargoCount = Integer.parseInt(sc.nextLine());
for (int i = 0; i < cargoCount; i++) {
String type = sc.nextLine();
cargos.add(createCargo(type, sc));
}
Flight flight = new Flight(
sc.nextLine(),
Double.parseDouble(sc.nextLine())
);
sc.nextLine(); sc.nextLine(); sc.nextLine();
double totalWeight = cargos.stream()
.mapToDouble(Cargo::getChargeableWeight)
.sum();
if (totalWeight > flight.getMaxLoad()) {
System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.\n",
flight.getNumber());
return;
}
String[] orderInfo = new String[8];
for (int i = 0; i < 8; i++) {
orderInfo[i] = sc.nextLine();
}
double totalFreight = cargos.stream()
.mapToDouble(Cargo::getFreight)
.sum();
totalFreight *= user.getDiscountRate();
printOrder(user, cargos, flight, orderInfo, totalWeight, totalFreight);
}
private static User createUser(String type, Person person) {
switch (type) {
case "个人": return new PersonalUser(person);
case "集团": return new CorporateUser(person);
default: throw new IllegalArgumentException("无效的用户类型");
}
}
private static Cargo createCargo(String type, Scanner sc) {
String[] params = new String[6];
for (int i = 0; i < 6; i++) {
params[i] = sc.nextLine();
}
switch (type) {
case "普通货物":
return new GeneralCargo(params[0], params[1],
Double.parseDouble(params[2]),
Double.parseDouble(params[3]),
Double.parseDouble(params[4]),
Double.parseDouble(params[5]));
case "危险货物":
return new DangerousCargo(params[0], params[1],
Double.parseDouble(params[2]),
Double.parseDouble(params[3]),
Double.parseDouble(params[4]),
Double.parseDouble(params[5]));
case "加急货物":
return new ExpressCargo(params[0], params[1],
Double.parseDouble(params[2]),
Double.parseDouble(params[3]),
Double.parseDouble(params[4]),
Double.parseDouble(params[5]));
default: throw new IllegalArgumentException("无效的货物类型");
}
}
private static void printOrder(User user, List<Cargo> cargos, Flight flight,
String[] orderInfo, double totalWeight, double totalFreight) {
System.out.println("客户:" + user.getCustomerInfo() + "订单信息如下:");
System.out.println("-----------------------------------------");
System.out.println("航班号:" + flight.getNumber());
System.out.println("订单号:" + orderInfo[0]);
System.out.println("订单日期:" + orderInfo[1]);
System.out.println("发件人姓名:" + orderInfo[3]);
System.out.println("发件人电话:" + orderInfo[4]);
System.out.println("发件人地址:" + orderInfo[2]);
System.out.println("收件人姓名:" + orderInfo[5]);
System.out.println("收件人电话:" + orderInfo[6]);
System.out.println("收件人地址:" + orderInfo[7]);
System.out.printf("订单总重量(kg):%.1f\n", totalWeight);
System.out.printf("微信支付金额:%.1f\n\n", totalFreight);
System.out.println("货物明细如下:");
System.out.println("-----------------------------------------");
System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
int index = 1;
for (Cargo cargo : cargos) {
System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n",
index++,
cargo.getName(),
cargo.getChargeableWeight(),
cargo.getRate(),
cargo.getFreight());
}
}
}
类图设计
因为没有写出这次题目,老是过不去测试案例,所以就没有绘画完整的类图,只展示类图的思路:
点击查看代码
abstract class User {
- person: Person
+ User(person: Person)
+ abstract getDiscountRate(): double
+ getCustomerInfo(): String
}
abstract class Cargo {
- id: String
- name: String
- length: double
- width: double
- height: double
- grossWeight: double
+ Cargo(id: String, name: String, length: double, width: double, height: double, grossWeight: double)
+ getVolumeWeight(): double
+ getChargeableWeight(): double
+ abstract getRate(): double
+ getFreight(): double
+ getName(): String
}
class Flight {
- number: String
- maxLoad: double
+ Flight(number: String, maxLoad: double)
+ getNumber(): String
+ getMaxLoad(): double
}
class Person {
- name: String
- phone: String
- address: String
- number: String
+ Person(name: String, phone: String, address: String, number: String)
+ getName(): String
+ getPhone(): String
+ getAddress(): String
+ getNumber(): String
}
' 定义具体类
class PersonalUser {
+ PersonalUser(person: Person)
+ getDiscountRate(): double
}
class CorporateUser {
+ CorporateUser(person: Person)
+ getDiscountRate(): double
}
class GeneralCargo {
+ GeneralCargo(id: String, name: String, length: double, width: double, height: double, grossWeight: double)
+ getRate(): double
}
class DangerousCargo {
+ DangerousCargo(id: String, name: String, length: double, width: double, height: double, grossWeight: double)
+ getRate(): double
}
class ExpressCargo {
+ ExpressCargo(id: String, name: String, length: double, width: double, height: double, grossWeight: double)
+ getRate(): double
}
class Main {
+ main(args: String[]): void
- createUser(type: String, person: Person): User
- createCargo(type: String, sc: Scanner): Cargo
- printOrder(user: User, cargos: List<Cargo>, flight: Flight, orderInfo: String[], totalWeight: double, totalFreight: double): void
}
' 定义继承关系
User <|-- PersonalUser
User <|-- CorporateUser
Cargo <|-- GeneralCargo
Cargo <|-- DangerousCargo
Cargo <|-- ExpressCargo
写题历程:
这次作业真是让我栽了大跟头,最后代码都没能跑通,只能眼睁睁看着截止时间过去。一开始信心满满,觉得前几次作业都搞定了,这次肯定没问题,结果从用户类型处理开始就踩坑。题目要求区分个人和集团用户,折扣率不同,我照着模板写了抽象类和子类,但测试的时候发现,用户类型输入“个人”或者“集团”之外的词,导致一直无法通过测试用例。
一、货物类型的设计更是困难。题目要求用策略模式实现不同货物的运费计算,我写了普通、危险、加急三个子类,每个类里硬编码了费率判断。结果测试时发现,危险货物在50kg到100kg之间的费率应该是30,但我代码里写成25,和题目要求完全不符。想改的时候发现这些费率数字散落在三个类里,得一个个翻,一着急改漏了一个地方,导致部分重量区间的运费还是错的。
二、最让我烦恼的是航班载重校验。代码里累加所有货物的计费重量时,用了stream的sum()方法,但和预期结果对不上。最后交上去的版本连一半用例都没过,甚至有个别案例输出格式混乱,在IDE里显示正常,但在pta上数字对不齐,直接被判格式错误。
这次作业让我意识到,代码的鲁棒性真的很重要。“理论上不会输错”的输入,测试时一定会出错;“差不多就行”的浮点计算,最后一定会差一点点。
分析SourceMonitor报表

优点:
代码规模可控:
总行数 170(行业参考值≤200),结构紧凑,无冗余,符合中小型项目代码量标准,便于整体管理与维护。
方法粒度合理:
方法平均语句数 6.25(参考值≤8),单个方法逻辑清晰,拆分合理(如createUser、createCargo、printOrder等独立方法),降低维护复杂度,符合高内聚原则。
逻辑复杂度低:
分支语句占比 3.2%(参考值≤15%),条件判断少(仅航班载重检查),代码结构扁平,无嵌套冗余,提升可读性。
最大复杂度 2(参考值≤5),执行路径单一(无多层循环 / 条件嵌套),调试时可快速定位问题,理解成本极低。
缺点:
注释严重缺失:
仅 2.4% 代码含注释(参考值≥20%),关键逻辑无说明
三、踩坑心得:
1.输入处理是万恶之源:前几次作业总以为用户会“按规矩输入”,结果一到测试全是负数、字母、乱序,程序秒崩。尤其是第三次作业,输入顺序错一位就全盘皆输,还因为没校验货物数量,差点让“-3件货物”成功下单。
浮点数的精度陷阱:从第三次到第六次,每次用double算钱或重量,总有一两个测试用例卡在0.0001的误差上。最惨的是第六次作业,30.3kg的航班载重因为精度误差判成超重,被迫熬夜改BigDecimal,结果发现除法舍入规则写反了。
2.设计模式“一看就会,一写就废”:第二次作业的策略模式以为懂了,结果第六次作业的运费策略还是硬编码在类里,想加新类型得改一堆代码,完全违背开闭原则。
边界条件总能给你惊喜:电梯作业里最高层有人按“上行”,航班载重刚好等于总重量,50kg的运费分段点……这些边界情况测试时必现原形,而我总是写完才想起来补。
四、自我反思
1.总想先把功能跑通再优化,结果核心逻辑和输入处理、异常校验混在一起。
2.以为double能应付所有计算,直到作业三的运费始终不正确才懂BigDecimal的重要性;以为“策略模式=接口+实现类”,直到第六次作业想扩展货物类型时,才发现自己根本没吃透设计模式。
3.每次写完代码随便输两三个“理想数据”就交。第六次作业拖到截止前两夜才开始写,发现输入处理和策略模式的设计漏洞时,马上改代码,最后连基础功能都没跑通。
五、总结
输入校验的必要性:多次作业因未严格检查输入合法性导致程序崩溃或逻辑错误,今后需优先实现输入过滤模块,对所有用户输入进行格式和范围校验。
浮点计算的精确性:三次作业因使用double类型累加出现精度问题,最终需改用BigDecimal并统一舍入规则,避免因微小误差影响业务逻辑。
设计模式的实际应用:初期对策略模式、工厂模式的理解停留在理论层面,实际编码时仍硬编码逻辑,后续需通过实践案例(如动态扩展货物类型)深入掌握设计模式的核心思想。
边界条件的全覆盖测试:未充分考虑极端情况,导致部分用例失败,今后需提前列出边界条件清单并针对性测试。
代码结构与时间管理:核心逻辑与输入/输出代码混杂,维护困难。后续应分层开发:先设计数据结构和接口,再实现输入解析与业务逻辑,最后处理输出格式化。
改进方向:
输入标准化:封装工具类统一处理输入校验与转换,减少重复代码。
设计文档先行:编码前绘制类图或流程图,明确模块职责与交互关系。
测试驱动开发:针对关键逻辑编写单元测试,确保核心功能稳定。
分阶段提交:将复杂作业拆分为输入模块、核心逻辑、输出模块分阶段实现,降低后期调试压力。
通过六次作业实践,我认识到代码健壮性、可维护性与精确性需从设计阶段开始重视,逐步养成严谨的编码习惯。
建议:
希望老师能多给一些测试数据,这样我就能知道代码到底错在哪,也有修改的方向。
每次作业提交结束后,希望老师能告诉我们代码主要的问题在哪。

浙公网安备 33010602011771号