#面向对象程序设计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];
}//根据序号查找一条记录
}

(二)设计思路

  • 功能需求:这道题的功能需求十分复杂,需要应对多种输入情况,同时需要考虑各种输入格式或因输入错误导致的异常。
  • 性能方面:本题基本不会涉及到代码性能问题,不作为考虑的终点
  • 可扩展性:本题没有要求可扩展,但考虑到本题隶属于菜单类系列题,还是尽量保证扩展性
  • 可测试性:测试数据将由平台给出,无需过多关注
  • 安全性:需要特别注意各种输入异常导致的空指针问题

程序类图如下:

p9MEQds.png

该类图暂时不能完成题目全部要求,需要进行大量的改进


三、踩坑心得

提交结果:

p9MZwP1.png
p9MZa5R.png

这道题目对我来说确实是难度很大,在有限的时间内我也尽力尝试去完成了,即便只完成了很少的测试点,但在此次作业中,我还是有很多收获,下面列举一些我在本题中踩过的坑与收获心得。

  1. 在面对复杂程序时,务必要先花时间进行类设计,不论是实体类还是业务类,都要根据需求进行相应的设计,不得边写边设计,最后耗费时间且代码质量低。
  2. 编码应当遵守编码规范,尤其是空格和缩进,对于这种长代码,往往一时半会难以完成,隔天再看时代码的结构非常影响阅读,合理的编码方式不仅提高效率,也提高代码质量,反之则会导致低效低质。

四、改进建议

  1. 命名规范:变量名、函数名、类名等要使用有意义的名称,遵循命名规范,使代码易于理解和维护。

  2. 注释:在关键地方添加注释,解释代码的作用、实现方法等,便于阅读和维护。

  3. 异常处理:由于本题输入比较复杂,对字符串的处理难以面面俱到,这对依赖文字输入的程序来说是十分致命的,可以使用异常处理机制来规避不恰当的输入引发的程序崩溃问题


五、总结

这道题目非常侧重考察设计,对于细节的处理都是学过并且经常用的知识,但是怎么把这些细节有机的组合起来是非常考人的一点。

posted @ 2023-04-25 20:14  一杯大丞汁  阅读(276)  评论(0)    收藏  举报