前三次PTA作业总结

------------恢复内容开始------------
一、作业总结
我们总共进行了三次题目集的练习,无论是题目量,题目难度还是知识点,都是逐步递增的,并且越进行到后面的题目,运用到的技术也越来越多,在一周内(并且还有其他的课业的情况下)完成还是颇具难度的。

第一次题目集总体难度并不大,主要考察的是对输入输出最基本的运用,更多的还是面向过程的运用,只有最后一题判断三角形需要考虑的多一点。

第二次题目集相比与第一次,难度就有着一定的提升了,第二次的四题开始考察类与方法的运用,慢慢开始有着面向对象的影子了。

第三次题目的难度与前两次相比,难度就开始直线上升了,代码的耦合度开始迅速上升,开始运用继承,多态以及接口等,对于这次题目,我也想了很久才做出来,期中遇到的问题也是最多的,调试测试点的过程也是很痛苦,不过好在最后也是成功钻研了出来。

二、设计与分析

主要分析菜单计价程序1,菜单计价程序2和菜单计价程序3:

菜单计价程序一
菜品类:对应菜谱上一道菜的信息。
Dish {
String name;//菜品名称
int unit_price; //单价
int getPrice(int portion)//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份)
}

菜谱类:对应菜谱,包含饭店提供的所有菜的信息。
Menu {
Dish[] dishs ;//菜品数组,保存所有菜品信息
Dish searthDish(String dishName)//根据菜名在菜谱中查找菜品信息,返回Dish对象。
}

点菜记录类:保存订单上的一道菜品记录
Record {
Dish d;//菜品
int portion;//份额(1/2/3代表小/中/大份)
int getPrice()//计价,计算本条记录的价格
}

订单类:保存用户点的所有菜的信息。
Order {
Record[] records;//保存订单上每一道的记录
int getTotalPrice()//计算订单的总价
Record addARecord(String dishName,int portion)
//添加一条菜品信息到订单中。
}

输入格式:
每条点菜记录的格式:
菜名+空格(英文)+份额
注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
最后一条记录以“end”结束。

输出格式:
订单上所有菜品的总价(整数数值),每份菜
如果订单中包含不能识别的菜名,则在总价之前输出“ does not exist”,是不能识别的菜名

类图

image

方法图

image

image

菜单计价系统1:main函数通过调用 菜品类Dish 菜谱类Menu 点菜记录类Record 订单类Order 4个类来完成相应功能,然后创建一个菜谱对象和一个订单对象,接受的一整行的输入,将这一行沿空格进行分割,将相应的份数转换为浮点数,再将点菜的菜名与菜谱对象进行比较,检查对应的菜名,

运行结果如下:
image
代码如下:import java.util.*;
import java.util.Scanner;

// 菜品类
class Dish {
String name; // 菜品名称
int unit_price; // 单价

Dish(String name, int unit_price) {
    this.name = name;
    this.unit_price = unit_price;
}

// 计算菜品价格的方法
int getPrice(int portion) {
    double price = unit_price;
    if (portion == 2) {
        price *= 1.5;
    } else if (portion == 3) {
        price *= 2;
    }
    return (int) Math.round(price);
}

}

// 菜谱类
class Menu {
Dish[] dishes; // 菜品数组,保存所有菜品信息

// 构造函数
Menu(Dish[] dishes) {
    this.dishes = dishes;
}

// 根据菜名在菜谱中查找菜品信息
Dish searchDish(String dishName) {
    for (Dish dish : dishes) {
        if (dish.name.equals(dishName)) {
            return dish;
        }
    }
    return null;
}

}

// 点菜记录类
class Record {
Dish dish; // 菜品
int portion; // 份额(1/2/3代表小/中/大份)

// 构造函数
Record(Dish dish, int portion) {
    this.dish = dish;
    this.portion = portion;
}

// 计价,计算本条记录的价格
int getPrice() {
    return dish.getPrice(portion);
}

}

// 订单类
class Order {
List records; // 保存订单上每一道的记录

// 构造函数
Order() {
    this.records = new ArrayList<>();
}




Record addARecord(String dishName, int portion, Menu menu) {
    Dish dish = menu.searchDish(dishName);
    if (dish == null) {
        System.out.println(dishName + " does not exist");
        return null;
    }
    Record record = new Record(dish, portion);
    records.add(record);
    return record;
}


int getTotalPrice() {
    int totalPrice = 0;
    for (Record record : records) {
        totalPrice += record.getPrice();
    }
    return totalPrice;
}

}

// 主函数
public class Main {
public static void main(String[] args) {
// 创建菜谱
Dish[] dishes = {
new Dish("西红柿炒蛋", 15),
new Dish("清炒土豆丝", 12),
new Dish("麻婆豆腐", 12),
new Dish("油淋生菜", 9)
};
Menu menu = new Menu(dishes);

    // 创建订单
    Order order = new Order();

    // 读取点菜记录
    Scanner scanner = new Scanner(System.in);
    while (true) {
        String line = scanner.nextLine();
        if (line.equals("end")) {
            break;
        }
        String[] parts = line.split(" ");
        String dishName = parts[0];

        int portion = Integer.parseInt(parts[1]);
        order.addARecord(dishName, portion, menu);
    }

    // 输出总价
    System.out.println(order.getTotalPrice());
}

}

菜单计价系统二:
设计点菜计价程序,根据输入的信息,计算并输出总价格。

输入内容按先后顺序包括两部分:菜单、订单,最后以"end"结束。

菜单由一条或多条菜品记录组成,每条记录一行

每条菜品记录包含:菜名、基础价格 两个信息。

订单分:点菜记录和删除信息。每一类信息都可包含一条或多条记录,每条记录一行。
点菜记录包含:序号、菜名、份额、份数。
份额可选项包括:1、2、3,分别代表小、中、大份。

删除记录格式:序号 delete

标识删除对应序号的那条点菜记录。

不同份额菜价的计算方法:
小份菜的价格=菜品的基础价格。
中份菜的价格=菜品的基础价格1.5。
小份菜的价格=菜品的基础价格2。
如果计算出现小数,按四舍五入的规则进行处理。

参考以下类的模板进行设计:
菜品类:对应菜谱上一道菜的信息。

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)//根据序号查找一条记录
}
输入格式:
菜品记录格式:

菜名+英文空格+基础价格

如果有多条相同的菜名的记录,菜品的基础价格以最后一条记录为准。

点菜记录格式:
序号+英文空格+菜名+英文空格+份额+英文空格+份数
注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。

删除记录格式:序号 +英文空格+delete

最后一条记录以“end”结束。

输出格式:
按顺序输出每条订单记录的处理信息,

每条点菜记录输出:序号+英文空格+菜名+英文空格+价格。其中的价格等于对应记录的菜品*份数,序号是之前输入的订单记录的序号。
如果订单中包含不能识别的菜名,则输出“** does not exist”,**是不能识别的菜名

如果删除记录的序号不存在,则输出“delete error”

最后输出订单上所有菜品的总价(整数数值),

UML图如下:
image

运行结果如下:
image

image

代码如下:
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner input = new Scanner(System.in);
Menu menu = new Menu();
Order order = new Order();
Tranform tranform = new Tranform();
String s;
String name;
int price;
int portion;
int id;
int a;
int dishsum;
while(true){
s = input.nextLine();
String[] split = s.split(" "); //分割符操作
if(split[0].equals("end")){
break;
}
a = tranform.getsize(split);
if(a1){
name = split[0];
price = Integer.parseInt(split[1]);
menu.addDish(name,price);
}
else if(a
2){
id = Integer.parseInt(split[0]);
name = split[1];
portion = Integer.parseInt(split[2]);
dishsum = Integer.parseInt(split[3]);
Dish dish = menu.searthDish(name);
if(dishnull){
System.out.println(name+" does not exist");
}
else{
System.out.println(id+" "+name+" "+dish.getprice(portion,dishsum));
order.addARecord(id,dish,portion,dishsum);//添加订单
}
}
else if(a
3) {
id = Integer.parseInt(split[0]);
Record record = order.findRecordByNum(id);
if (record == null) {
System.out.println("delete error");
} else {
order.delARecordByOrderNum(id);
}
}
}
int sum=0;
sum= order.getTotalPrice(); //计算总和
System.out.println(sum);
}
}

class Dish {
String name;//菜品名称
int price; //单价
public Dish(String name,int price){
this.name = name;
this.price = price;
}
public int getprice(int portion,int dishsum){
if(portion1) {
return price*dishsum;
}
else if(portion
2) {
int Iprice = (int) (price1.5);
if(price-Iprice>=0.5){
return (Iprice+1)
dishsum;
}
else {
return Ipricedishsum;
}
}
else if(portion==3) {
return price
2*dishsum;
}
return 0;
}//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份;
}

class Menu {

Dish[] dishs = new Dish[100];

public Dish searthDish(String dishName){
    for(int i=0;i<dishs.length;i++) {
        if(dishs[i]!=null && dishs[i].name.equals(dishName)) {
            return dishs[i];
        }
    }
    return null;
}
public Dish addDish(String dishName,int price){
    Dish dish = new Dish(dishName,price);
    for(int i=0;i<dishs.length;i++){
        if(dishs[i]==null){
            dishs[i] = dish;
            break;
        }
    }
    return dish;
}

}

class Order {
Record[] records = new Record[100];//保存订单上每一道的记录
public int getTotalPrice() {
int sum = 0;
for(int i=0;i<records.length;i++) {
if(records[i]!=null) {
records[i].getPrice();
sum+=records[i].getPrice();
}
}
return sum;
}
public Record addARecord(int id,Dish dish,int portion,int dishsum){
//Menu menu = new Menu();
//Dish dish = menu.searthDish(dishName);
Record record = new Record(dish,portion,dishsum);
records[id] = record;
return record;
}
//添加一条菜品信息到订单中。
public Record findRecordByNum(int id){
return records[id];
}
public Record delARecordByOrderNum(int id){
records[id]=null;
return null;
}
}

class Record {
int dishsum;
Dish d;//菜品
int portion;//份额(1/2/3代表小/中/大份)
public Record(Dish dish, int portion,int dishsum){
this.dishsum = dishsum;
this.d=dish;
this.portion=portion;
}
int getPrice(){
int price = d.getprice(portion,dishsum);
return price;
}//计价,计算本条记录的价格
}

class Tranform {
public int getsize(String[] split) {
String id = split[0];
String[] arr = {"1", "2", "3", "4", "5", "6", "7", "8", "9"};
for (int i = 0; i < 9; i++) {
if (id.substring(0, 1).equals(arr[i])) {
String cd = split[1];
if (cd.equals("delete")) {
return 3;
} else {
return 2;
}
}
}
return 1;
}
}

菜单计价系统三:
设计点菜计价程序,根据输入的信息,计算并输出总价格。

输入内容按先后顺序包括两部分:菜单、订单,最后以"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+英文空格+桌号+“:”+英文空格+当前桌的总价

UML图:
首先引入一个时间类:

import java.time.LocalDateTime;
以下是time类代码:

class Time{
String time1;
String time2;
int year;
int month;
int day;
int hour;
int minute;
int weekday;
void getWeekday()
{
this.weekday= LocalDateTime.of(this.year,this.month,this.day,this.hour,this.minute).getDayOfWeek().getValue();
}
void getYear()
{
String [] k=time1.split("\/");
year=Integer.parseInt(k[0]);
month=Integer.parseInt(k[1]);
day=Integer.parseInt(k[2]);
}
void getDay()
{
String[] k = time2.split("\/");
hour=Integer.parseInt(k[0]);
minute=Integer.parseInt(k[1]);
}
}

这里的时间类就是将传入的字符串按顺序分割,获得是在星期几的几点。然后在table里计算价格的时候使用。

以下为table类代码:

class Table{

int num;
Time time=new Time();
Order order=new Order();
int tablePrice;void getPrice()
{
    time.getDay();
    time.getYear();
    time.getWeekday();
    if(time.weekday<=5&&time.weekday>=1)
    {
        if((time.hour>=17&&time.hour<20)||(time.hour==20&&time.minute<=30))
        {
            tablePrice=(int)Math.round(order.getTotalPrice()*0.8);
            System.out.print("table "+this.num+": "+this.tablePrice+" ");
        }
        else if((time.hour==10&&time.minute>=30)||(time.hour>=11&&time.hour<14)||(time.hour==14&&time.minute<=30))
        {
            tablePrice=(int)Math.round(order.getTotalPrice()*0.6);
            System.out.print("table "+this.num+": "+this.tablePrice+" ");
        }
        else System.out.println("table "+this.num+" out of opening hours");
    }
    if(time.weekday==6||time.weekday==7)
    {
        if((time.hour==9&&time.minute>=30)||(time.hour>9&&time.hour<21)||(time.hour==21&&time.minute<=30))
        {
            tablePrice=(int)Math.round(order.getTotalPrice());
            System.out.print("table "+this.num+": "+this.tablePrice+" ");

        }
        else System.out.println("table "+this.num+" out of opening hours");
    }
}

}

这里我在获得价格的同时直接输出结果(对获得的时间进行判断然后计算相应折扣),所以在主函数直接调用就行,而且这里一个桌子就代表着一个订单,则在内部再定义一个order类。还有一个桌号的属性。

主函数main就需要重新写过,因为每个字符串的长度发生了变化,所以出现的情况会有所不同,main函数代码如下:

public static void main(String[] args)
{
    Scanner sc=new Scanner(System.in);
    Table[] table=new Table[10];
    Menu menu=new Menu();
    int i=0;
    String str=sc.nextLine();
    while(!str.equals("end"))
    {
        String[] data = str.split(" ");
        if(data.length==2&&!data[1].equals("delete"))
        {
            menu.add(data[0],Integer.parseInt(data[1]));
        }
        if(data.length==4&&data[0].equals("table"))
        {
            i++;
            table[i]=new Table();
            table[i].order=new Order();
            table[i].order.menu=menu;
            table[i].num=Integer.parseInt(data[1]);
            table[i].time.time1=data[2];
            table[i].time.time2=data[3];
            System.out.println("table "+Integer.parseInt(data[1])+":");
        }
        if(data.length==4&&!data[0].equals("table"))
        {
            if(menu.searthDish(data[1])!=null)
            {
                table[i].order.addARecord(Integer.parseInt(data[0]), data[1], Integer.parseInt(data[2]), Integer.parseInt(data[3]));
                Dish d = new Dish();
                d=menu.searthDish(data[1]);
                System.out.println(data[0]+" "+data[1]+" "+d.getPrice(Integer.parseInt(data[2]))*Integer.parseInt(data[3]));
            }
            else if(menu.searthDish(data[1])==null)
            {
                System.out.println(data[1]+ " does not exist");
            }
        }
        if(data.length==5)
        {
            if(menu.searthDish(data[2])!=null)
            {
                table[i].order.addARecord(Integer.parseInt(data[1]),data[2],Integer.parseInt(data[3]),Integer.parseInt(data[4]));
                Dish d = new Dish();
                d=menu.searthDish(data[2]);
                System.out.println(data[1]+" "+"table"+" "+table[i].num+" pay for table "+data[0]+" "+d.getPrice(Integer.parseInt(data[3]))*Integer.parseInt(data[4]));
            }
            else if(menu.searthDish(data[2])==null)
            {
                System.out.println(data[2]+ " does not exist");
            }
        }
        if(data.length==2&&data[1].equals("delete"))
        {
            table[i].order.delARecordByOrderNum(Integer.parseInt(data[0]));
        }
        str=sc.nextLine();
    }
    for(int num=1;num<=i;num++)
    {
        table[num].getPrice();
    }
    sc.close();
}

由于第三次的类图与uml图较为混乱且有缺陷,在此就不做展示了。

三、踩坑心得
在实现第三道题目中,最困难的是维护数量不确定的订单中的菜品,特别是在添加订单记录时需要动态地增加记录,在Java中可以使用数组实现。同时,在写查询单个菜品的接口时,
需要注意到可能返回null值的情况,必须进行判断以避免程序崩溃。除此之外,整个程序的设计并没有太大的问题,实现起来比较顺畅。

在写代码时,最重要的是规划,如果一个for循环就可以解决的问题,不必用更多的for循环而导致运行超时。
四、主要困难与踩坑建议

应让代码更加符合规范。此外,尽可能的将方法简化,将重复代码提出,减少重复代码,提高代码的复用性。
SouranceMonitor的生成报表图可知,有简单明了的注释,在自己写代码的过程中也能够让自己读懂,方便前后增添、修改代码。除代码的注释外,对变量的命名也要规范,要使用简单易懂,具有意义的名称,使代码易于理解与维护。

五、总结
java中的已经定义的类是非常多的,仅仅是写题目的时候用一次只能有一个粗略映像,需要花其他时间去巩固。此外,在写题目时,不要急于求成,也不要不带脑子写代码,使得写出的代码不仅得分不高,质量还低

posted on 2023-05-24 23:45  ADBBAB  阅读(58)  评论(0)    收藏  举报

导航