#面向对象程序设计PTA作业# 第4、5、6次作业总结(一)
一、前言
本次总结涉及题目集4的7-1、题目集5的7-5、7-6,题目集6的7-1、7-4、7-5。
1. 三次题目集涉及的的知识点:
- 面向对象技术特性之封装性、字符串处理(4:7-1)
- 面向对象程序设计方法之聚合(5:7-5,7-6)
- 面向对象程序设计之继承(6:7-4,7-5)
2. 题量、难度情况
这三次题目集的题量和难度对于我来说比较大,是我第一次真真切切感受到,软件工程专业的作业不熬几个大夜根本写不完。难度有高有低,跨度还是很明显的,简单题一拿到手里就有思路可以着手来写,但是复杂题往往拿到手里没有什么思路,一方面复杂题(比如6:7-4)的需求就很复杂,阅读量也比较大,需要多读几遍题才能理解需求,另一方面复杂题代码量比较大,编码过程本身就比较耗时,在长达五六个小时的编码过程中还需要时刻注意遵循需求,因此复杂题对我而言还是比较困难。
二、设计与分析
(一)题目集4:7-1
原题
7-1 菜单计价程序-3 分数 30 作者 蔡轲 单位 南昌航空大学 设计点菜计价程序,根据输入的信息,计算并输出总价格。输入内容按先后顺序包括两部分:菜单、订单,最后以"end"结束。
菜单由一条或多条菜品记录组成,每条记录一行
每条菜品记录包含:菜名、基础价格 两个信息。
订单分:桌号标识、点菜记录和删除信息、代点菜信息。每一类信息都可包含一条或多条记录,每条记录一行或多行。
桌号标识独占一行,包含两个信息:桌号、时间。
桌号以下的所有记录都是本桌的记录,直至下一个桌号标识。
点菜记录包含:序号、菜名、份额、份数。份额可选项包括:1、2、3,分别代表小、中、大份。
不同份额菜价的计算方法:小份菜的价格=菜品的基础价格。中份菜的价格=菜品的基础价格1.5。小份菜的价格=菜品的基础价格2。如果计算出现小数,按四舍五入的规则进行处理。
删除记录格式:序号 delete
标识删除对应序号的那条点菜记录。
如果序号不对,输出"delete error"
代点菜信息包含:桌号 序号 菜品名称 份额 分数
代点菜是当前桌为另外一桌点菜,信息中的桌号是另一桌的桌号,带点菜的价格计算在当前这一桌。
程序最后按输入的先后顺序依次输出每一桌的总价(注意:由于有代点菜的功能,总价不一定等于当前桌上的菜的价格之和)。
每桌的总价等于那一桌所有菜的价格之和乘以折扣。如存在小数,按四舍五入规则计算,保留整数。
折扣的计算方法(注:以下时间段均按闭区间计算):
周一至周五营业时间与折扣:晚上(17:00-20:30)8折,周一至周五中午(10:30--14:30)6折,其余时间不营业。
周末全价,营业时间:9:30-21:30
如果下单时间不在营业范围内,输出"table " + t.tableNum + " out of opening hours"
参考以下类的模板进行设计:菜品类:对应菜谱上一道菜的信息。
Dish {
String name;//菜品名称
int unit_price; //单价
int getPrice(int portion)//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份) }
菜谱类:对应菜谱,包含饭店提供的所有菜的信息。
Menu {
Dish[] dishs ;//菜品数组,保存所有菜品信息
Dish searthDish(String dishName)//根据菜名在菜谱中查找菜品信息,返回Dish对象。
Dish addDish(String dishName,int unit_price)//添加一道菜品信息
}
点菜记录类:保存订单上的一道菜品记录
Record {
int orderNum;//序号\
Dish d;//菜品\
int portion;//份额(1/2/3代表小/中/大份)\
int getPrice()//计价,计算本条记录的价格\
}
订单类:保存用户点的所有菜的信息。
Order {
Record[] records;//保存订单上每一道的记录
int getTotalPrice()//计算订单的总价
Record addARecord(int orderNum,String dishName,int portion,int num)//添加一条菜品信息到订单中。
delARecordByOrderNum(int orderNum)//根据序号删除一条记录
findRecordByNum(int orderNum)//根据序号查找一条记录
}
输入格式:
桌号标识格式:table + 序号 +英文空格+ 日期(格式:YYYY/MM/DD)+英文空格+ 时间(24小时制格式: HH/MM/SS)
菜品记录格式:
菜名+英文空格+基础价格
如果有多条相同的菜名的记录,菜品的基础价格以最后一条记录为准。
点菜记录格式:序号+英文空格+菜名+英文空格+份额+英文空格+份数注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
删除记录格式:序号 +英文空格+delete
代点菜信息包含:桌号+英文空格+序号+英文空格+菜品名称+英文空格+份额+英文空格+分数
最后一条记录以“end”结束。
输出格式:
按输入顺序输出每一桌的订单记录处理信息,包括:
1、桌号,格式:table+英文空格+桌号+”:”
2、按顺序输出当前这一桌每条订单记录的处理信息,
每条点菜记录输出:序号+英文空格+菜名+英文空格+价格。其中的价格等于对应记录的菜品*份数,序号是之前输入的订单记录的序号。如果订单中包含不能识别的菜名,则输出“** does not exist”,**是不能识别的菜名
如果删除记录的序号不存在,则输出“delete error”
最后按输入顺序一次输出每一桌所有菜品的总价(整数数值)格式:table+英文空格+桌号+“:”+英文空格+当前桌的总价
本次题目不考虑其他错误情况,如:桌号、菜单订单顺序颠倒、不符合格式的输入、序号重复等,在本系列的后续作业中会做要求。
输入格式:
桌号标识格式:table + 序号 +英文空格+ 日期(格式:YYYY/MM/DD)+英文空格+ 时间(24小时制格式: HH/MM/SS)
菜品记录格式:
菜名+英文空格+基础价格
如果有多条相同的菜名的记录,菜品的基础价格以最后一条记录为准。
点菜记录格式:序号+英文空格+菜名+英文空格+份额+英文空格+份数注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
删除记录格式:序号 +英文空格+delete
代点菜信息包含:桌号+英文空格+序号+英文空格+菜品名称+英文空格+份额+英文空格+分数
最后一条记录以“end”结束。
输出格式:
按输入顺序输出每一桌的订单记录处理信息,包括:
1、桌号,格式:table+英文空格+桌号+“:”
2、按顺序输出当前这一桌每条订单记录的处理信息,
每条点菜记录输出:序号+英文空格+菜名+英文空格+价格。其中的价格等于对应记录的菜品*份数,序号是之前输入的订单记录的序号。如果订单中包含不能识别的菜名,则输出“** does not exist”,**是不能识别的菜名
如果删除记录的序号不存在,则输出“delete error”
最后按输入顺序一次输出每一桌所有菜品的总价(整数数值)格式:table+英文空格+桌号+“:”+英文空格+当前桌的总价
本次题目不考虑其他错误情况,如:桌号、菜单订单顺序颠倒、不符合格式的输入、序号重复等,在本系列的后续作业中会做要求。
输入样例:
在这里给出一组输入。例如:
麻婆豆腐 12
油淋生菜 9
table 1 2023/3/22 12/2/3
1 麻婆豆腐 2 2
2 油淋生菜 1 3
end
输出样例:
在这里给出相应的输出。例如:
table 1:
1 麻婆豆腐 36
2 油淋生菜 27
table 1: 38
输入样例1:
在这里给出一组输入。例如:
麻婆豆腐 12
油淋生菜 9
table 1 2023/3/22 17/0/0
1 麻婆豆腐 2 2
2 油淋生菜 1 3
1 delete
end
输出样例1:
在这里给出相应的输出。例如:
table 1:
1 麻婆豆腐 36
2 油淋生菜 27
table 1: 22
输入样例2:
在这里给出一组输入。例如:
麻婆豆腐 12
油淋生菜 9
table 1 2023/3/22 16/59/59
1 麻婆豆腐 2 2
2 油淋生菜 1 3
1 delete
end
输出样例2:
在这里给出相应的输出。例如:
table 1:
1 麻婆豆腐 36
2 油淋生菜 27
table 1 out of opening hours
输入样例3:
在这里给出一组输入。例如:
麻婆豆腐 12
油淋生菜 9
table 1 2022/12/5 15/03/02
1 麻婆豆腐 2 2
2 油淋生菜 1 3
3 麻辣鸡丝 1 2
5 delete
7 delete
table 2 2022/12/3 15/03/02
1 麻婆豆腐 2 2
2 油淋生菜 1 3
3 麻辣鸡丝 1 2
7 delete
end
输出样例3:
在这里给出相应的输出。例如:
table 1:
1 麻婆豆腐 36
2 油淋生菜 27
麻辣鸡丝 does not exist
delete error;
delete error;
table 2:
1 麻婆豆腐 36
2 油淋生菜 27
麻辣鸡丝 does not exist
delete error;
table 1 out of opening hours
table 2: 63
输入样例4:
在这里给出一组输入。例如:
麻婆豆腐 12
油淋生菜 9
table 1 2022/12/3 19/5/12
1 麻婆豆腐 2 2
2 油淋生菜 1 3
3 麻辣鸡丝 1 2
table 2 2022/12/3 15/03/02
1 麻婆豆腐 2 2
2 油淋生菜 1 3
3 麻辣鸡丝 1 2
1 4 麻婆豆腐 1 1
7 delete
end
输出样例4:
在这里给出相应的输出。例如:
table 1:
1 麻婆豆腐 36
2 油淋生菜 27
麻辣鸡丝 does not exist
table 2:
1 麻婆豆腐 36
2 油淋生菜 27
麻辣鸡丝 does not exist
4 table 2 pay for table 1 12
delete error;
table 1: 63
table 2: 75
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
我的代码
import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.*; public class Main{ public static void main(String[] args) { int countOfOrder = 0; Menu menu = new Menu();// 创建一个菜单 Order order = new Order();// 创建集合 Operator operator = new Operator();// 创建业务类对象 operator.recordDish(menu);// 记录菜单 operator.recordOrderNum(order);// 记录桌号 operator.recordTime(order);// 记录时间 operator.orderDish(order,menu);// 记录点菜 // 输出 operator.output(order);}}
class Operator {// 业务类Scanner input = new Scanner(System.in); public void output(Order order) { boolean flag = true; for (int j = 0; flag; j++) { float totalCost = 0; if(order.judgeTime(order) == -1){ System.out.println("table " + order.getTableNum() + " out of opening hours"); }else { System.out.println("table " + order.getTableNum() + ": "); for (int i = 0; ; i++) { if (order.records[i].getOrderNum() != -1) { if (order.records[i].getIsValue() == 1) { totalCost = totalCost + order.records[i].getPrice(); } System.out.println(order.records[i].orderNum + " " + order.records[i].getD().getName() + " " + order.records[i].getPrice()); } else { totalCost = totalCost * order.judgeTime(order); if (totalCost - (int) totalCost >= 0.5) { totalCost = (int) totalCost + 1;// 四舍五入 } System.out.println("table " + order.getTableNum() + ": " + (int) totalCost); break; } } // 没设计好,先输出一桌 } flag = false; }}
public void recordDish(Menu menu) { //记录菜品
int price;
String name;for (int i = 0; ; i++) { name = input.next(); if (name.equals("table")) { break; } price = input.nextInt(); menu.addDish(name, price); } } public void recordOrderNum(Order order) {// 记录编号 order.setTableNum(input.nextInt()); } public void recordTime(Order order) {// 记录时间 int year = 0,month = 0,day = 0,hour = 0,min = 0,sec = 0; String[] time = input.nextLine().replace("/"," ").split(" "); year = Integer.parseInt(time[1]); month = Integer.parseInt(time[2]); day = Integer.parseInt(time[3]); hour = Integer.parseInt(time[4]); min = Integer.parseInt(time[5]); sec = Integer.parseInt(time[6]); order.setYear(year); order.setMonth(month); order.setDay(day); order.setHours(hour); order.setMin(min); order.setSec(sec); } public boolean orderDish(Order order, Menu menu) {// 记录点菜记录 boolean result = true; for (int i = 0; result; i++) { int num = 0; String nums = input.next(); if (nums.equals("end")){ result = false; break; }else{ num = Integer.parseInt(nums); } String dish = input.next(); if(dish.equals("delete")){ order.delARecordByOrderNum(num); } else { order.records[i].isValue = 1; order.records[i].setOrderNum(num); order.records[i].setD(menu.searchDish(dish)); order.records[i].setPortion(input.nextInt()); order.records[i].setOrderQuantity(input.nextInt()); } } return result; } public void deleteRecord(Order order, int recordNum) {// 删除记录 order.delARecordByOrderNum(recordNum); }}
class Dish {// 记录一道菜品的信息Dish(String name, int unit_price) { this.name = name; this.unit_price = unit_price; } private String name;//菜品名称 private int unit_price; //单价 public int getPrice(int portion) {//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份) double cost = 0; if (portion == 1) { cost = unit_price; } else if (portion == 2) { cost = unit_price * 1.5; } else if (portion == 3) { cost = unit_price * 2; } int result = 0; if (cost - (int) cost >= 0.5) { result = (int) cost + 1; } else { result = (int) cost; } return result; } public String getName() { return name; } public int getUnit_price() { return unit_price; }}
class Menu {public Dish[] dishs = new Dish[1000];//菜品数组,保存所有菜品信息 int dishIndex = 0; public Dish searchDish(String dishName) { boolean existDish = false;// 记录判断结果 Dish result = new Dish("0", 0); for (int i = 0; i < 1000 ; i++) { if (this.dishs[i].getName().equals(dishName)) { result = dishs[i]; existDish = true; break; } } if (existDish == false) { System.out.println(dishName + "does not exist"); } return result; }//根据菜名在菜谱中查找菜品信息,返回Dish对象。 public void addDish(String dishName, int unit_price) { dishs[dishIndex] = new Dish(dishName, unit_price); dishIndex++; }//添加一道菜品信息}
class Record {
int orderNum;// 序号
Dish d;// 菜品
int orderQuantity;// 份数
int isValue = 0;// 记录是否作数
int portion;// 份额(1/2/3代表小/中/大份)public int getPrice() { return this.orderQuantity * d.getPrice(portion); }//计价,计算本条记录的价格 Record(){ } Record(int orderNum, String name, int orderQuantity, int portion) { setOrderNum(orderNum); setPortion(portion); setOrderQuantity(orderQuantity); } public int getOrderNum() { return orderNum; } public void setOrderNum(int orderNum) { this.orderNum = orderNum; } public int getIsValue() { return isValue; } public void setIsValue(int isValue) { this.isValue = isValue; } public Dish getD() { return d; } public void setD(Dish d) { this.d = d; } public int getOrderQuantity() { return orderQuantity; } public void setOrderQuantity(int orderQuantity) { this.orderQuantity = orderQuantity; } public int getPortion() { return portion; } public void setPortion(int portion) { this.portion = portion; }}
class Order {Record[] records = new Record[100];// 保存订单上每一道的记录 int tableNum;// 桌编号 Order(){ for(int i = 0 ;i < 100 ;i++){ this.records[i] = new Record(-1,"1",-1,-1);// 实例化创建内存 } } public float judgeTime(Order order) {// 判断下单时间获得折扣或不营业 int week = 3;// 星期数 float discount = 1; LocalDate timeStander = LocalDate.of(1898, 12, 19);// 设置参照时间1000年1月1 星期三 LocalDate time = LocalDate.of(order.getYear(), order.getMonth(), order.getDay()); week = ((int) timeStander.until(time, ChronoUnit.DAYS) + week) % 7;// 计算当前星期几 if (week >= 1 && week <= 5) {// 周一到周五 if (order.getHours() >= 17 && order.getHours() < 20 && order.getMin() < 60 && order.getMin() >= 0 || order.getHours() == 20 && order.getMin() <= 30 && order.getMin() >= 0) { discount = 0.8F; }//晚上 else if (order.getHours() > 10 && order.getHours() < 14 && order.getMin() < 60 && order.getMin() >= 0 || order.getHours() == 10 && order.getMin() <= 30 && order.getMin() >= 0 || order.getHours() == 14 && order.getMin() <= 30 && order.getMin() >= 0) { discount = 0.6F; }//中午 else { discount = -1; }// 不营业 } else {// 周六周日week = 6 或 0 if (order.getHours() > 9 && order.getHours() < 21 && order.getMin() < 60 && order.getMin() >= 0 || order.getHours() == 9 && order.getMin() <= 30 && order.getMin() >= 0) { discount = 1; }// 营业 else { discount = -1; }// 不营业 } return discount; } public int getTableNum() { return tableNum; } public void setTableNum(int tableNum) { this.tableNum = tableNum; } int year, month, day, hours, min, sec; // 记录下单时间 public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } public int getHours() { return hours; } public void setHours(int hours) { this.hours = hours; } public int getMin() { return min; } public void setMin(int min) { this.min = min; } public int getSec() { return sec; } public void setSec(int sec) { this.sec = sec; } public int getTotalPrice() { int sum = 0; for (int i = 0; i <= recordIndex; i++) { if(records[i].getIsValue() == 1){ sum = sum + records[i].getPrice(); } } return sum; }//计算订单的总价(不含折扣) static int recordIndex = 0;// 记录索引 public Record addARecord(int orderNum, String name, int orderQuantity, int portion, Menu menu) { this.records[recordIndex] = new Record(orderNum, name, orderQuantity, portion); recordIndex++; return this.records[recordIndex - 1]; }//添加一条菜品信息到订单中。 public void delARecordByOrderNum(int orderNum) { int i = 0; for (i = 0; ; i++) { if (records[i].orderNum == orderNum) { records[i].setIsValue(0); break; } } }//根据序号删除一条记录 public Record findRecordByNum(int orderNum) { int i = 0; for (i = 0; ; i++) { if (orderNum == records[i].orderNum) { records[i].isValue = 0; break; } } return records[i]; }//根据序号查找一条记录
}
(二)设计思路
- 功能需求:这道题的功能需求十分复杂,需要应对多种输入情况,同时需要考虑各种输入格式或因输入错误导致的异常。
- 性能方面:本题基本不会涉及到代码性能问题,不作为考虑的终点
- 可扩展性:本题没有要求可扩展,但考虑到本题隶属于菜单类系列题,还是尽量保证扩展性
- 可测试性:测试数据将由平台给出,无需过多关注
- 安全性:需要特别注意各种输入异常导致的空指针问题
程序类图如下:
该类图暂时不能完成题目全部要求,需要进行大量的改进
三、踩坑心得
提交结果:
这道题目对我来说确实是难度很大,在有限的时间内我也尽力尝试去完成了,即便只完成了很少的测试点,但在此次作业中,我还是有很多收获,下面列举一些我在本题中踩过的坑与收获心得。
- 在面对复杂程序时,务必要先花时间进行类设计,不论是实体类还是业务类,都要根据需求进行相应的设计,不得边写边设计,最后耗费时间且代码质量低。
- 编码应当遵守编码规范,尤其是空格和缩进,对于这种长代码,往往一时半会难以完成,隔天再看时代码的结构非常影响阅读,合理的编码方式不仅提高效率,也提高代码质量,反之则会导致低效低质。
四、改进建议
-
命名规范:变量名、函数名、类名等要使用有意义的名称,遵循命名规范,使代码易于理解和维护。
-
注释:在关键地方添加注释,解释代码的作用、实现方法等,便于阅读和维护。
-
异常处理:由于本题输入比较复杂,对字符串的处理难以面面俱到,这对依赖文字输入的程序来说是十分致命的,可以使用异常处理机制来规避不恰当的输入引发的程序崩溃问题
五、总结
这道题目非常侧重考察设计,对于细节的处理都是学过并且经常用的知识,但是怎么把这些细节有机的组合起来是非常考人的一点。




浙公网安备 33010602011771号