#面向对象程序设计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  一杯大丞汁  阅读(259)  评论(0)    收藏  举报