第二次博客
一、前言
对于PTA题目集4.5.6都是对于菜单题目的不断迭代,难度也是越来越高,对于第四次pta第三题也就是菜单二中main函数代码还是很少的,到了菜单三中的一下关键的逻辑代码就放到了主函数中,到后面不断的迭代主函数的内容也是在不断地增加。还是在类设计方面没有做的很好。主要涉及到的知识点就是类的涉及,各个类之间的关系以及类属性和方法。还有就是抽象类以及类的继承之间的关系。
虽然后来每周的题目量只有一题,但是题目的难度还是很大的,尤其是测试点特别多,对于知道的测试点来说还是比较容易修改的,但是很多测试点都是非公开的,很多时候就需要我们不断地去测试发现自己代码的漏洞。
涉及到的知识点:
- 类与类之间的六种关系
1.继承关系
继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力。在Java中继承关系通过关键字extends明确标识,在设计时一般没有争议性。在UML类图设计中,继承用一条带空心三角箭头的实线表示,从子类指向父类,或者子接口指向父接口。
2.实现关系
实现指的是一个class类实现interface接口(可以是多个)的功能,实现是类与接口之间最常见的关系。在Java中此类关系通过关键字implements明确标识,在设计时一般没有争议性。在UML类图设计中,实现用一条带空心三角箭头的虚线表示,从类指向实现的接口。
3.依赖关系
简单的理解,依赖就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A。比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖。表现在代码层面,为类B作为参数被类A在某个method方法中使用。在UML类图设计中,依赖关系用由类A指向类B的带箭头虚线表示。
4.关联关系
关联体现的是两个类之间语义级别的一种 强依赖关系,比如我和我的朋友,这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的。关联可以是单向、双向的。表现在代码层面,为被关联类B以类的属性形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量。在UML类图设计中,关联关系用由关联类A指向被关联类B的带箭头实线表示。
5.聚合关系
聚合是关联关系的一种特例,它体现的是整体与部分的关系,即 has-a 的关系。此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。比如计算机与CPU、公司与员工的关系等。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在UML类图设计中,聚合关系以空心菱形加实线表示。(菱形指向整体)
6.组合关系
组合也是关联关系的一种特例,这种关系比聚合更强,也称为强聚合。它同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束,比如人和人的大脑。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在UML类图设计中,组合关系以实心菱形加实线表示。
二、设计与分析
1.7-4 菜单计价程序-2
题目:
设计点菜计价程序,根据输入的信息,计算并输出总价格。
输入内容按先后顺序包括两部分:菜单、订单,最后以"end"结束。
菜单由一条或多条菜品记录组成,每条记录一行
每条菜品记录包含:菜名、基础价格 两个信息。
订单分:点菜记录和删除信息。每一类信息都可包含一条或多条记录,每条记录一行。
点菜记录包含:序号、菜名、份额、份数。
份额可选项包括:1、2、3,分别代表小、中、大份。
删除记录格式:序号 delete
标识删除对应序号的那条点菜记录。
不同份额菜价的计算方法:
小份菜的价格=菜品的基础价格。
中份菜的价格=菜品的基础价格1.5。
小份菜的价格=菜品的基础价格2。
如果计算出现小数,按四舍五入的规则进行处理。
代码类图:
分析过程:
类的构造:
-
菜品类(Dish):
-
属性:菜品名称(name)和单价(unit_price)。
-
方法:
getPrice(int portion)用来计算菜品价格,通过传入份额来计算不同份额菜品的价格。
get_name()获取菜名
-
-
菜谱类(Menu):
-
属性:菜品数组(dishes)保存所有菜品信息。
-
方法:
searchDish(String dishName)根据菜名在菜谱中查找菜品信息,返回Dish对象。
searchDishCount(String dishName)根据菜名在菜谱中查找菜品信息,返回count第几份菜对象。
addDish(String dish_name,int unit_price)添加一道菜品信息
change_price(String dish_name,int unit_price)修改菜价
-
-
点菜记录类(Record):
-
属性:菜品(dish)份额(portion)orderNum序号,dish_number份数。
-
方法:getPrice()用来计算本条记录的价格。
getOrderNum()获取订单号
-
-
订单类(Order):
-
属性:记录数组(records)保存订单上每一道的记录,有效记录的数量(count),数组price保存每条点菜记录的总价。
-
方法:addARecord(Dish dish, int portion)用来添加一条菜品信息到订单中,getTotalPrice()用来计算订单的总价
searchDishCount(int number) 根据菜名在菜谱中查找菜品信息,返回在第几条记录。
addARecord(int orderNum, Dish dish, int portion,int dish_number)添加一条菜品信息到订单中
delARecordByOrderNum(int orderNum)根据序号删除一条记录
-
5.输入类(Input):
-
属性:menu和order
-
方法:matchingInput(String s)根据输入的字符串判断为什么类型的输入信息返回对应数字。
matchingOrder(String s)判断是否为订单信息
matchingDeleter(String s)判断是否为删除信息
enter(String message)根据输入的信息做对应的逻辑处理
6.主类(Main):
- 创建菜谱对象(Menu)并初始化菜品信息。
- 创建订单对象(Order)。
- 通过Scanner类读取用户输入的订单信息,并将订单信息添加到订单对象中。
- 最后输出订单的总价。
2.7-1 菜单计价程序-3
题目:
设计点菜计价程序,根据输入的信息,计算并输出总价格。
输入内容按先后顺序包括两部分:菜单、订单,最后以"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"
代码类图:
代码分析:
相比于菜单二,菜单三增加了一个Table类。其中Table类的属性包括订单(Order),折扣dicount,桌号table_number,以及一些相关的时间等。方法包括计算折扣discount(DayOfWeek dayOfWeek),根据日期计算是周几count_week(String date),以及计算折扣后的价格getDiscountPrice(int price),初始化桌号setTable_number(int number)。
class Table{
Order order;
public int table_number;
private int hour;
private int minute;
private int second;
double discount1 = 0;
// 创建开始和结束时间
LocalTime startDayTime = LocalTime.of(10, 30,0);
LocalTime endDayTime = LocalTime.of(14, 30,0);
LocalTime startNightTime = LocalTime.of(17, 0,0);
LocalTime endNightTime = LocalTime.of(20, 30,0);
LocalTime week_startDayTime = LocalTime.of(9, 30,0);
LocalTime week_endDayTime = LocalTime.of(21, 30,0);
public void setTable_number(int number){
this.table_number = number;
}
public void setHourMinute(String time){
String[] dishParts = time.split("/");
this.hour = Integer.parseInt(dishParts[0]);
this.minute = Integer.parseInt(dishParts[1]);
this.second = Integer.parseInt(dishParts[2]);
}
//计算折扣
public double discount(DayOfWeek dayOfWeek){
LocalTime targetTime = LocalTime.of(hour, minute,second);
if(dayOfWeek.equals(DayOfWeek.SUNDAY) || dayOfWeek.equals(DayOfWeek.SATURDAY) ){
if (targetTime.compareTo(week_startDayTime) >= 0 && targetTime.compareTo(week_endDayTime) <= 0)
return 1.0;
else
return -1.0;
}
else {
if (targetTime.compareTo(startDayTime) >= 0 && targetTime.compareTo(endDayTime) <= 0)
return 0.6;
else if (targetTime.compareTo(startNightTime) >= 0 && targetTime.compareTo(endNightTime) <= 0) {
return 0.8;
}else{
return -1.0;
}
}
}
public DayOfWeek count_week(String date){
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/M/d");
LocalDate date1 = LocalDate.parse(date, formatter);
DayOfWeek dayOfWeek = date1.getDayOfWeek();
return dayOfWeek;
}
public void addOrder(Order order){
this.order = order;
}
//计算打折后的总价
public int getDiscountPrice(int price){
return (int) Math.round(price * discount1);
}
}
还有一个很大的变化就是将整个逻辑流程代码,没有放在Input类中,而是放在了Main中
主要逻辑就是先判断是否为end,如果不是则往下继续判断,如果是菜品信息,就加到菜单中,如果是桌号信息,就进入一个循环,用来不断判断是否为菜单信息,进入后,初始化变量flag为false,用于内层循环的跳出。如果用户输入的消息是"end",则跳出外层循环。否则,根据输入的消息进行不同的处理。如果输入的消息格式不正确(x == 0),输出"wrong format"。如果输入的消息是菜品信息(x == 1)且table_count为0,解析菜品名称和价格,并根据菜品是否存在进行相应的操作。如果输入的消息是桌号信息(x == 4)或table_count大于等于1,进入内层循环。在内层循环中,解析桌号、日期和时间,并创建Table和Order对象。接收用户输入的消息,并根据消息的类型进行相应的处理。如果消息是点菜记录(y == 2),解析菜品数量、菜品名称、菜品份数和菜品数量,并根据菜品是否存在进行相应的操作。如果消息是删除记录(y == 3),解析菜品数量,并删除相应的记录。如果消息是代点菜(y == 5),解析目标桌号、菜品数量、菜品名称、菜品份数和菜品数量,并根据菜品是否存在进行相应的操作。如果消息为"end",将当前桌的信息添加到tableList中,并设置桌的时间、星期几和折扣信息,然后设置flag为true。如果以上条件都不满足,将当前桌的信息添加到tableList中,设置桌的时间、星期几和折扣信息,增加table_count的计数,并跳出内层循环。如果flag2为true,跳出外层循环。
然后,利用一个循环,将tableList中的信息按题目要求打印出来。
3.7-1 菜单计价程序-4
题目:
本体大部分内容与菜单计价程序-3相同,增加的部分用加粗文字进行了标注。
本次课题比菜单计价系列-3增加的异常情况:
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类中加入了一个属性is_special,用于判断是否为特价菜,并且加入特色菜后,最后的计算价格的方法也要稍作修改。其中要判断是否为特价菜,如果是的话,按照周一至周五 7折, 周末全价打折。
//计算打折后的总价
public void getDiscountPrice(){
total = 0;
for (int i=0;i<order.count;i++){
if(order.records[i].getDish().is_special && !order.records[i].is_week)//如果是特价菜,并且不是周末
total += order.price[i]*0.7;
else
total += (int) Math.round(order.price[i]*discount1);
}
}
要注意特色菜打了折,就不会再参与其他折扣,在这里我就踩了坑!
对于异常分析,我主要是将各个异常分析分散到对应的类中,或者逻辑判断的地方。
比如这段代码就是在逻辑判断的时候加上了题目份额超出范围(1、2、3)输出:序号+" portion out of range "+份额,份额不能超过1位,否则为非法格式,参照第13条输出。份数超出范围,每桌不超过15份,超出范围输出:序号+" num out of range "+份数。份数必须为数值,最高位不能为0,否则按非法格式参照第16条输出。异常情况分析
if (!table.isOrder(table,number)){ System.out.println("record serial number sequence error");
}else if (!menu.searchDish(dish_name)) { //菜品不存在 System.out.println(dish_name + " does not exist");
} else if (portion>3 && portion<10){ System.out.println(number+" portion out of range "+portion);
}else if (portion>=10){ System.out.println("not a valid time period");
} else if (dish_number >15) { System.out.println(number + " num out of range " + dish_number);
}
4.7-1 菜单计价程序-5
题目:
以上为菜单计价系列-3的题目要求,加粗的部分是有调整的内容。本次课题相比菜单计价系列-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金额都要累加。
输出用户支付金额格式:
用户姓名+英文空格+手机号+英文空格+支付金额
代码类图:
代码分析:
菜单5也是在菜单3的基础上进行迭代,其中也增加了特色菜的相关要求,还增加了特色菜会有对于的口味类型和不同程度的口味度,同时对于每桌的信息也增加了,加上了名字电话号码等一些要求,然后就是在输出的内容上进行了拓展。
对于考虑客户订多桌菜的情况,输入时桌号时,增加用户的信息这个要求,主要就是在原来的Table中再增加name与telephone_number属性,同时Iuput中桌号信息的正则表达式匹配也需要做出对用的改变。在最后的输出时,还要进行排序,可以使用 Comparator 接口和 Collections.sort 方法按拼音顺序重新排序。
对于特色菜的口味等变化,在Dish类中,增加taste口味类型这个属性,对于口味度是否超过指定值,在Dish类中加入了判断函数。
public boolean is_exceed(String taste2, int level) {
switch (taste2) {
case "川菜":
if (level < 0 || level > 5) {
System.out.println("spicy num out of range :" + level);
return false;
}
return true;
case "晋菜":
if (level < 0 || level > 4) {
System.out.println("acidity num out of range :" + level);
return false;
}
return true;
case "浙菜":
if (level < 0 || level > 3) {
System.out.println("sweetness num out of range :" + level);
return false;
}
return true;
default:
return false;
}
}
在主函数判断为特色菜信息之后,如果菜品存在的话,就要调用
is_exceed()这个方法来判断口味度是否超过指定值。如果超过了,就不加入订单了。
同时还要增加整个桌的口味度的计算,这就可以在Table类中加入对应的方法。
//计算口味度
public String count_level(Table table){
int total_level_spicy = level_spicy;
int total_level_acidity = level_acidity;
int total_level_sweetness = level_sweetness;
int count_spicy=this.count_spicy;
int count_acidity=this.count_acidity;
int count_sweetness=this.count_sweetness;
StringBuilder s = new StringBuilder(); // 使用 StringBuilder 代替 String
for (int i=0; i<table.order.count; i++){
if(!table.order.records[i].is_replace_record){
if (!(table.order.records[i].getDish().getTaste()==null)){
if (table.order.records[i].getDish().getTaste().equals("川菜")){
total_level_spicy += table.order.records[i].getLevel()*table.order.records[i].getDish_number();
count_spicy += table.order.records[i].getDish_number();
}else if(table.order.records[i].getDish().getTaste().equals("晋菜")){
total_level_acidity += table.order.records[i].getLevel()*table.order.records[i].getDish_number();
count_acidity += table.order.records[i].getDish_number();
}else {
total_level_sweetness += table.order.records[i].getLevel()*table.order.records[i].getDish_number();
count_sweetness += table.order.records[i].getDish_number();
}
}
}
}
table.level_spicy = Math.round((float)total_level_spicy /count_spicy);
table.count_spicy = count_spicy;
table.level_acidity = Math.round((float)total_level_acidity /count_acidity);
table.count_acidity = count_acidity;
table.level_sweetness = Math.round((float)total_level_sweetness /count_sweetness);
table.count_sweetness = count_sweetness;
String Chinese_level_spicy = null;
String Chinese_level_acidity= null;
String Chinese_level_sweetness= null;
switch (table.level_spicy){
case 0:
Chinese_level_spicy = "不辣";
break;
case 1:
Chinese_level_spicy = "微辣";
break;
case 2:
Chinese_level_spicy = "稍辣";
break;
case 3:
Chinese_level_spicy = "辣";
break;
case 4:
Chinese_level_spicy = "很辣";
break;
case 5:
Chinese_level_spicy = "爆辣";
break;
}
switch (table.level_acidity){
case 0:
Chinese_level_acidity = "不酸";
break;
case 1:
Chinese_level_acidity = "微酸";
break;
case 2:
Chinese_level_acidity = "稍酸";
break;
case 3:
Chinese_level_acidity = "酸";
break;
case 4:
Chinese_level_acidity = "很酸";
break;
}
switch (table.level_sweetness){
case 0:
Chinese_level_sweetness = "不甜";
break;
case 1:
Chinese_level_sweetness = "微甜";
break;
case 2:
Chinese_level_sweetness = "稍甜";
break;
case 3:
Chinese_level_sweetness = "甜";
break;
}
if (table.count_spicy != 0){
s.append(" 川菜 ").append(table.count_spicy).append(" ").append(Chinese_level_spicy);
}
if (table.count_acidity != 0){
s.append(" 晋菜 ").append(table.count_acidity).append(" ").append(Chinese_level_acidity);
}
if (table.count_sweetness != 0){
s.append(" 浙菜 ").append(table.count_sweetness).append(" ").append(Chinese_level_sweetness);
}
if (s.toString().isEmpty()){
s = new StringBuilder(" ");
}
return s.toString(); // 返回 StringBuilder 对象的字符串表示
}
5.期中考试
题目:
7-1 测验1-圆类设计
创建一个圆形类(Circle),私有属性为圆的半径,从控制台输入圆的半径,输出圆的面积
输入圆的半径,取值范围为(0,+∞)
,输入数据非法,则程序输出Wrong Format
,注意:只考虑从控制台输入数值的情况
输出圆的面积(保留两位小数,可以使用String.format(“%.2f”,输出数值)控制精度)
7-2 测验2-类结构设计
设计一个矩形类,其属性由矩形左上角坐标点(x1,y1)及右下角坐标点(x2,y2)组成,其中,坐标点属性包括该坐标点的X轴及Y轴的坐标值(实型数),求得该矩形的面积。
7-3 测验3-继承与多态
将测验1与测验2的类设计进行合并设计,抽象出Shape父类(抽象类),Circle及Rectangle作为子类
7-4 测验4-抽象类与接口
在测验3的题目基础上,重构类设计,实现列表内图形的排序功能(按照图形的面积进行排序)。
代码分析:
第三题:
import java.util.Scanner;
abstract class Shape{
public Shape(){
}
public abstract double getArea();
}
class Rectangle extends Shape{
private double x1, y1; // 左上角坐标
private double x2, y2; // 右下角坐标
// 构造函数
public Rectangle(double x1, double y1, double x2, double y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
// 计算面积方法
public double getArea() {
double width = Math.abs(x2 - x1);
double height = Math.abs(y2 - y1);
double area = width * height;
return area;
}
}
class Circle extends Shape{
private double r;
private double area;
public Circle(double r){
this.r = r;
}
public boolean is_valid(double r){
if(r<=0){
System.out.println("Wrong Format");
return false;
}
return true;
}
public double getArea(){
double S = 0;
double pi = Math.PI;
S = pi * this.r*this.r;
this.area = S;
return S;
}
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner input = new Scanner(System.in);
int choice = input.nextInt();
switch(choice) {
case 1://Circle
double radiums = input.nextDouble();
Shape circle = new Circle(radiums);
if(((Circle) circle).is_valid(radiums)){
printArea(circle);
}
break;
case 2://Rectangle
double x1 = input.nextDouble();
double y1 = input.nextDouble();
double x2 = input.nextDouble();
double y2 = input.nextDouble();
Rectangle rectangle = new Rectangle(x1, y1, x2, y2);
printArea(rectangle);
break;
}
}
static void printArea(Shape shape){
System.out.println(String.format("%.2f", shape.getArea()));
}
}
Shape
是一个抽象类,它具有一个抽象方法getArea()
,用于计算面积。Shape
类的目的是为其他具体图形类提供一个统一的接口。Rectangle
类是Shape
类的子类,表示矩形。它包含了四个坐标的成员变量和相应的构造函数。Rectangle
类实现了getArea()
方法,根据矩形的坐标计算面积。Circle
类是Shape
类的另一个子类,表示圆形。它包含了半径r
和面积area
两个成员变量,以及一个is_valid()
方法判断半径是否合法。Circle
类同样实现了getArea()
方法,通过半径计算面积。Main
类是程序的主类,包含了main()
方法。在main()
方法中,首先从用户输入中获取一个整数choice
,根据用户选择的不同,创建相应的图形对象(Circle
或Rectangle
),并调用printArea()
方法打印出图形的面积。- 当
choice
为 1 时,表示选择计算圆形的面积,需要输入半径,然后创建Circle
对象并判断半径是否合法,如果合法则调用printArea()
方法打印面积。 - 当
choice
为 2 时,表示选择计算矩形的面积,需要输入矩形的四个坐标,然后创建Rectangle
对象,并调用printArea()
方法打印面积。
- 当
printArea()
方法接受一个Shape
类型的参数,根据传入的具体图形对象调用对应的getArea()
方法,然后打印出面积。
三、采坑心得
1.要注意特色菜打了折,就不会再参与其他折扣。当时第一次的时候计算的时候没有注意
导致我计算的价格更低,然后通过仔细计算测试点的结果是什么算出来的,然后就发现了自己的错误在哪了
2.在菜单4的时候有一个错误的菜谱信息测试点一直过不了,测了很多次才发现可能是我的正则表达是在接受连续两个空格的时候,算做了正确的信息格式,所以导致测试点过不去,然后就正则表达式进行了修改,然后测试点就过了。
3.对于一些没有给出的测试点,需要通过不断地测试,去发现自己代码存在的问题。
四、改进建议
对于我自己的代码,在主函数中if-else的嵌套是由的过多,导致可读性差,过多的嵌套会导致代码结构复杂,可读性较差。阅读和理解代码会变得困难,尤其是在嵌套层数较深时。
可以使用多态、策略模式、状态模式等设计模式来提供更清晰、可扩展和可维护的代码结构。另外,合理使用代码重构技术,如提取方法和类,可以将复杂的嵌套结构拆解成更简洁清晰的代码片段。
五、总结
在本次实验中,我完成了PTA题目集中的菜单计价程序的设计和实现。通过这个实验,我对类与类之间的关系、类的属性和方法、继承和多态等概念有了更深入的理解,并学会了如何应用这些知识点进行程序设计。
在实验的过程中,我通过分析题目要求,设计了合适的类和方法,并根据需求逐步完善和优化程序。我学会了如何进行类的封装和继承,合理组织类之间的关系,使代码更加清晰和易于理解。同时,我也学会了如何处理异常情况,保证程序的稳定性和健壮性。在实验过程中,我遇到了一些问题和困难。有时由于对题目要求理解不准确,导致代码设计出错;有时由于逻辑复杂,代码中嵌套过多的if-else语句,导致代码可读性较差;还有一些测试点没有给出,需要自己进行测试和调试。但是通过不断的学习和思考,我解决了这些问题,并不断改进和优化代码。通过本次实验,我不仅学会了如何设计和实现菜单计价程序,还锻炼了自己的编程和问题解决能力。我学会了如何合理划分类和方法,如何设计良好的代码结构,如何处理各种异常情况。这些知识和经验将对我今后的编程学习和实践有很大的帮助。我深刻认识到了编程的重要性和挑战性。编程需要细心、耐心和持续学习的精神,我将继续努力学习和提高自己的编程能力,以应对更复杂和挑战性的问题。同时,我也认识到合作和交流的重要性,通过与同学共同学习和讨论,相互帮助和分享经验,我收获了更多的知识和思路。
本次实验让我从理论到实践地运用了类与类之间的关系、类的属性和方法、继承和多态等知识点。通过不断地迭代和优化代码,我提高了自己的编程能力和代码质量。我相信这些经验和知识对我的编程学习和职业发展都会起到积极的推动作用。