题集4-6次作业总结
一、题集4
7-4 菜单计价程序-2
根据上次菜单计价1的前提,增加了数组和字符串操作。由于要求没有过于复杂,简单将两个数组menu和和price来存储菜单和价格信息。在使用字符串控制台读取实现读入输入的菜单等数据。
在菜单一的基础上,让我学会了如何简单的写代码,不复用。
此题由于结构简单,代码中只有一个main类,类图及类设计就不给出了
代码如下
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); String[] menu = new String[500]; int[] price = new int[500]; String menuName; int inputPrice; int menuCount = 0; String wholeJudge; do { int repetition = 1; menuName = input.next(); if (menuName.equals("1")) { wholeJudge = "1"; break; } else if (menuName.equals("end")) { wholeJudge = "0"; break; } else { if (menuName.length() > 10) { System.out.println("Menu name should not exceed 10 characters;"); continue; } if (input.hasNextInt()) { inputPrice = input.nextInt(); for (int k = 0; k < menuCount; k++) { if (menuName.equals(menu[k])) { price[k] = inputPrice; repetition = 0; break; } } if (repetition == 1) { menu[menuCount] = menuName; price[menuCount] = inputPrice; menuCount++; } } else { System.out.println("Invalid price format;"); input.nextLine(); // 清除非整数价格的输入 } } } while (true); int everyPrice = 0; int totalPrice = 0; int orderCount = 0; int[] recording = new int[100]; int re = 0; String endJudge = ""; boolean deleteFlag = false; if (wholeJudge.equals("1")) { do { everyPrice = 0; String order = input.next(); switch (order) { case "delete": if (deleteFlag) { wholeJudge = endJudge; int p = Integer.parseInt(wholeJudge); if (p < 1 || p > orderCount || recording[p - 1] == 0) System.out.println("delete error;"); if (p >= 1 && p <= orderCount && recording[p - 1] != 0) { totalPrice -= recording[p - 1]; recording[p - 1] = 0; } endJudge = input.next(); if (endJudge.equals("end")) break; else { deleteFlag = false; wholeJudge = endJudge; continue; } } else { System.out.println("Invalid command;"); break; } default: int size1 = input.nextInt(); int b = input.nextInt(); for (int j = 0; j < menuCount; j++) { if (order.equals(menu[j])) { if (size1 == 1) { everyPrice += price[j]; } else if (size1 == 2) { if (price[j] % 2 == 1) { everyPrice += price[j] * 1.5 + 1; } else { everyPrice += price[j] * 1.5; } } else if (size1 == 3) { everyPrice += 2 * price[j]; } } } if (everyPrice == 0) { recording[re++] = 0; System.out.println(order + " does not exist"); orderCount++; } else { everyPrice *= b; totalPrice += everyPrice; recording[re++] = everyPrice; System.out.println(wholeJudge + " " + order + " " + everyPrice); orderCount++; } deleteFlag = true; wholeJudge = input.next(); break; } } while (!wholeJudge.equals("end")); } if (!wholeJudge.equals("0")) { System.out.println(totalPrice); } else { System.out.println("0"); } } } |
7-2 单词统计与排序
此题主要涉及输入单词的存储以及单词比较。主要教会了我类设计以及代码中比较数据如何比较,对输入输出数据的判断,对错误的处理等。
这题比较简单主要难点还是字符串判断。类设计就不发出来,以下是代码。
import java.util.*; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); String text = scanner.nextLine(); // 将英文文本转换为单词列表 List<String> words = Arrays.asList(text.split("[,\\.\\s]+")); // 使用自定义比较器对单词进行排序 Collections.sort(words, new WordComparator()); // 去除重复单词 Set<String> uniqueWords = new LinkedHashSet<>(words); // 输出排序后的结果 for (String word : uniqueWords) { System.out.println(word); } } // 自定义比较器,根据单词长度和字母顺序进行比较 static class WordComparator implements Comparator<String> { @Override public int compare(String a, String b) { if (a.length() != b.length()) { return b.length() - a.length(); // 按照长度由高到低排序 } return a.compareToIgnoreCase(b); // 长度相同,按照字母顺序排 } } } |
7-3 判断两个日期的先后,计算间隔天数、周数
这个题目主要涉及
输入输出流:使用了java.util.Scanner类来接收用户的输入,并通过System.out.println语句进行输出。
字符串操作:我在代码中使用了String类的split()方法将用户输入的日期字符串按照指定的分隔符进行分割,以获取年、月、日等具体的日期信息。
日期处理:使用了java.time.LocalDate类来表示和处理日期。通过LocalDate.parse()方法将字符串解析为LocalDate对象,使用isAfter()和isBefore()方法判断日期的先后顺序,使用until()方法计算两个日期之间的天数或周数。
异常处理:我一开始没有进行输入验证和错误处理,假设用户输入的日期是有效的。如果用户输入的日期格式不正确,会导致解析错误。在修改后增加了异常处理的功能。
7-2 菜单计价程序-3
设计点菜计价程序,根据输入的信息,计算并输出总价格。
我的设计思路;下面是按照类设计的具体功能实现解析。
菜单管理
通过Menu类来管理菜单信息,包括添加菜品、搜索菜品等操作。这样可以保持菜单信息的结构化和易于管理。
点餐记录管理:
使用Record类来管理点餐记录,包括记录订单号、菜品、份数、数量、点餐时间等信息。这样可以方便地对每次点餐进行记录和管理。
订单处理:
使用Order类来管理整个订单的信息,包括桌号、点餐记录、下单时间等。在订单处理阶段,程序会根据营业时间判断、折扣计算等规则对订单进行处理,并输出最终的结算信息。
主程序逻辑:
在主程序中,通过不同阶段的逻辑控制,实现了菜单管理阶段和点餐处理阶段的交互。根据用户输入的不同指令,程序会进行相应的菜单操作、点餐记录管理和订单处理等逻辑。
主要是通过控制台去实现数据的输入及存储。
具体类图如下
实现截图
总结及踩坑心得:
通过这道题我学会了如何通过将程序中的实体抽象为类,实现了封装、继承和多态等OOP的特性。每个类都有自己的属性和方法,用于表示不同的实体和处理相应的逻辑。
在代码中的Dish、Menu、Record和Order都是类,它们作为模板定义了具体实例的属性和方法。通过创建对象,可以使用类中定义的方法来进行相应的操作。
在主程序中,使用条件判断语句(如if语句)来根据不同的输入执行相应的逻辑。同时,使用循环语句(如while循环)来重复读取输入和处理操作,直到满足退出条件。
我了解到程序中使用了Java 8的日期时间API(如LocalDateTime类)来处理点餐时间和营业时间的判断。通过解析输入的日期时间字符串,可以进行比较和计算。
我同时学会了错误处理和异常处理,在调试代码的过程中,由于输入数据的空格或者标点问题,导致无法将数据读入数组,也无法提示是否是输入格式的问题。
在学习知识点后,学习了通过使用try-catch块来捕获和处理可能发生的异常,以确保程序的正常运行。当用户输入错误的指令或无效的桌号时,会给出相应的错误提示。
在调试过程中,有几个测试点始终无法通过,这还有待我提升。
此题代码省略
题集5
7-1 菜单计价程序-4
菜单四在菜单三的情况下新增了许多异常情况功能;在菜单三的情况下进行重写;
增加的异常情况如下:
1.菜谱信息与订单信息混合,应忽略夹在订单信息中的菜谱信息。输出:"invalid dish"
2、桌号所带时间格式合法(格式见输入格式部分说明,其中年必须是4位数字,月、日、时、分、秒可以是1位或2位数),数据非法,比如:2023/15/16 ,输出桌号+" date error"
3、同一桌菜名、份额相同的点菜记录要合并成一条进行计算,否则可能会出现四舍五入的误差。
4、重复删除,重复的删除记录输出"deduplication :"+序号。
5、代点菜时,桌号不存在,输出"Table number :"+被点菜桌号+" does not exist";本次作业不考虑两桌记录时间不匹配的情况。
6、菜谱信息中出现重复的菜品名,以最后一条记录为准。
7、如果有重复的桌号信息,如果两条信息的时间不在同一时间段,(时段的认定:周一到周五的中午或晚上是同一时段,或者周末时间间隔1小时(不含一小时整,精确到秒)以内算统一时段),此时输出结果按不同的记录分别计价。
8、重复的桌号信息如果两条信息的时间在同一时间段,此时输出结果时合并点菜记录统一计价。前提:两个的桌号信息的时间都在有效时间段以内。计算每一桌总价要先合并符合本条件的饭桌的点菜记录,统一计价输出。
9、份额超出范围(1、2、3)输出:序号+" portion out of range "+份额,份额不能超过1位,否则为非法格式,参照第13条输出。
10、份数超出范围,每桌不超过15份,超出范围输出:序号+" num out of range "+份数。份数必须为数值,最高位不能为0,否则按非法格式参照第16条输出。
11、桌号超出范围[1,55]。输出:桌号 +" table num out of range",桌号必须为1位或多位数值,最高位不能为0,否则按非法格式参照第16条输出。
12、菜谱信息中菜价超出范围(区间(0,300)),输出:菜品名+" price out of range "+价格,菜价必须为数值,最高位不能为0,否则按非法格式参照第16条输出。
13、时间输入有效但超出范围[2022.1.1-2023.12.31],输出:"not a valid time period"
14、一条点菜记录中若格式正确,但数据出现问题,如:菜名不存在、份额超出范围、份数超出范围,按记录中从左到右的次序优先级由高到低,输出时只提示优先级最高的那个错误。
15、每桌的点菜记录的序号必须按从小到大的顺序排列(可以不连续,也可以不从1开始),未按序排列序号的输出:"record serial number sequence error"。当前记录忽略。(代点菜信息的序号除外)
16、所有记录其它非法格式输入,统一输出"wrong format"
17、如果记录以“table”开头,对应记录的格式或者数据不符合桌号的要求,那一桌下面定义的所有信息无论正确或错误均忽略,不做处理。如果记录不是以“table”开头,比如“tab le 55 2023/3/2 12/00/00”,该条记录认为是错误记录,后面所有的信息并入上一桌一起计算。
本次作业比菜单计价系列-3增加的功能:
菜单输入时增加特色菜,特色菜的输入格式:菜品名+英文空格+基础价格+"T"
例如:麻婆豆腐 9 T
菜价的计算方法:
周一至周五 7折, 周末全价。
注意:不同的四舍五入顺序可能会造成误差,请按以下步骤累计一桌菜的菜价:
计算每条记录的菜价:将每份菜的单价按份额进行四舍五入运算后,乘以份数计算多份的价格,然后乘以折扣,再进行四舍五入,得到本条记录的最终支付价格。
最后将所有记录的菜价累加得到整桌菜的价格。
根据要求需要,我们需要增加dish类中属性判断是否为特色菜,在menu类中增加方法用于查询菜品对象,在order类中添加一个判断营业时间的属性,更新菜品价格计算方法,更新删除的方法属性,判断桌号点餐的情况,判断重复点餐是否同一时间段等,以及异常输入情况的判断和异常情况的更新此外还有输入面板信息的更新与储存,桌号中菜品单价异常的情况等。
在-3的基础上,修改了小部分的属性,并且增加了许多异常情况的处理等。
// 是否在营业时间 public boolean isWork() { if(orderTime.getDayOfWeek().getValue() <= 5) { LocalDateTime startOfNight = LocalDateTime.of(orderTime.getYear(), orderTime.getMonthValue(), orderTime.getDayOfMonth(), 17, 0); LocalDateTime endOfNight = LocalDateTime.of(orderTime.getYear(), orderTime.getMonthValue(), orderTime.getDayOfMonth(), 20, 30); LocalDateTime startOfNoon = LocalDateTime.of(orderTime.getYear(), orderTime.getMonthValue(), orderTime.getDayOfMonth(), 10, 30); LocalDateTime endOfNoon = LocalDateTime.of(orderTime.getYear(), orderTime.getMonthValue(), orderTime.getDayOfMonth(), 14, 30); if(!orderTime.isBefore(startOfNight) && !orderTime.isAfter(endOfNight)) { return true; } else if(!orderTime.isBefore(startOfNoon) && !orderTime.isAfter(endOfNoon)) { return true; } else { return false; } } else { LocalDateTime start = LocalDateTime.of(orderTime.getYear(), orderTime.getMonthValue(), orderTime.getDayOfMonth(), 9, 30); LocalDateTime end = LocalDateTime.of(orderTime.getYear(), orderTime.getMonthValue(), orderTime.getDayOfMonth(), 21, 30); if(!orderTime.isBefore(start) && !orderTime.isAfter(end)) { return true; } else { return false}}} |
判断是否在营业时间:修改后已经涵盖了不同的营业时间段和工作日/非工作日的情况。根据不同的条件,判断订单时间是否在营业时间内,并返回相应的布尔值,这样可以确保系统能够根据营业时间进行正确的处理。
特价菜价格更新规则:在Order类中添加了两个方法:getTableNum()和getDiscount(boolean special)。
getTableNum()方法返回tableNum属性的值,这是订单所属的桌号。
getDiscount(boolean special)方法根据订单的下单时间和特殊标记(special)来计算折扣。根据您的代码逻辑,首先判断订单是否在工作日(星期一至星期五),如果是,则进一步判断订单时间是否在特定的时间段内。根据不同的条件,返回相应的折扣值(0.7、0.8、0.6);如果不满足任何条件,则返回-1表示无效折扣。如果订单不是在工作日,则返回1.0表示不打折。
public int getTableNum() { return tableNum;} double getDiscount(boolean special) { if(orderTime.getDayOfWeek().getValue() <= 5) { if (special) { return 0.7; } else { LocalDateTime startOfNight = LocalDateTime.of(orderTime.getYear(), orderTime.getMonthValue(), orderTime.getDayOfMonth(), 17, 0); LocalDateTime endOfNight = LocalDateTime.of(orderTime.getYear(), orderTime.getMonthValue(), orderTime.getDayOfMonth(), 20, 30); LocalDateTime startOfNoon = LocalDateTime.of(orderTime.getYear(), orderTime.getMonthValue(), orderTime.getDayOfMonth(), 10, 30); LocalDateTime endOfNoon = LocalDateTime.of(orderTime.getYear(), orderTime.getMonthValue(), orderTime.getDayOfMonth(), 14, 30); if(!orderTime.isBefore(startOfNight) && !orderTime.isAfter(endOfNight)) { return 0.8; } else if(!orderTime.isBefore(startOfNoon) && !orderTime.isAfter(endOfNoon)) { return 0.6; } else { return -1; } } } else { return 1.0; } } |
判断点单时间段等:如果两个时间是同一天,就进一步判断是否是工作日(星期一至星期五)。如果是工作日(d1.getDayOfWeek().getValue() > 5),则返回true表示在同一个时间段内。
如果不是工作日,则将工作日的晚上开始时间(startOfNight)设置为17:00,即当天的17:00时刻。接下来通过比较两个时间与工作日晚上开始时间的关系,来判断它们是否在同一个时间段内。如果两个时间都早于工作日晚上开始时间,或者都晚于工作日晚上开始时间,则返回true表示在同一个时间段,否则,返回false表示不在同一个时间段内。
// 判断是否是一个时间段 public static boolean isSameTime(LocalDateTime d1, LocalDateTime d2) { if(!d1.toLocalDate().equals(d2.toLocalDate())) { return false; } if(d1.getDayOfWeek().getValue() > 5) { return true; } else { LocalDateTime startOfNight = d1.toLocalDate().atTime(17, 0); if(d1.isBefore(startOfNight) && d2.isBefore(startOfNight) || !d1.isBefore(startOfNight) && !d2.isBefore(startOfNight)) { return true; } else { return false; } } } |
在main中判断异常情况等:程序启动时进入菜单状态,并等待用户输入。
根据用户输入的不同命令,程序会进行菜单管理、订单记录和错误处理等操作。
用户输入特定命令可以切换程序状态,比如从菜单状态切换到订单状态,或者处理桌号错误状态。
public static void main(String[] args) { Step step = Step.MENU; String line = null; Scanner scanner = new Scanner(System.in); Menu menu = new Menu(); List<Order> orderList = new ArrayList<>(); Order order = null; Set<Integer> deleted = new HashSet<>(); while(!(line = scanner.nextLine()).equals("end")) { String[] words = line.split(" "); boolean ok = words.length != 0; for(String s: words) { if(s.length() == 0) { ok = false; } } if(!ok && step != Step.ERROR_TABLE) { System.out.println("wrong format"); if(words[0].length() > 0 && words[0].charAt(0) == 't') { step = Step.ERROR_TABLE; } continue; } // System.out.println(Arrays.toString(words)); if(step == Step.MENU) { if(!isMenu(words)) { // 菜谱结束 step = Step.ORDER; } else if(!isNum(words[1])) { System.out.println("wrong format"); } else { if(words.length == 2) { // 普通菜 int price = Integer.parseInt(words[1]); if(price < 300 && price > 0) { menu.addDish(words[0], price, false); } else { System.out.println(words[0]+" price out of range "+ price); } } else if(words.length == 3) { if(words[2].equals("T")) { // 特色菜 int price = Integer.parseInt(words[1]); if(price < 300 && price > 0) { menu.addDish(words[0], price, true); } else { System.out.println(words[0]+" price out of range "+ price); } } else { System.out.println("wrong format"); } } } } if(step == Step.ERROR_TABLE) { // 桌号错误状态 if(words.length == 4 && words[0].equals("table") && isNum(words[1]) && parseDateTime(Integer.parseInt(words[1]), words[2], words[3]) != null) { step = Step.ORDER; // 当出现新桌号 } } if(step == Step.ORDER) { if(isNum(words[0])) { if (words[1].equals("delete")) { // 删除 int deleteNum = Integer.parseInt(words[0]); if(deleted.contains(deleteNum)) { System.out.println("deduplication " + deleteNum); } else { deleted.add(deleteNum); if(!order.delRecordByOrderNum(deleteNum)) { System.out.println("delete error;"); } } } else if(words.length == 4) { // 点菜信息 Dish dish = menu.searchDish(words[1]); if(dish == null) { System.out.println(words[1] + " does not exist"); } else if(!isNum(words[0]) || words[0].charAt(0) == '0') { System.out.println("wrong format"); } else if(!isNum(words[3]) || words[3].charAt(0) == '0') { System.out.println("wrong format"); } else if(!isNum(words[2]) || words[2].charAt(0) == '0') { System.out.println("wrong format"); } else if(Integer.parseInt(words[2]) > 3 || Integer.parseInt(words[2]) < 1) { int portion = Integer.parseInt(words[2]); int orderNum = Integer.parseInt(words[0]); System.out.println(orderNum + " portion out of range " + portion); } else { int num = Integer.parseInt(words[3]); int orderNum = Integer.parseInt(words[0]); int portion = Integer.parseInt(words[2]); if(num > 15) { System.out.println(orderNum + " num out of range " + num); } else if(order.getLastNum() >= orderNum) { System.out.println("record serial number sequence error"); } else { Record record = new Record(orderNum, dish, portion, num, order.getOrderTime()); System.out.println(orderNum + " " + words[1] + " " + record.getPrice()); order.addRecord(record); order.setLastNum(orderNum); } } } else if(words.length == 5) { // 代点菜 int tableNum = Integer.parseInt(words[0]); boolean fg = false; for(Order order2 : orderList) { if(order2.getTableNum() == tableNum) { fg = true; } } // if(!isNum(words[0]) || words[0].charAt(0) == '0') { // fg = false; // } if(fg) { Dish dish = menu.searchDish(words[2]); if(dish == null) { System.out.println(words[2] + " does not exist"); } else if(!isNum(words[1]) || words[1].charAt(0) == '0') { System.out.println("wrong format"); } else if(!isNum(words[4]) || words[4].charAt(0) == '0') { System.out.println("wrong format"); } else if(!isNum(words[3]) || words[3].charAt(0) == '0') { System.out.println("wrong format"); } else if(Integer.parseInt(words[3]) > 3 || Integer.parseInt(words[3]) < 1) { int portion = Integer.parseInt(words[3]); int orderNum = Integer.parseInt(words[1]); System.out.println(orderNum + " portion out of range " + portion); } else { int num = Integer.parseInt(words[4]); int orderNum = Integer.parseInt(words[1]); int portion = Integer.parseInt(words[3]); if(num > 15) { System.out.println(orderNum + " num out of range " + num); } else if(order.getLastNum() >= orderNum) { System.out.println("record serial number sequence error"); } else { Record record = new Record(orderNum, dish, portion, num, order.getOrderTime()); System.out.println(orderNum + " " + words[2] + " " + record.getPrice()); order.addRecord(record); order.setLastNum(orderNum); } } } else { System.out.println("Table number :" + tableNum + " does not exist"); } } } else if(words[0].equals("table")){ // 桌号 String tableNumStr = words[1]; if(words.length != 4 || !isNum(words[1]) || words[1].charAt(0) == '0' || words[2].length() == 0 || words[3].length() == 0) { // 桌号信息错误,本桌信息忽略 step = Step.ERROR_TABLE; System.out.println("wrong format"); } else { // 正常桌号 int tableNum = Integer.parseInt(tableNumStr); if(tableNum > 55 || tableNum < 1) { step = Step.ERROR_TABLE; System.out.println(tableNum +" table num out of range"); } else { LocalDateTime datetime = parseDateTime(tableNum, words[2], words[3]); if(datetime != null) { // 日期时间无误 order = new Order(menu, tableNum, datetime); boolean fg = true; if(order.isWork()) { for(int i = 0; i < orderList.size(); i ++ ) { if(orderList.get(i).getTableNum() == tableNum && isSameTime(datetime, orderList.get(i).getOrderTime())) { fg = false; order = orderList.get(i); order.setLastNum(-1); order.setOrderTime(datetime); break; } } } if(order.isWork()) { deleted = new HashSet<>(); if(fg) orderList.add(order); System.out.println("table " + tableNum + ": "); } else { System.out.println("table " + order.getTableNum() + " out of opening hours"); step = Step.ERROR_TABLE; } } else { step = Step.ERROR_TABLE; } } } } else if(isMenu(words)) { // 混入菜谱 System.out.println("invalid dish"); } else { // 错误格式 System.out.println("wrong format"); } } } |
类设计如下:
总结及踩坑心得:
枚举类(Enum):我在代码中使用了 Step 枚举类来表示程序的不同状态。枚举类是一种特殊的数据类型,用于定义一组常量。它提供了一种清晰、可读性高的方式来表示有限的可能取值。类和对象:代码中涉及到了多个类的设计,包括 Menu 类、Order 类和 Record 类。类是面向对象编程的基本概念,用于封装数据和行为。通过创建类的实例(对象),可以使用类定义的属性和方法。
数据结构:代码中使用了不同的数据结构来存储和管理数据。例如,菜单信息可能使用列表或字典进行存储,点菜记录使用列表等。选择合适的数据结构可以提高数据的组织和访问效率。方法和函数:类中定义了多个方法,用于封装对象的行为。方法是与类关联的函数,可以通过对象调用。此外,代码中还可能涉及到其他函数的定义和调用,用于执行特定的操作。
输入和输出:代码中通过读取用户的输入命令行来进行相应的操作,并根据结果输出信息。输入和输出是程序与用户交互的重要方式,需要处理和验证用户的输入,以及提供准确和清晰的输出。异常处理:代码中可能需要进行异常处理,以应对输入错误、数据不一致等异常情况。异常处理是保证程序健壮性和可靠性的重要手段。
设计原则和模式:类的设计涉及到设计原则和模式,如单一职责原则、开放封闭原则、依赖倒置原则、工厂模式等。这些原则和模式可以提高代码的可维护性、可扩展性和可复用性。
题集6
菜单计价-5
菜单计价-5相对于-3增加了许多需求,具体如下:
1、菜单输入时增加特色菜,特色菜的输入格式:菜品名+英文空格+口味类型+英文空格+基础价格+"T"
例如:麻婆豆腐 川菜 9 T
菜价的计算方法:
周一至周五 7折, 周末全价。
特色菜的口味类型:川菜、晋菜、浙菜
川菜增加辣度值:辣度0-5级;对应辣度水平为:不辣、微辣、稍辣、辣、很辣、爆辣;
晋菜增加酸度值,酸度0-4级;对应酸度水平为:不酸、微酸、稍酸、酸、很酸;
浙菜增加甜度值,甜度0-3级;对应酸度水平为:不甜、微甜、稍甜、甜;
例如:麻婆豆腐 川菜 9 T
输入订单记录时如果是特色菜,添加口味度(辣/酸/甜度)值,格式为:序号+英文空格+菜名+英文空格+口味度值+英文空格+份额+英文空格+份数
例如:1 麻婆豆腐 4 1 9
单条信息在处理时,如果口味度超过正常范围,输出"spicy/acidity/sweetness num out of range : "+口味度值,spicy/acidity/sweetness(辣度/酸度/甜度)根据菜品类型择一输出,例如:
acidity num out of range : 5
输出一桌的信息时,按辣、酸、甜度的顺序依次输出本桌菜各种口味的口味度水平,如果没有某个类型的菜,对应的口味(辣/酸/甜)度不输出,只输出已点的菜的口味度。口味度水平由口味度平均值确定,口味度平均值只综合对应口味菜系的菜计算,不做所有菜的平均。比如,某桌菜点了3份川菜,辣度分别是1、3、5;还有4份晋菜,酸度分别是,1、1、2、2,辣度平均值为3、酸度平均值四舍五入为2,甜度没有,不输出。
一桌信息的输出格式:table+英文空格+桌号+:+英文空格+当前桌的原始总价+英文空格+当前桌的计算折扣后总价+英文空格+"川菜"+数量+辣度+英文空格+"晋菜"+数量+酸度+英文空格+"浙菜"+数量+甜度。
如果整桌菜没有特色菜,则只输出table的基本信息,格式如下,注意最后加一个英文空格:
table+英文空格+桌号+:+英文空格+当前桌的原始总价+英文空格+当前桌的计算折扣后总价+英文空格
例如:table 1: 60 36 川菜 2 爆辣 浙菜 1 微甜
计算口味度时要累计本桌各类菜系所有记录的口味度总和(每条记录的口味度乘以菜的份数),再除以对应菜系菜的总份数,最后四舍五入。
注:本题要考虑代点菜的情况,当前桌点的菜要加上被其他桌代点的菜综合计算口味度平均值。
2、考虑客户订多桌菜的情况,输入时桌号时,增加用户的信息:
格式:table+英文空格+桌号+英文空格+":"+英文空格+客户姓名+英文空格+手机号+日期(格式:YYYY/MM/DD)+英文空格+ 时间(24小时制格式: HH/MM/SS)
例如:table 1 : tom 13670008181 2023/5/1 21/30/00
约束条件:客户姓名不超过10个字符,手机号11位,前三位必须是180、181、189、133、135、136其中之一。
输出结果时,先按要求输出每一桌的信息,最后按字母顺序依次输出每位客户需要支付的金额。不考虑各桌时间段的问题,同一个客户的所有table金额都要累加。
输出用户支付金额格式:
用户姓名+英文空格+手机号+英文空格+支付金额
注意:不同的四舍五入顺序可能会造成误差,请按以下步骤累计一桌菜的菜价:
计算每条记录的菜价:将每份菜的单价按份额进行四舍五入运算后,乘以份数计算多份的价格,然后乘以折扣,再进行四舍五入,得到本条记录的最终支付价格。
将所有记录的菜价累加得到整桌菜的价格。
根据以上的需求需要设计的是dish类中菜的属性,分为川菜浙菜晋菜等,每个菜系又有其对应的口味属性,将其中添加酸甜辣等口味分别将这几个存储在特殊数组中。如下图
在-4的基础上,异常处理情况也更新了很多,这里不再赘述。并且在-4的条件下增加的特色菜特性也一并更新到-5的代码中,这里不在给出。Menu菜单也不需要更新什么,只需要将dish类同步更新即可。
在-4的前提在dish类增加phone和name的属性及方法
同步更新main中输入的格式等
Dish类添加;
String name; String phone; public LocalDateTime getOrderTime() { return orderTime; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } |
Main中修改控制台输入以及异常情况等。 public static boolean isPhone(String s) { if(!isNum(s) || s.length() != 11) return false; String pre = s.substring(0, 3); // System.out.println(pre); String[] ss = {"180", "181", "189", "133", "135", "136"}; for (String x : ss) { if(x.equals(pre)) { return true; } } return false; } public static void main(String[] args) { Step step = Step.MENU; String line = null; Scanner scanner = new Scanner(System.in); Menu menu = new Menu(); List<Order> orderList = new ArrayList<>(); Order order = null; Set<Integer> deleted = new HashSet<>(); Map<String, Integer> kindMap = new HashMap<>(); kindMap.put("川菜", 1); kindMap.put("晋菜", 2); kindMap.put("浙菜", 3); Map<Integer, Integer[]> levelMap = new HashMap<>(); Map<String, String> phoneMap = new HashMap<>(); Map<String, Integer> priceMap = new HashMap<>(); while(!(line = scanner.nextLine()).equals("end")) { String[] words = line.split(" "); // System.out.println(Arrays.toString(words)); if(step == Step.MENU) { if(words[0].charAt(0) == 't') { // 菜谱结束 step = Step.ORDER; } else { if(words.length == 2) { // 普通菜 menu.addDish(words[0], Integer.parseInt(words[1]), false, 0); } else if(words.length == 4) { if(words[3].equals("T")) { // 特色菜 int kind = kindMap.get(words[1]); menu.addDish(words[0], Integer.parseInt(words[2]), true, kind); } else { System.out.println("wrong format"); } } else { System.out.println("wrong format"); } } } if(step == Step.ERROR_TABLE) { // 桌号错误状态 if(words.length == 4 && words[0].equals("table") && isNum(words[1]) && parseDateTime(Integer.parseInt(words[1]), words[2], words[3]) != null) { step = Step.ORDER; // 当出现新桌号 } } if(step == Step.ORDER) { if(isNum(words[0])) { if (words.length == 2 && words[1].equals("delete")) { // 删除 int deleteNum = Integer.parseInt(words[0]); if(deleted.contains(deleteNum)) { System.out.println("deduplication " + deleteNum); } else { deleted.add(deleteNum); int recordId = order.findRecordByNum(deleteNum); if(recordId != -1) { Integer[] levels = levelMap.get(order.getTableNum()); Record record = order.getRecords().get(recordId); Dish dish = record.getDish(); int num = record.getNum(); levels[dish.getKind() - 1] -= num; levels[dish.getKind() + 2] -= num * record.getLevel(); order.delRecordByOrderNum(deleteNum); } else { System.out.println("delete error;"); } } } else if(words.length == 4) { // 点菜信息 int orderNum = Integer.parseInt(words[0]); Dish dish = menu.searchDish(words[1]); int portion = Integer.parseInt(words[2]); int num = Integer.parseInt(words[3]); if(dish == null) { System.out.println(words[1] + " does not exist"); } else { Record record = new Record(orderNum, dish, portion, num, order.getOrderTime(), -1); System.out.println(orderNum + " " + words[1] + " " + record.getPrice()); order.addRecord(record); } } else if(words.length == 5 && !isNum(words[1])) { // 特色菜 int orderNum = Integer.parseInt(words[0]); Dish dish = menu.searchDish(words[1]); int level = Integer.parseInt(words[2]); int portion = Integer.parseInt(words[3]); int num = Integer.parseInt(words[4]); if(dish == null) { System.out.println(words[1] + " does not exist"); } else { int kind = dish.getKind(); if(level < 0 || level >= Dish.specialDishLevel[kind - 1].length) { System.out.println(Dish.specialDishKind[kind - 1] + " num out of range :" + level); } else { Record record = new Record(orderNum, dish, portion, num, order.getOrderTime(), level); System.out.println(orderNum + " " + words[1] + " " + record.getPrice()); order.addRecord(record); Integer[] levels = levelMap.get(order.getTableNum()); levels[kind - 1] += num; levels[kind + 3 - 1] += level * num; } } } else if(words.length == 6) { // 代点特色菜 int tableNum = Integer.parseInt(words[0]); int orderNum = Integer.parseInt(words[1]); Dish dish = menu.searchDish(words[2]); int level = Integer.parseInt(words[3]); int portion = Integer.parseInt(words[4]); int num = Integer.parseInt(words[5]); if(dish == null) { System.out.println(words[2] + " does not exist"); } else { int kind = dish.getKind(); if(level < 0 || level >= Dish.specialDishLevel[kind - 1].length) { System.out.println(Dish.specialDishKind[kind - 1] + " num out of range :" + level); } else { Record record = new Record(orderNum, dish, portion, num, order.getOrderTime(), level); System.out.println(orderNum + " table " + order.getTableNum() + " pay for table " + tableNum + " " + record.getPrice()); order.addRecord(record); Integer[] levels = levelMap.get(tableNum); levels[kind - 1] += num; levels[kind + 3 - 1] += level * num; } } } else { System.out.println("wrong format"); } } else if(words[0].charAt(0) == 't'){ // 桌号 String tableNumStr = words[1]; if(!words[0].equals("table") || !isNum(words[1]) || words[1].charAt(0) == '0') { // 桌号信息错误,本桌信息忽略 step = Step.ERROR_TABLE; System.out.println("wrong format"); } else { // 正常桌号 int tableNum = Integer.parseInt(tableNumStr); String name = words[3]; String phone = words[4]; if(name.length() > 10 || !isPhone(phone)) { System.out.println("wrong format"); step = Step.ERROR_TABLE; } else { LocalDateTime datetime = parseDateTime(tableNum, words[5], words[6]); if(datetime != null) { // 日期时间无误 order = new Order(menu, tableNum, datetime, name, phone); if(order.isWork()) { deleted = new HashSet<>(); orderList.add(order); System.out.println("table " + tableNum + ": "); if(!levelMap.containsKey(tableNum)) { levelMap.put(tableNum, new Integer[]{0, 0, 0, 0, 0, 0}); } } else { System.out.println("table " + tableNum + " out of opening hours"); step = Step.ERROR_TABLE; } } else { step = Step.ERROR_TABLE; } } } } else { // 错误格式 System.out.println("wrong format"); } } } // System.out.println("aaa"); List<String> customers = new ArrayList<>(); for (Order od: orderList) { if(od.isWork()) { String s = "table " + od.getTableNum() + ": " + od.getPreTotalPrice() + " " + od.getTotalPrice(); Integer[] levels = levelMap.get(od.getTableNum()); boolean fg = true; for(int i = 0; i < 3; i ++ ) { if(levels[i] > 0) { fg = false; int avgLevel = Math.round(1.0f * levels[i + 3] / levels[i]); s += " " + Dish.specialDishName[i] + " " + levels[i] + " " + Dish.specialDishLevel[i][avgLevel]; } } if(fg) s += " "; System.out.println(s); phoneMap.put(od.getName(), od.getPhone()); if(priceMap.containsKey(od.getName())) { priceMap.put(od.getName(), od.getTotalPrice() + priceMap.get(od.getName())); } else { customers.add(od.getName()); priceMap.put(od.getName(), od.getTotalPrice()); } } else { System.out.println("table " + od.getTableNum() + " out of opening hours"); } } Collections.sort(customers); for (String name: customers) { System.out.println(name + " " + phoneMap.get(name) + " " + priceMap.get(name)); } |
类图设计:
踩坑心得:在调试过程中,始终有一个点报错那就是异常错误点的数据未更新,导致系统无法正确识别输入数据的正确与否。在调试后,更新了这个测试点的情况,终于过了。在根据-3和-4的基础下,增加的一些功能需要在main中去调用,导致了一些空指针异常,于是根据这个情况修改了一些设置的属性。在调试中我学会了这个LocalDateTim的使用:
LocalDateTime是用于表示不带时区的日期和时间的类。它包含了年、月、日、小时、分钟、秒和毫秒等信息。相比于旧的Date和Calendar类,LocalDateTime提供了更简洁和易用的方式来处理日期时间。
使用LocalDateTime创建一个指定日期和时间的LocalDateTime对象,例如:LocalDateTime.now() 获取当前日期和时间。
获取或设置LocalDateTime对象中的年、月、日、小时、分钟、秒和毫秒等字段。
进行日期时间的计算、比较和格式化等操作。
DateTimeFormatter:
DateTimeFormatter是用于格式化和解析日期时间字符串的类。它允许将日期时间对象转换为特定格式的字符串,或者将字符串解析成对应的日期时间对象。
使用DateTimeFormatter,可以执行以下操作:
创建一个指定格式DateTimeFormatter对象,例如:DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") 创建一个格式为"年-月-日 时:分:秒"的日期时间格式化器。
将LocalDateTime对象格式化成字符串,例如:LocalDateTime.now().format(formatter) 将当前日期时间按照指定的格式进行格式化。
将字符串解析成LocalDateTime对象,例如:LocalDateTime.parse("2023-11-19 08:57:26", formatter) 将字符串解析为对应的日期时间对象。