面向对象程序设计第12-15周内容总结
作者:互联网
(1)前言:总结之前所涉及到的知识点、题量、难度等情况
(2)设计与分析:重点对题目的提交源码进行分析,可参考SourceMonitor的生成报表内容以及PowerDesigner的相应类图,要有相应的解释和心得(做到有图有真相),本次Blog必须分析PTA中的三个资费题目
(3)采坑心得:对源码的提交过程中出现的问题及心得进行总结,务必做到详实,拿数据、源码及测试结果说话,切忌假大空
(4)改进建议:对相应题目的编码改进给出自己的见解,做到可持续改进
(5)总结:对本阶段(12-15周)综合性总结,学到了什么,哪些地方需要进一步学习及研究,对教师、课程、作业、实验、课上及课下组织方式等方面的改进建议及意见。
还没写完,持续更新ing。。。
前言
本次PTA题目集09-11主要围绕电信计费系统展开,涉及到的知识点包括继承与多态、对象容器、正则表达式等多方面的内容。每个题目集均以1+N的模式,即1道电信计费系统+N道基础题型,题量适中,其中电信计费系统难度较高,基础题型难度较低。
设计与分析
题目集09
如下是题目集09中我设计的电信计费系统的powerDesigner类图,关于整个系统的设计,我将其分为Main、用户端、计费方式、通讯记录四个板块。
1.Main
我将信息输入工作放在Main函数中完成,以下是我的Main源码:
1 import java.util.Date; 2 import java.text.ParseException; 3 import java.text.SimpleDateFormat; 4 import java.util.ArrayList; 5 import java.util.Collections; 6 import java.util.Comparator; 7 import java.util.Scanner; 8 9 public class Main { 10 11 public static Scanner input = new Scanner(System.in); 12 public static String uPattern = "[u]-0791[0-9]{7,8}\\s[0-2]"; 13 public static String tPattern = "[t]-0791[0-9]{7,8}\\s" + "0[0-9]{9,11}\\s" + 14 "((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]|[0-9][1-9][0-9]{2}|[1-9][0-9]{3})\\.(((0?[13578]|1[02])\\.(0?" + 15 "[1-9]|[12][0-9]|3[01]))|(([469]|11)\\.([1-9]|[12][0-9]|30))|(2\\.([1-9]|[1][0-9]|2[0-8]))))|(((" + 16 "[0-9]{2})([48]|[2468][048]|[13579][26])|(([48]|[2468][048]|[3579][26])00))\\.2\\.29))" + 17 "\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])\\s" + 18 "((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]|[0-9][1-9][0-9]{2}|[1-9][0-9]{3})\\.((([13578]|1[02])\\.(" + 19 "[1-9]|[12][0-9]|3[01]))|(([469]|11)\\.([1-9]|[12][0-9]|30))|(2\\.([1-9]|[1][0-9]|2[0-8]))))|(((" + 20 "[0-9]{2})([48]|[2468][048]|[13579][26])|(([48]|[2468][048]|[3579][26])00))\\.2\\.29))" + 21 "\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])"; 22 23 public static String exitPattern = "end"; 24 public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); 25 26 public static void main(String[] args) { 27 28 ArrayList<User> users = new ArrayList<User>(); 29 boolean isT = false; 30 31 while(true) { 32 String string = input.nextLine(); 33 String[] strings = string.split(" "); 34 if (string.matches(uPattern) && !isT) { 35 User user = new User(strings); 36 if (!users.contains(user)) { 37 users.add(user); 38 } 39 } else if (string.matches(tPattern)) { 40 String userNumber = strings[0].substring(2); 41 for (User each:users) { 42 if (each.getNumber().equals(userNumber)) { 43 CallRecord callRecord = new CallRecord(); 44 String startDateString = strings[2] + " " + strings[3]; 45 String endDateString = strings[4] + " " + strings[5]; 46 Date startDate; 47 Date endDate; 48 try { 49 startDate = sdf.parse(startDateString); 50 } catch (ParseException e) { 51 continue; 52 } 53 try { 54 endDate = sdf.parse(endDateString); 55 } catch (ParseException e) { 56 continue; 57 } 58 callRecord.setCallingNumber(strings[0].substring(2)); 59 callRecord.setAnswerNumber(strings[1]); 60 callRecord.setStartTime(startDate); 61 callRecord.setEndTime(endDate); 62 callRecord.setCallingAddressAreaCode("0791"); 63 if (strings[1].substring(0, 4).matches("0791")) { 64 callRecord.setAnswerAddressAreaCode("0791"); 65 each.getUserRecords().addCallingInCityRecords(callRecord); 66 } else if (strings[1].substring(0, 4).matches("07(9[0-9]{1}|01)")) { 67 callRecord.setAnswerAddressAreaCode(strings[1].substring(0, 4)); 68 each.getUserRecords().addCallingInProvinceRecords(callRecord); 69 } else { 70 callRecord.setAnswerAddressAreaCode(strings[1].substring(0, 4)); 71 each.getUserRecords().addCallingInLandRecords(callRecord); 72 } 73 isT = true; 74 } 75 } 76 } else if (string.matches(exitPattern)) { 77 break; 78 } 79 } 80 Collections.sort(users, new Comparator<User>() { 81 public int compare(User user1, User user2) { 82 return user1.getNumber().compareTo(user2.getNumber()); 83 } 84 }); 85 for (User each:users) { 86 System.out.println(each.getNumber() + " " + String.format("%.1f", each.calCost()) + " " + String.format("%.1f", each.calBalance())); 87 } 88 89 } 90 91 }
如源码第32-33行所示,我在Main中先一行行读入输入数据,当数据读入成功时,我通过split函数将接收到的数据分割,这样可以更方便的提取需要的信息;
如源码第34、39、76行所示,我将每次读到的行数据进行格式比对,如果遇到格式错误就直接跳过后续步骤,开始读入下一行数据;
如源码第34-38行所示,此时如果模式为u(开通),则程序会在用户库中检索号码是否已被开通,如果没开通,程序将会通过分割后所得的数据实例化一个带参的User对象,并将之写入到用户库中;如果未被开通,则会直接跳过后续步骤,开始读入下一行数据;
如源码第39-75行所示,此时如果模式为t(通讯),程序会在用户库中检索主叫号码是否已被开通;
如源码第42-72行所示,如果已开通,程序会实例化一个带参的CallRecord对象,根据分割后所得的数据完善这个对象的所有信息,并通过if判断,将它写入主叫号码的对应通话记录中,然后将输入模式修改为t输入模式(源码第73行),此后若读入u模式数据也会被判定为格式错误;如果未被开通,则会直接跳过后续步骤,开始读入下一行数据;
如源码第80-87行所示,当信息完全录入完毕后,程序通过Collection类中的sort函数将用户库中的用户按照号码大小排序,然后依次计算当月资费信息并输出。
2.通讯记录
通讯记录类的作用仅为保存通讯信息,故只需要完成数据的写入和输出即可,即完成getters与setters方法便可,不多做赘述。
3.计费方式
计费方式的核心便是calCost函数,其中CallChargeRule类与其下属类的calCost均是对ChargeRule中的复写;
我对于calCost函数的设计为,传入一个泛型为通讯记录类的容器,然后分别计算容器中所有通讯记录的资费并累加后传回,对于不同的计费类型(如LandPhoneInlandRule、LandPhoneInProvinceRule等),计算资费的方式会有所不同;
以下是LandPhoneInCityRule类的源码,仅供参考:
1 import java.util.ArrayList; 2 3 public class LandPhoneInCityRule extends CallChargeRule{ 4 5 public LandPhoneInCityRule() { 6 // TODO Auto-generated constructor stub 7 } 8 9 @Override 10 public double calCost(ArrayList<CallRecord> callRecords) { 11 double cost = 0; 12 for (CallRecord each:callRecords) { 13 long startTime = each.getStartTime().getTime(); 14 long endTime = each.getEndTime().getTime(); 15 int minute = (int)(endTime - startTime) / 60000; 16 int Second = (int)(endTime - startTime) % 60000 / 1000; 17 if (Second > 0) { 18 minute ++; 19 } 20 cost += minute * 0.1; 21 } 22 return cost; 23 } 24 25 }
4.用户端
用户端的主要作用为储存通讯数据,统计并输出资费信息,其中最为关键的内容为ChargeMode类;
关于chargeMode类,我的设计为:
其私有属性ArrayList<ChargeRule>用于存储相关的具体计费模式,这一步我将它放在实例化时实现;
其calCost方法需要传入一个UserRecord对象,然后对于该对象中的不同类型的通话记录采用对应的ChargeRule进行资费计算;
以LandlinePhoneCharging为例,以下是其源码与相应说明:
1 public class LandlinePhoneCharging extends ChargeMode { 2 3 private double monthlyRent = 20; 4 5 public LandlinePhoneCharging() { 6 super.getChargeRules().add(new LandPhoneInCityRule()); 7 super.getChargeRules().add(new LandPhoneInProvinceRule()); 8 super.getChargeRules().add(new LandPhoneInlandRule()); 9 } 10 11 @Override 12 public double calCost(UserRecords userRecords) { 13 double cost = 0; 14 cost += super.getChargeRules().get(0).calCost(userRecords.getCallingInCityRecords()); 15 cost += super.getChargeRules().get(1).calCost(userRecords.getCallingInProvinceRecords()); 16 cost += super.getChargeRules().get(2).calCost(userRecords.getCallingInLandRecords()); 17 return cost; 18 } 19 20 @Override 21 public double getMonthlyRent() { 22 return monthlyRent; 23 } 24 25 }
如源码第5-9行,当LandlinePhoneCharging类实例化时,程序将会将LandPhoneInCityRule等计费方式写入父类中的ArrayList<ChargeRule>;
如源码第12-18行,程序提取出userRecords中的通讯记录,并调用ArrayList<ChargeRule>中的计费方式对其进行资费计算并累加后传回。
标签:12,15,String,callRecord,public,面向对象,源码,each,strings 来源: https://www.cnblogs.com/3110321341chg/p/16380372.html