一、前言

对于前三次的点菜系列的PTA大作业所涉及到的知识点大致是差不多的

  ·第一次的作业是点菜作业的起点,其实现了对单道菜和多道菜的点单功能以及对异常菜菜名进行处理

  ·第二次的作业是在点菜一的基础上实现对点菜记录的删除等一系列操作

  ·第三次的作业是在点菜二的基础上实现多桌点菜以及对菜馆开放的时间做出相应的安排

二、设计与分析

第一次点菜作业:

  1.基于题设所给出的类

  菜品类:对应菜谱上一道菜的信息。

  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)
  //添加一条菜品信息到订单中。
  }

  修改后的类

  class Dish
  {
  String name="";//菜品名称
  int unit_price;
  //单价
  int getPrice(int portion)
  {
    int cai=0;
    if(portion==1)
    {
      cai=unit_price;
    }
    if(portion==2)
    {
      cai=Math.round(unit_price*1.5f);
    }
    if(portion==3)
    {
      cai=unit_price*2;
    }
    return cai;
  }
  }
  class allMenu
  {
    Dish[] dishs =new Dish[15];
    Dish searthDishone(String dishName) //根据菜名在菜谱中查找菜品信息,返回Dish对象。
    {
      dishs[0]=new Dish();
      dishs[1]=new Dish();
      dishs[2]=new Dish();
      dishs[3]=new Dish();
      dishs[0].name="西红柿炒蛋";
      dishs[0].unit_price=15;
      dishs[1].name="清炒土豆丝";
      dishs[1].unit_price=12;
      dishs[2].name="麻婆豆腐";
      dishs[2].unit_price=12;
      dishs[3].name="油淋生菜";
      dishs[3].unit_price=9;
      int i=0;
      int flag=0;
      for(i=0;i<4;i++)
      {
        if(dishName.compareTo(dishs[i].name)==0)
        {
          flag=1;
          break;
        }
      }
      if(flag==1)
        return dishs[i];
      else
      {
        return null;
      }
    }
  }
  class Record
  {
      Dish d=new Dish();//菜品
      int portion;
      int getPrice()
      {
        return d.getPrice(portion);
      }
  }
  class Order
  {
    Record[] records=new Record[10];
    int j=0;
    int getTotalPrice(Order f)
    {
      int sum=0,i=0;
      for(i=0;i<j;i++)
      {
        sum+=f.records[i].getPrice();
      }
      return sum;
    }
    Record addARecord(String dishName,int portion)
    {
      Record r=new Record();
      r.d.name=dishName;
      r.portion=portion;
      j++;
      return r;
    }
  }

  2.程序执行

  在这里菜谱是已经定好的,我们所要做的就只是去点菜

  allMenu c=new allMenu();//这个是菜谱对象,在这里面是已经放好了菜品的信息

  abuu=input.next();//字符串的输入代表的是点菜的开始

  while(!abuu.equals("end"))//判断输入的是否是结束的标志
  {

    b=input.nextInt();//输入份数
    f.records[i]=new Record();//存放入订单信息
    f.records[i]=f.addARecord(abuu,b);
    i++;
    abuu=input.next();//输入下一条信息
  }
  //计算单菜价格
  for(int j=0;j<i;j++)
  {
  //查找
    g=c.searthDishone(f.records[j].d.name);
  //计算
    if(g!=null)
      f.records[j].d.unit_price=g.unit_price;
    else
      System.out.println(f.records[j].d.name+" does not exist");
  }

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

  3.执行效果

  

第二次点菜作业:

  1.基于题设所给出的类

  菜品类:对应菜谱上一道菜的信息。

  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(String dishName,int portion)
  //添加一条菜品信息到订单中。
  }

  修改后的基本类如下

class Dish{

  String name;//菜品名称
  int unit_price; //单价//总价
  int getPrice(int portion)//计算菜品价格的方法
  {
    double shui=0;
    if(portion==1)
  {  
    shui=unit_price*1;
  }
  if(portion==2)
  {
    shui=unit_price*1.5;
  }
  if(portion==3)
  {
    shui=unit_price*2;
  }
  return (int)Math.round(shui);
  }
  }
  class Menu{
  Dish[] dishs=new Dish[250];
  Dish searthDish(String dishName,int k)//根据菜名在菜谱中查找菜品信息,返回Dish对象。
  {
    for(int i=0;i<k;i++)
    {
      if(dishName.equals(dishs[i].name))
      {
        return dishs[i];
      }
    }
    return null;
  }
  Dish addDish(String dishName,int unit_price)//添加一道菜品信息
  {
    Dish d=new Dish();
    d.name=dishName.substring(0);
    d.unit_price=unit_price;
    return d;
  }
  }
  class Record
  {
    int orderNum;
    Dish d;
    int portion;//份额(1/2/3代表小/中/大份)
    int flag=0;//是否被删除
    int num;
    int getPrice()//计价,计算本条记录的价格
    {
      return (int)Math.round(d.getPrice(portion)*num);
    }
  }
  class Order {
  Record[] records=new Record[120];//保存订单上每一道的记录
  int getTotalPrice(int n)//计算订单的总价
  {
    int shabi=0;
    for(int i=1;i<=n;i++)
    {
      if(records[i].flag==0)
      {
        shabi+=records[i].getPrice();
      }
    }
    return shabi;
   }
  Record addARecord(int orderNum,Dish d,int portion,int num)//添加一条菜品信息到订单中。
  {
    Record ruu=new Record();
    ruu.d=new Dish();
    ruu.orderNum=orderNum;
    ruu.d=d;
    ruu.portion=portion;
    ruu.num=num;
    return ruu;
  }
  void delete(int orderNum)//根据序号删除一条记录
  {
    records[orderNum].flag=1;
  }
  }

  2.程序执行

  在此基础上,我们可以在程序的一开始就下创建好菜谱类的对象,用来存放菜品的信息。

  Menu menu=new Menu();

  然后根据输入的字符串进行解析(因为给菜谱加菜的时候开头的总是文字,因此我们可以定义一个判断标志flag去判定什么时候开头的不是文字,那么就代表点菜的开始)

  sbucc=in.nextLine();//字符串的输入

  int flag=0;//假定一开始为0代表的是一直给菜谱加菜

  while(!sbucc.equals("end"))//不是结束标志那么就进入循环

  String[] b=sbucc.split(" ");//分割字符串

 

  if(b[0].equals("1"))

    flag=1;//进行判定,如果开头的不是文字,那么代表点菜开始

  if(flag==0)

  {
    menu.dishs[i]=new Dish();
    if(menu.searthDish(b[0],i)==null)
  {
    menu.dishs[i]=menu.addDish(b[0],Integer.parseInt(b[1]));
    i++;
  }
  else{

    menu.searthDish(b[0],i).unit_price=Integer.parseInt(b[1]);

  }//这里再菜谱类里面定义了菜谱信息增加的功能以及菜品信息重样,以后加的菜品信息为准的功能

  对菜谱信息进行完善之后就开始点菜的环节了,还是对输入的字符串进行处理 

  else

  {
    if(menu.searthDish(b[1],i)!=null)//先寻找菜谱中是不是有自己要点的菜
    {
      num=Integer.parseInt(b[0]);//点菜的顺序
      order.records[num]=new Record();//创建订单记录
      order.records[num]=order.addARecord(num,menu.searthDish(b[1],i),Integer.parseInt(b[2]),Integer.parseInt(b[3]));//增加菜品信息
      System.out.println(order.records[num].orderNum+" "+order.records[num].d.name+" "+order.records[Integer.parseInt(b[0])].getPrice());//根据题意输出
    }
    else if(b[1].equals("delete"))//判断是不是执行删除操作
    {
      if(Integer.parseInt(b[0])<=num&&Integer.parseInt(b[0])>=1&&order.records[Integer.parseInt(b[0])].flag==0)//对删除的序号进行合理的判断
        order.delete(Integer.parseInt(b[0]));//要删除的是存在的点菜记录的话,那就执行删除
      else
        System.out.println("delete error;");//要删除的序号不存在,输出提示
    }
    else//要点的菜是菜谱上不存在的菜
    {
      num=Integer.parseInt(b[0]);
      order.records[num]=new Record();
      Dish dish=new Dish();
      dish.name=dish.name+b[1];
      dish.unit_price=0;
      order.records[num]=order.addARecord(num,dish,Integer.parseInt(b[2]),Integer.parseInt(b[3]));
      System.out.println(b[1]+" does not exist");//给出相关的输出
    }
  }
  sbucc=in.nextLine();//对下此输出的字符串进行相应的解析处理
  }

  最后输出相应点菜的总价

  System.out.println(f.getTotalPrice(f));

  3.执行效果

  

第三次点菜作业:

  1.基于作业二的类改进后的类

class Table {
//设置私有属性
private String index;
private String dayTime;
private String time;
private Order order;
public String getIndex()
{
  return index;
}
public void setIndex(String index)
{
  this.index = index;
}
public void setDayTime(String dayTime)
{
  this.dayTime = dayTime;
}

public void setTime(String time) {
  this.time = time;
}

public Order getOrder() {
  return order;
}

public void setOrder(Order order) {
  this.order = order;
}

private Calendar getTimeCalendar(String time) {
  String[] arr = time.split("/");
  Calendar cal = Calendar.getInstance();
  cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(arr[0]));
  cal.set(Calendar.MINUTE, Integer.parseInt(arr[1]));
  cal.set(Calendar.SECOND, Integer.parseInt(arr[2]));
  return cal;
}

public int getSale() {
  String[] dayArray = time.split("/");
  Calendar calendar = Calendar.getInstance();
  calendar.set(Calendar.DAY_OF_MONTH, 32);
  calendar.set(Integer.parseInt(dayArray[0]), Integer.parseInt(dayArray[1])-1, Integer.parseInt(dayArray[2]));
  Calendar now = getTimeCalendar(dayTime);
  int weekDay = calendar.get(Calendar.DAY_OF_WEEK) - 1;
  if (weekDay == 0 || weekDay == 6) {
  if (!now.before(getTimeCalendar("9/30/0")) && !now.after(getTimeCalendar("21/00/00"))) {
  return 10;
}
}

else {
  if (!now.before(getTimeCalendar("17/00/00")) && !now.after(getTimeCalendar("20/30/00"))) {
    return 8;
}
if (!now.before(getTimeCalendar("10/30/00")) && !now.after(getTimeCalendar("14/30/00"))) {
    return 6;
}
}
    return 0;
}
}

class Menu {
private final Map<String, Dish> dishs = new HashMap<>();//菜品数组,保存所有菜品信息

Menu() {
}

Dish searchDish(String dishName) {
  return dishs.get(dishName);
}
void addDish(String dishName, int unit_price) {
  Dish dish = new Dish(dishName, unit_price);
  dishs.put(dishName, dish);
}
}

class Dish {
  String name;//菜品名称
  int unit_price; //单价
  public Dish(String dishName) {
  this.name = dishName;
}
public String getName() {
  return name;
}


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

//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份)
int getPrice(int portion) {
  float b[] = {1, 1.5f, 2};
  return Math.round((unit_price * b[portion - 1]));
}
}

class Record {
private int orderNum;//序号
private Dish d;//菜品
private int portion;//份额(1/2/3代表小/中/大份)
private int num;
private String from;
private String to;
private boolean isDelete = false;
private boolean isAddError = false;
private boolean isDelError = false;

public Record() {
}

  public Record(int orderNum, Dish d, int portion, int num, String from, String to) {
  this.orderNum = orderNum;
  this.d = d;
  this.portion = portion;
  this.num = num;
  this.from = from;
  this.to = to;
}

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

public int getOrderNum() {
  return orderNum;
}

public Dish getD() {
  return d;
}

public void setD(Dish d) {
  this.d = d;
}

public boolean isDelete() {
  return isDelete;
}

public void setDelete(boolean delete) {
  isDelete = delete;
}

public boolean isAddError() {
  return isAddError;
}

public void setAddError(boolean addError) {
  isAddError = addError;
}

public boolean isDelError() {
  return isDelError;
}

public void setDelError(boolean delError) {
  isDelError = delError;
}

public String getFrom() {
  return from;
}

public String getTo() {
  return to;
}

public void setTo(String touu) {
  this.to = touu;
}
}

class Order {
private Menu menu;
private Table table;
public List<Record> getRecords() {
return records;
}
private final List<Record> records = new ArrayList<>();//保存订单上每一道的记录
public Order(Menu majj, Table tabb)
{
  this.menu = majj;
  this.table = tabb;
}
int getTotalPrice()
{
  int sumuu = 0;
  for (Record record : records)
{
if (!record.isDelete() && !record.isDelError() && !record.isAddError() && table.getIndex().equals(record.getFrom()))
{
  int priceuu = record.getPrice();
  sumuu = sumuu + priceuu;
}
}
return sumuu;
}
void addARecord(int orderNum, String dishName, int portion, int num, String from, String to) {
Dish dish = menu.searchDish(dishName);
if (dish == null && table.getIndex().equals(from)) {
Record record = new Record();
record.setD(new Dish(dishName));
record.setAddError(true);
records.add(record);
return;
}
Record record = new Record(orderNum, dish, portion, num, from, to);
records.add(record);
}

public void delARecordByOrderNum(int orderNum) {
for (Record record : records) {
if (record.getOrderNum() == orderNum) {
record.setDelete(true);
return;
}
}
Record record = new Record();
record.setDelError(true);
records.add(record);
}
}

2.程序开始

还是一开始构造菜谱

Menu menu = new Menu();//初始化一个菜单

  此外,因为增加了多桌点菜,所以在这里需要加上一个桌子的链表

   List<Table> tables = new ArrayList<>();//构造一个数组链表

  开始输入

  String line = in.nextLine();//输入

  对输入的字符串进行解析操作

  while (!line.equals("end")) //判断输入的是否是结束的标志

  String[] parts = line.split(" ");//实现字符串的分隔

  对输入的字符串的长度进行研究,当输入的字符串的长度大于2的时候,那么就是执行带有为本桌子点菜,为别的桌子点菜,为本桌创立好桌子的信息记录  

                    if ("table".equals(parts[0])) //这个是对点菜那张桌子的信息记录
                    {
                        Table table = new Table();
                        tables.add(table);
                        table.setTime(parts[2]);//实现对时间方面的处理
                        table.setIndex(parts[1]);
                        table.setDayTime(parts[3]);
                        table.setOrder(new Order(menu, table));
                        curTable = table;
                    } 

  上述的代码实现了对桌子信息的处理

                    else if (parts.length == 4) //实现了对本张桌子的点菜
                    {
                        int orderNum = Integer.parseInt(parts[0]);//点菜的顺序
                        String dishName = parts[1];//点的菜的名字
                        int portion = Integer.parseInt(parts[2]);
                        int num = Integer.parseInt(parts[3]);
                        assert curTable != null;
                        curTable.getOrder().addARecord(orderNum, dishName, portion, num, curTable.getIndex(), curTable.getIndex());//完成对所点菜的订单记录
                    }

  接下来实现代点菜的功能是差不多的,在此就不多赘述了

          else if ("delete".equals(parts[1])) //这段代码是用来执行删除
                {
                    assert curTable != null;
                    curTable.getOrder().delARecordByOrderNum(Integer.parseInt(parts[0]));
                } 

  接下里来是对菜谱里面增加菜品的信息进行处理

                else 
                {
                    menu.addDish(parts[0], Integer.parseInt(parts[1]));
                }

  最后对所有的桌子信息进行处理并且输出相应的信息

            for (Table table : tables) 
            {
                System.out.println("table " + table.getIndex() + ": ");
                Order order = table.getOrder();
                for (Record record : order.getRecords()) {
                    String from = record.getFrom();
                    String to = record.getTo();
                    if (record.isAddError()) {
                        System.out.println(record.getD().getName() + " does not exist");
                    } else if (record.isDelError()) {
                        System.out.println("delete error;");
                    } else if (from.equals(table.getIndex())) {
                        if (to.equals(table.getIndex())) {
                            System.out.println(record.getOrderNum() + " " + record.getD().getName() + " " + record.getPrice());
                        } else {
                            System.out.println(record.getOrderNum() + " table " + from + " pay for table " + to + " " + record.getPrice());
                        }
                    }
                }
            }
            for (Table table : tables) {
                Order order = table.getOrder();
                int sale = table.getSale();
                if (sale > 0) {
                    System.out.println("table " + table.getIndex() + ": " + Math.round(order.getTotalPrice() * sale / 10f));
                } else {
                    System.out.println("table " + table.getIndex() + " out of opening hours");
                }
            }

  到此执行过程概述完毕

  3.执行效果

   

三、踩坑心得

  第一点:对于作业三中出现的对菜馆开门时间的判断,这是一个问题,一开始我是将时分秒分别拿出来去作比较,运用多个if判断语句去判断客户定桌子的时间和开门的时间,但是这样就会造成代码非常的杂糅,冗长,所以后来我想到了改进的方法,简化了判断的方法,以下是部分判断的代码

    private Calendar getTimeCalendar(String time) {
        String[] arr = time.split("/");
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(arr[0]));
        cal.set(Calendar.MINUTE, Integer.parseInt(arr[1]));
        cal.set(Calendar.SECOND, Integer.parseInt(arr[2]));
        return cal;
    }

  第二点:对于作业三中出现打折的方法的计算,这也是困扰我的一个点,在这里我也是一开始很笨的去用时分秒去对比,后来发现并不需要,参考营业时间的对比方法,代码如下

  

        if (weekDay == 0 || weekDay == 6) {
            if (!now.before(getTimeCalendar("9/30/0")) && !now.after(getTimeCalendar("21/00/00"))) {
                return 10;
            }
        } else {
            if (!now.before(getTimeCalendar("17/00/00")) && !now.after(getTimeCalendar("20/30/00"))) {
                return 8;
            }
            if (!now.before(getTimeCalendar("10/30/00")) && !now.after(getTimeCalendar("14/30/00"))) {
                return 6;
            }
        }

四、主要困难以及改进建议

  1.个人代码思路感觉没有问题,但是就是有些测试点过不去,有些测试点感觉就是没必要那么设计

  2.个人的编码属于C语言的块式风格的,对于java的格式控制不怎么熟悉  

五、总结

  个人总结:个人感觉做完这3次大的PTA的作业,收获是有的,从一开始的点菜一到现在的点菜三,我一路走来,虽然说自己不能把这三道题都AC把,但是个人感觉,这一系列的题目考察了我们学生的思维能力,对流程的控制,对大型设计程序框架的设计,在锻炼我们编码的能力,也悄悄为我们叩开了写出优美的大型框架之门。通过点菜系列题目的磨练,我感觉到我的心态的变化,我的心态变得更加的稳定,我的情绪波动不再会那么大。最后,通过了点菜系列题目,巩固了我对于类和对象的知识,点菜系列的题目使金典的,我很赞赏。

  个人意见:老师可以在课堂上对于点菜系列的题目进行相应的讲解,去帮助班上编程能力水平较低的学生,他们懂得可能没有那么快,其次吧,个人对该类型的题目的建议是可以适当降低难度,转向去将题目与实时所学建立联系,做到现学现用。