PTA第6~8题目集总结
PTA第6~8题目集总结
1.前言
相较于之前的多边形大作业,这三次pta作业较为简单。三次作业的套路相似,主要难点集中在理解电信计费系列题目的类图,并编写正确的正则表达式完成字符串的筛选,最后将筛选出的结果存储进对应的数组中,要计算结果时直接调用事先写好的程序遍历数组即可。大多数无法通过的情况都是程序设计时粗心导致的,例如截取字符串的初始位设置错误,或者正则表达式写错了等等。只要注意规避这些细微的错误,那么可以说这几次作业总体上写起来还是比较得心应手的。
2.程序设计分析
题目集六
7-1 电信计费系列1-座机计费
题目:
实现一个简单的电信计费程序:
假设南昌市电信分公司针对市内座机用户采用的计费方式:
月租20元,接电话免费,市内拨打电话0.1元/分钟,省内长途0.3元/分钟,国内长途拨打0.6元/分钟。不足一分钟按一分钟计。
南昌市的区号:0791,江西省内各地市区号包括:0790~0799以及0701。
输入格式:
输入信息包括两种类型
1、逐行输入南昌市用户开户的信息,每行一个用户,
格式:u-号码 计费类型 (计费类型包括:0-座机 1-手机实时计费 2-手机A套餐)
例如:u-079186300001 0
座机号码除区号外由是7-8位数字组成。
本题只考虑计费类型0-座机计费,电信系列2、3题会逐步增加计费类型。
2、逐行输入本月某些用户的通讯信息,通讯信息格式:
座机呼叫座机:t-主叫号码 接听号码 起始时间 结束时间
t-079186330022 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:11
以上四项内容之间以一个英文空格分隔,
时间必须符合"yyyy.MM.dd HH:mm:ss"格式。提示:使用SimpleDateFormat类。
以上两类信息,先输入所有开户信息,再输入所有通讯信息,最后一行以“end”结束。
注意:
本题非法输入只做格式非法的判断,不做内容是否合理的判断(时间除外,否则无法计算),比如:
1、输入的所有通讯信息均认为是同一个月的通讯信息,不做日期是否在同一个月还是多个月的判定,直接将通讯费用累加,因此月租只计算一次。
2、记录中如果同一电话号码的多条通话记录时间出现重合,这种情况也不做判断,直接 计算每条记录的费用并累加。
3、用户区号不为南昌市的区号也作为正常用户处理。
输出格式:
根据输入的详细通讯信息,计算所有已开户的用户的当月费用(精确到小数点后2位,
单位元)。假设每个用户初始余额是100元。
每条通讯信息单独计费后累加,不是将所有时间累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。
错误处理:
输入数据中出现的不符合格式要求的行一律忽略。
建议类图:
参见图1、2、3,可根据理解自行调整:
图一
图1中User是用户类,包括属性:
userRecords (用户记录)、balance(余额)、chargeMode(计费方式)、number(号码)。
ChargeMode是计费方式的抽象类:
chargeRules是计费方式所包含的各种计费规则的集合,ChargeRule类的定义见图3。
getMonthlyRent()方法用于返回月租(monthlyRent)。
UserRecords是用户记录类,保存用户各种通话、短信的记录,
各种计费规则将使用其中的部分或者全部记录。
其属性从上到下依次是:
市内拨打电话、省内(不含市内)拨打电话、省外拨打电话、
市内接听电话、省内(不含市内)接听电话、省外接听电话的记录
以及发送短信、接收短信的记录。
图二
图2中CommunicationRecord是抽象的通讯记录类:
包含callingNumber拨打号码、answerNumber接听号码两个属性。
CallRecord(通话记录)、MessageRecord(短信记录)是它的子类。
CallRecord(通话记录类)包含属性:
通话的起始、结束时间以及
拨号地点的区号(callingAddressAreaCode)、接听地点的区号(answerAddressAreaCode)。
区号用于记录在哪个地点拨打和接听的电话。座机无法移动,就是本机区号,如果是手机号,则会有差异。
图三
图3是计费规则的相关类,这些类的核心方法是:
calCost(ArrayList<CallRecord> callRecords)。
该方法针根据输入参数callRecords中的所有记录计算某用户的某一项费用;如市话费。
输入参数callRecords的约束条件:必须是某一个用户的符合计费规则要求的所有记录。
LandPhoneInCityRule、LandPhoneInProvinceRule、LandPhoneInLandRule三个类分别是
座机拨打市内、省内、省外电话的计费规则类,用于实现这三种情况的费用计算。
(提示:可以从UserRecords类中获取各种类型的callRecords)。
后续扩展说明:
后续题目集将增加手机用户,手机用户的计费方式中除了与座机计费类似的主叫通话费之外,还包含市外接听电话的漫游费以及发短信的费用。在本题的设计时可统一考虑。
通话记录中,手机需要额外记录拨打/接听的地点的区号,比如:
座机打手机:t-主叫号码 接听号码 接听地点区号 起始时间 结束时间
t-079186330022 13305862264 020 2022.1.3 10:00:25 2022.1.3 10:05:11
手机互打:t-主叫号码 拨号地点 接听号码 接听地点区号 起始时间 结束时间
t-18907910010 0791 13305862264 0371 2022.1.3 10:00:25 2022.1.3 10:05:11
短信的格式:m-主叫号码,接收号码,短信内容
m-18907910010 13305862264 welcome to jiangxi
m-13305862264 18907910010 thank you
输入样例:
在这里给出一组输入。例如:
u-079186300001 0
t-079186300001 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:25
end
输出样例:
在这里给出相应的输出。例如:
079186300001 3.0 77.0
源码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
public class Main {
public static void main(String[] args) throws ParseException {
Scanner sc=new Scanner(System.in);
String s;
s=sc.nextLine();
//创建用户数组
List<User> users=new ArrayList<>();
List<CallRecord> communicationRecords=new ArrayList<>();
while(!s.equals("end"))
{
if(s.matches("[u]-[0][7]([9][0-9]|[0][1])[0-9]{7,8} [0-3]")==true||s.matches("^t\\-0\\d{9,11}\\s0\\d{9,11}((\\s\\d{4}\\.([1-9]|([1]{1}[0-2]{1}))\\.([1-9]|([1-2]{1}[0-9]{1})|3[0-1])\\s(([0-1][0-9])|(2[0-3]))\\:([0-5][0-9])\\:([0-5][0-9])){2})")==true)
{
//创建用户
if(s.charAt(0)=='u')
{
User user = LineToUser(s);
int flag=0;
for (User user1 : users) {
if(user1.getNumber().equals(user.getNumber()))
{
flag=1;
break;
}
}
if(flag==0)
{
users.add(user);
}
}
//创建通话记录
else if(s.charAt(0)=='t')
{
communicationRecords.add(LineToCallRecord(s));
}
}
s=sc.nextLine();
}
List<User> temp=new ArrayList<>(users);
temp.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getNumber().compareTo(o2.getNumber());
}
});
for (User user : temp) {
List<CallRecord> temps=new ArrayList<>();
UserRecords userRecords=new UserRecords();
for (CallRecord communicationRecord : communicationRecords) {
if(communicationRecord.getCallingNumber().equals(user.getNumber()))
{
String answerNumber = communicationRecord.getAnswerNumber();
//市内
if(answerNumber.substring(0,4).equals("0791"))
{
userRecords.addCallingInCityRecords(communicationRecord);
}
//省内
else if((answerNumber.substring(0,3).equals("079")&&!answerNumber.substring(0,4).equals("0791"))||answerNumber.substring(0,4).equals("0701"))
{
userRecords.addCallingInProvinceRecords(communicationRecord);
}
// 国内
else
{
userRecords.addCallingInLandRecords(communicationRecord);
}
}
}
user.setUserRecords(userRecords);
System.out.println(user.getNumber()+" "+Double.parseDouble(String.format("%.2f",user.calCost()))+" "+Double.parseDouble(String.format("%.2f",user.calBalance())));
}
}
private static User LineToUser(String str){
str = str.substring(2);
String[] data = str.split(" ");
return new User(data[0],Integer.parseInt(data[1]));
}
private static CallRecord LineToCallRecord(String str) throws ParseException {
SimpleDateFormat format=new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
str = str.substring(2);
String[] s = str.split(" ");
CallRecord call=new CallRecord();
call.setCallingNumber(s[0]);
call.setAnswerNumber(s[1]);
String startTime=s[2]+" "+s[3];
String endTime=s[4]+" "+s[5];
Date date1=format.parse(startTime);
Date date2=format.parse(endTime);
call.setStartTime(date1);
call.setEndTime(date2);
return call;
}
}
class User{
public User(String s1, int s2)
{
this.number=s1;
if(s2==0)
{
this.chargeMode=new LandlinePhoneCharging();
}
}
private String number;
private ChargeMode chargeMode;
private double balance=100;
UserRecords userRecords=new UserRecords();
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public ChargeMode getChargeMode() {
return chargeMode;
}
public void setChargeMode(ChargeMode chargeMode) {
this.chargeMode = chargeMode;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public UserRecords getUserRecords() {
return userRecords;
}
public void setUserRecords(UserRecords userRecords) {
this.userRecords = userRecords;
}
public double calBalance()
{
ChargeMode chargeMode1=new LandlinePhoneCharging();
double monthlyRent = chargeMode1.getMonthlyRent();
return this.balance-calCost()-monthlyRent;
}
public double calCost()
{
ChargeMode chargeMode1=new LandlinePhoneCharging();
double cost = chargeMode1.CalCost(getUserRecords());
return cost;
}
}
class UserRecords{
private ArrayList<CallRecord> callingInCityRecords=new ArrayList<>();
private ArrayList<CallRecord> callingInProvinceRecords=new ArrayList<>();
private ArrayList<CallRecord> callingInLandRecords=new ArrayList<>();
private ArrayList<CallRecord> answerInCityRecords=new ArrayList<>();
private ArrayList<CallRecord> answerInProvinceRecords=new ArrayList<>();
private ArrayList<CallRecord> answerInLandRecords=new ArrayList<>();
private ArrayList<MessageRecord> sendMessageRecords=new ArrayList<>();
private ArrayList<MessageRecord> receiveMessageRecords=new ArrayList<>();
public ArrayList<CallRecord> getCallingInCityRecords() {
return callingInCityRecords;
}
public ArrayList<CallRecord> getCallingInProvinceRecords() {
return callingInProvinceRecords;
}
public void addCallingInCityRecords(CallRecord Records) {
callingInCityRecords.add(Records);
}
public void addCallingInProvinceRecords(CallRecord Records) {
callingInProvinceRecords.add(Records);
}
public void addCallingInLandRecords(CallRecord Records) {
callingInLandRecords.add(Records);
}
public ArrayList<CallRecord> getCallingInLandRecords() {
return callingInLandRecords;
}
public ArrayList<CallRecord> getAnswerInCityRecords() {
return answerInCityRecords;
}
public void addAnswerInCityRecords(ArrayList<CallRecord> answerInCityRecords) {
this.answerInCityRecords = answerInCityRecords;
}
public ArrayList<CallRecord> getAnswerInProvinceRecords() {
return answerInProvinceRecords;
}
public void addAnswerInProvinceRecords(ArrayList<CallRecord> answerInProvinceRecords) {
this.answerInProvinceRecords = answerInProvinceRecords;
}
public ArrayList<CallRecord> getAnswerInLandRecords() {
return answerInLandRecords;
}
public void addAnswerInLandRecords(ArrayList<CallRecord> answerInLandRecords) {
this.answerInLandRecords = answerInLandRecords;
}
public ArrayList<MessageRecord> getSendMessageRecords() {
return sendMessageRecords;
}
public void addSendMessageRecords(ArrayList<MessageRecord> sendMessageRecords) {
this.sendMessageRecords = sendMessageRecords;
}
public ArrayList<MessageRecord> getReceiveMessageRecords() {
return receiveMessageRecords;
}
public void addReceiveMessageRecords(ArrayList<MessageRecord> receiveMessageRecords) {
this.receiveMessageRecords = receiveMessageRecords;
}
}
abstract class ChargeMode{
private ArrayList<ChargeRule> chargeRules=new ArrayList<>();
public ArrayList<ChargeRule> getChargeRules() {
return chargeRules;
}
public void setChargeRules(ArrayList<ChargeRule> chargeRules) {
this.chargeRules = chargeRules;
}
public abstract double CalCost(UserRecords userRecords);
public abstract double getMonthlyRent();
}
class LandlinePhoneCharging extends ChargeMode{
double MonthlyRent=20;
@Override
public double CalCost(UserRecords userRecords) {
ChargeRule InCity=new LandPhoneInCityRule();
double InCityCost = InCity.calCost(userRecords.getCallingInCityRecords());
ChargeRule InLand=new LandPhoneInlandRule();
double InLandCost = InLand.calCost(userRecords.getCallingInLandRecords());
ChargeRule InProvince=new LandPhoneInProvince();
double InProvinceCost = InProvince.calCost(userRecords.getCallingInProvinceRecords());
return InCityCost+InProvinceCost+InLandCost;
}
@Override
public double getMonthlyRent() {
return MonthlyRent;
}
}
abstract class CommunicationRecord{
private String callingNumber;
private String answerNumber;
public String getCallingNumber() {
return callingNumber;
}
public void setCallingNumber(String callingNumber) {
this.callingNumber = callingNumber;
}
public String getAnswerNumber() {
return answerNumber;
}
public void setAnswerNumber(String answerNumber) {
this.answerNumber = answerNumber;
}
}
class MessageRecord extends CommunicationRecord{
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
class CallRecord extends CommunicationRecord{
private Date startTime;
private Date endTime;
private String callingAddressAreaCode;
private String answerAddressAreaCode;
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public String getCallingAddressAreaCode() {
return callingAddressAreaCode;
}
public void setCallingAddressAreaCode(String callingAddressAreaCode) {
this.callingAddressAreaCode = callingAddressAreaCode;
}
public String getAnswerAddressAreaCode() {
return answerAddressAreaCode;
}
public void setAnswerAddressAreaCode(String answerAddressAreaCode) {
this.answerAddressAreaCode = answerAddressAreaCode;
}
}
abstract class ChargeRule {
public abstract double calCost(ArrayList<CallRecord> callRecords);
}
abstract class CallChargeRule extends ChargeRule{
}
class LandPhoneInCityRule extends CallChargeRule{
@Override
public double calCost(ArrayList<CallRecord> callRecords) {
double sum=0;
for (CallRecord callRecord : callRecords) {
Date endTime = callRecord.getEndTime();
Date startTime = callRecord.getStartTime();
double f1,f2;
f2=endTime.getTime();
f1=startTime.getTime();
double res = (f2 - f1) / 1000 / 60;
int res1=(int) res;
if(res-res1!=0)
{
res1=res1+1;
}
sum+=0.1*res1;
}
return sum;
}
}
class LandPhoneInProvince extends CallChargeRule{
@Override
public double calCost(ArrayList<CallRecord> callRecords) {
double sum=0;
for (CallRecord callRecord : callRecords) {
Date endTime = callRecord.getEndTime();
Date startTime = callRecord.getStartTime();
double f1,f2;
f2=endTime.getTime();
f1=startTime.getTime();
double res = (f2 - f1) / 1000 / 60;
int res1=(int) res;
if(res-res1!=0)
{
res1=res1+1;
}
sum+=0.3*res1;
}
return sum;
}
}
class LandPhoneInlandRule extends CallChargeRule{
@Override
public double calCost(ArrayList<CallRecord> callRecords) {
double sum=0;
for (CallRecord callRecord : callRecords) {
Date endTime = callRecord.getEndTime();
Date startTime = callRecord.getStartTime();
double f1,f2;
f2=endTime.getTime();
f1=startTime.getTime();
double res = (f2 - f1) / 1000 / 60;
int res1=(int) res;
if(res-res1!=0)
{
res1=res1+1;
}
sum+=0.6*res1;
}
return sum;
}
}
类图如下:
SourceMonitor分析图如下:
代码分析:
一、分析类图可得:
1.我根据题目给的类图搭建起了题目的框架,其中主要的类有User类,UserRecord类,这两个类分别存储用户的基本信息以及用户拨打与接听的记录,方便程序记录拨打电话的区号以及通话时长等等,其中UserRecord类作为User的属性存在,因此只需要获取不同注册号码对应的User对象,就可以查询到该用户所有的拨号与接听记录,就可以很方便的进行计费操作了。
2.在上面的基础上,我创建了ChargeRule抽象类,主要用于实现不同拨号主体的处理方式,例如电话拨打,手机接听,手机拨打手机接听等等,由于本题只涉及到电话与电话之间的通信,因此实现类主要是电话的市内处理方式LandPhoneInCity类,省内处理方式LandPhoneInPorvince类和国内处理方式LandPhoneInLand类,根据拨打者的区号判断需要采用哪种计费方式。
3.在main方法中,我使用了循环+正则表达式的方式获取有效信息,如果是注册用户操作,就将得到的字符串处理后,创建新的User对象并加入存储表users中,否则如果是通话记录,就创建相应的communicationRecord对象并存储表communicationRecords中,最后排序并遍历users表,得到user的电话号码,再嵌套遍历communicationRecords表,找到拨打号码与上面电话号码一致的记录,根据被拨打号码的电话所在区号,将这条通话记录存入UserRecord所对应的表中。通过这样的操作,可以实现每个通话记录与通话主体一一对应,最后将UserRecord加入对应的user中,完成该用户的信息录入...最后遍历users表,调用事先写好的CalCost()方法,即可得到最后每个用户的计费结果。
二、分析SourceMonitor图可得
1.除了最大复杂度超出预期外,该代码的大部分数值都控制在期望范围内,总体来说环复杂度较低。
2.本代码严格遵守题目提供类图编写,无论是最大深度还是平均深度都处在期望值,代码结构较为合理。
3.本代码采用了较多多态的设计理念,有效降低了代码量,提升了代码的复用程度,这一点也能从较低的Method/Class值中看出,总体来说代码质量较佳。
题目集七
7-1 电信计费系列2-手机+座机计费
题目:
实现南昌市电信分公司的计费程序,假设该公司针对手机和座机用户分别采取了两种计费方案,分别如下:
1、针对市内座机用户采用的计费方式(与电信计费系列1内容相同):
月租20元,接电话免费,市内拨打电话0.1元/分钟,省内长途0.3元/分钟,国内长途拨打0.6元/分钟。不足一分钟按一分钟计。
假设本市的区号:0791,江西省内各地市区号包括:0790~0799以及0701。
2、针对手机用户采用实时计费方式:
月租15元,市内省内接电话均免费,市内拨打市内电话0.1元/分钟,市内拨打省内电话0.2元/分钟,市内拨打省外电话0.3元/分钟,省内漫游打电话0.3元/分钟,省外漫游接听0.3元/分钟,省外漫游拨打0.6元/分钟;
注:被叫电话属于市内、省内还是国内由被叫电话的接听地点区号决定,比如以下案例中,南昌市手机用户13307912264在区号为020的广州接听了电话,主叫号码应被计算为拨打了一个省外长途,同时,手机用户13307912264也要被计算省外接听漫游费:
u-13307912264 1
t-079186330022 13307912264 020 2022.1.3 10:00:25 2022.1.3 10:05:11
输入:
输入信息包括两种类型
1、逐行输入南昌市用户开户的信息,每行一个用户,含手机和座机用户
格式:u-号码 计费类型 (计费类型包括:0-座机 1-手机实时计费 2-手机A套餐)
例如:u-079186300001 0
座机号码由区号和电话号码拼接而成,电话号码包含7-8位数字,区号最高位是0。
手机号码由11位数字构成,最高位是1。
本题在电信计费系列1基础上增加类型1-手机实时计费。
手机设置0或者座机设置成1,此种错误可不做判断。
2、逐行输入本月某些用户的通讯信息,通讯信息格式:
座机呼叫座机:t-主叫号码 接听号码 起始时间 结束时间
t-079186330022 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:11
以上四项内容之间以一个英文空格分隔,
时间必须符合"yyyy.MM.dd HH:mm:ss"格式。提示:使用SimpleDateFormat类。
输入格式增加手机接打电话以及收发短信的格式,手机接打电话的信息除了号码之外需要额外记录拨打/接听的地点的区号,比如:
座机打手机:
t-主叫号码 接听号码 接听地点区号 起始时间 结束时间
t-079186330022 13305862264 020 2022.1.3 10:00:25 2022.1.3 10:05:11
手机互打:
t-主叫号码 拨号地点 接听号码 接听地点区号 起始时间 结束时间
t-18907910010 0791 13305862264 0371 2022.1.3 10:00:25 2022.1.3 10:05:11
注意:以上两类信息,先输入所有开户信息,再输入所有通讯信息,最后一行以“end”结束。
输出:
根据输入的详细通讯信息,计算所有已开户的用户的当月费用(精确到小数点后2位,单位元)。假设每个用户初始余额是100元。
每条通讯、短信信息均单独计费后累加,不是将所有信息累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。
错误处理:
输入数据中出现的不符合格式要求的行一律忽略。
本题只做格式的错误判断,无需做内容上不合理的判断,比如同一个电话两条通讯记录的时间有重合、开户号码非南昌市的号码等,此类情况都当成正确的输入计算。但时间的输入必须符合要求,比如不能输入2022.13.61 28:72:65。
建议类图:
参见图1、2、3:
图一
图1中User是用户类,包括属性:
userRecords (用户记录)、balance(余额)、chargeMode(计费方式)、number(号码)。
ChargeMode是计费方式的抽象类:
chargeRules是计费方式所包含的各种计费规则的集合,ChargeRule类的定义见图3。
getMonthlyRent()方法用于返回月租(monthlyRent)。
UserRecords是用户记录类,保存用户各种通话、短信的记录,
各种计费规则将使用其中的部分或者全部记录。
其属性从上到下依次是:
市内拨打电话、省内(不含市内)拨打电话、省外拨打电话、
市内接听电话、省内(不含市内)接听电话、省外接听电话的记录
以及发送短信、接收短信的记录。
图二
图2中CommunicationRecord是抽象的通讯记录类:
包含callingNumber拨打号码、answerNumber接听号码两个属性。
CallRecord(通话记录)、MessageRecord(短信记录)是它的子类。CallRecord(通话记录类)包含属性:
通话的起始、结束时间以及
拨号地点的区号(callingAddressAreaCode)、接听地点的区号(answerAddressAreaCode)。
区号用于记录在哪个地点拨打和接听的电话。座机无法移动,就是本机区号,如果是手机号,则会有差异。
图三
图3是计费规则的相关类,这些类的核心方法是:
calCost(ArrayList<CallRecord> callRecords)。
该方法针根据输入参数callRecords中的所有记录计算某用户的某一项费用;如市话费。
输入参数callRecords的约束条件:必须是某一个用户的符合计费规则要求的所有记录。
SendMessageRule是发送短信的计费规则类,用于计算发送短信的费用。
LandPhoneInCityRule、LandPhoneInProvinceRule、LandPhoneInLandRule三个类分别是座机拨打市内、省内、省外电话的计费规则类,用于实现这三种情况的费用计算。
(提示:可以从UserRecords类中获取各种类型的callRecords)。
注意:以上图中所定义的类不是限定要求,根据实际需要自行补充或修改。
输入样例:
在这里给出一组输入。例如:
u-13811111111 1
t-13811111111 0791 13811111110 020 2022.1.3 08:00:00 2022.1.3 08:09:20
end
输出样例:
在这里给出相应的输出。例如:
13811111111 3.0 82.0
源码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
public class Main {
public static void main(String[] args) throws ParseException {
Scanner sc=new Scanner(System.in);
String s;
s=sc.nextLine();
//创建用户数组
List<User> users=new ArrayList<>();
List<CallRecord> communicationRecords=new ArrayList<>();
while(!s.equals("end"))
{
if(
s.matches("[u]-([0][7]([9][0-9]|[0][1])[0-9]{7,8}) [0-3]")
||s.matches("[u]-[1][0-9]{10} [0-3]")
||s.matches("(([t]-0791[0-9]{7,8}\\s0[0-9]{9,11})|([t]-0791[0-9]{7,8}\\s1[0-9]{10}\\s0[0-9]{2,3})|([t]-1[0-9]{10}\\s0[0-9]{2,3}\\s0[0-9]{9,11})|([t]-1[0-9]{10}\\s0[0-9]{2,3}\\s1[0-9]{10}\\s0[0-9]{2,3}))(\\s\\d{4}\\.([1-9]|([1]{1}[0-2]{1}))\\.([1-9]|([1-2]{1}[0-9]{1})|3[0-1])\\s(([0-1][0-9])|(2[0-3]))\\:([0-5][0-9])\\:([0-5][0-9])){2}")==true)
{
//创建用户
if(s.charAt(0)=='u')
{
User user = LineToUser(s);
int flag=0;
for (User user1 : users) {
if(user1.getNumber().equals(user.getNumber()))
{
flag=1;
break;
}
}
if(flag==0)
{
users.add(user);
}
}
//创建通话记录
else if(s.charAt(0)=='t')
{
communicationRecords.add(LineToCallRecord(s));
}
else if(s.charAt(0)=='m') {
}
}
s=sc.nextLine();
}
List<User> temp=new ArrayList<>(users);
temp.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getNumber().compareTo(o2.getNumber());
}
});
for (User user : temp) {
UserRecords userRecords=new UserRecords();
for (CallRecord communicationRecord : communicationRecords) {
// 遍历到拨打者
if (communicationRecord.getCallingNumber().equals(user.getNumber())) {
//市内
if (communicationRecord.gettype().matches("^1[1-3]$")) {
userRecords.addCallingInCityRecords(communicationRecord);
} else if (communicationRecord.gettype().matches("^2[1-3]$")) {
userRecords.addCallingInProvinceRecords(communicationRecord);
} else {
userRecords.addCallingInLandRecords(communicationRecord);
}
}
}
for (CallRecord communicationRecord : communicationRecords) {
//遍历到接收者
if(communicationRecord.getAnswerNumber().equals(user.getNumber()))
{
//市内
if(communicationRecord.gettype().matches("^[1-3]1$"))
{
userRecords.addAnswerInCityRecords(communicationRecord);
}
else if(communicationRecord.gettype().matches("^[1-3]2$"))
{
userRecords.addAnswerInProvinceRecords(communicationRecord);
}
else {
userRecords.addAnswerInLandRecords(communicationRecord);
}
}
}
user.setUserRecords(userRecords);
System.out.println(user.getNumber()+" "+Double.parseDouble(String.format("%.2f",user.calCost()))+" "+Double.parseDouble(String.format("%.2f",user.calBalance())));
}
}
private static User LineToUser(String str){
str = str.substring(2);
String[] data = str.split(" ");
return new User(data[0],Integer.parseInt(data[1]));
}
private static CallRecord LineToCallRecord(String str) throws ParseException {
SimpleDateFormat format=new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
str = str.substring(2);
String[] s = str.split(" ");
CallRecord call=new CallRecord();
if(s.length==6)
{
call.setCallingNumber(s[0]);
call.setAnswerNumber(s[1]);
String startTime=s[2]+" "+s[3];
String endTime=s[4]+" "+s[5];
Date date1=format.parse(startTime);
Date date2=format.parse(endTime);
call.setStartTime(date1);
call.setEndTime(date2);
call.setCallingAddressAreaCode(s[0].substring(0,4));
call.setAnswerAddressAreaCode(s[1].substring(0,4));
}
if(s.length==7)
{
if(s[0].charAt(0)!='0')
{
call.setCallingNumber(s[0]);
call.setAnswerNumber(s[2]);
call.setCallingAddressAreaCode(s[1]);
call.setAnswerAddressAreaCode(s[2].substring(0,4));
String startTime=s[3]+" "+s[4];
String endTime=s[5]+" "+s[6];
Date date1=format.parse(startTime);
Date date2=format.parse(endTime);
call.setStartTime(date1);
call.setEndTime(date2);
}
else {
call.setCallingNumber(s[0]);
call.setAnswerNumber(s[1]);
call.setCallingAddressAreaCode(s[0].substring(0,4));
call.setAnswerAddressAreaCode(s[2]);
String startTime=s[3]+" "+s[4];
String endTime=s[5]+" "+s[6];
Date date1=format.parse(startTime);
Date date2=format.parse(endTime);
call.setStartTime(date1);
call.setEndTime(date2);
}
}
else if(s.length==8)
{
call.setCallingNumber(s[0]);
call.setCallingAddressAreaCode(s[1]);
call.setAnswerNumber(s[2]);
call.setAnswerAddressAreaCode(s[3]);
String startTime=s[4]+" "+s[5];
String endTime=s[6]+" "+s[7];
Date date1=format.parse(startTime);
Date date2=format.parse(endTime);
call.setStartTime(date1);
call.setEndTime(date2);
}
return call;
}
}
class User{
@Override
public String toString() {
return "User{" +
"number='" + number + '\'' +
", chargeMode=" + chargeMode +
", balance=" + balance +
", userRecords=" + userRecords +
'}';
}
public User(String s1, int s2)
{
this.number=s1;
if(s2==0)
{
this.chargeMode=new LandlinePhoneCharging();
} else if (s2 == 1) {
this.chargeMode=new MobilePhoneCharging();
}
}
private String number;
private ChargeMode chargeMode;
private double balance=100;
UserRecords userRecords=new UserRecords();
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public ChargeMode getChargeMode() {
return chargeMode;
}
public void setChargeMode(ChargeMode chargeMode) {
this.chargeMode = chargeMode;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public UserRecords getUserRecords() {
return userRecords;
}
public void setUserRecords(UserRecords userRecords) {
this.userRecords = userRecords;
}
public double calBalance()
{
double monthlyRent = chargeMode.getMonthlyRent();
return this.balance-calCost()-monthlyRent;
}
public double calCost()
{
double cost = chargeMode.CalCost(getUserRecords());
return cost;
}
}
class UserRecords{
private ArrayList<CallRecord> callingInCityRecords=new ArrayList<>();
private ArrayList<CallRecord> callingInProvinceRecords=new ArrayList<>();
private ArrayList<CallRecord> callingInLandRecords=new ArrayList<>();
private ArrayList<CallRecord> answerInCityRecords=new ArrayList<>();
private ArrayList<CallRecord> answerInProvinceRecords=new ArrayList<>();
private ArrayList<CallRecord> answerInLandRecords=new ArrayList<>();
private ArrayList<MessageRecord> sendMessageRecords=new ArrayList<>();
private ArrayList<MessageRecord> receiveMessageRecords=new ArrayList<>();
public ArrayList<CallRecord> getCallingInCityRecords() {
return callingInCityRecords;
}
public ArrayList<CallRecord> getCallingInProvinceRecords() {
return callingInProvinceRecords;
}
public void addCallingInCityRecords(CallRecord Records) {
callingInCityRecords.add(Records);
}
public void addCallingInProvinceRecords(CallRecord Records) {
callingInProvinceRecords.add(Records);
}
public void addCallingInLandRecords(CallRecord Records) {
callingInLandRecords.add(Records);
}
public ArrayList<CallRecord> getCallingInLandRecords() {
return callingInLandRecords;
}
public ArrayList<CallRecord> getAnswerInCityRecords() {
return answerInCityRecords;
}
public void addAnswerInCityRecords(CallRecord Records) {
this.answerInCityRecords.add(Records);
}
public ArrayList<CallRecord> getAnswerInProvinceRecords() {
return answerInProvinceRecords;
}
public void addAnswerInProvinceRecords(CallRecord Records) {
this.answerInProvinceRecords.add(Records);
}
public ArrayList<CallRecord> getAnswerInLandRecords() {
return answerInLandRecords;
}
public void addAnswerInLandRecords(CallRecord Records) {
this.answerInLandRecords.add(Records);
}
public ArrayList<MessageRecord> getSendMessageRecords() {
return sendMessageRecords;
}
public void addSendMessageRecords(ArrayList<MessageRecord> sendMessageRecords) {
this.sendMessageRecords = sendMessageRecords;
}
public ArrayList<MessageRecord> getReceiveMessageRecords() {
return receiveMessageRecords;
}
public void addReceiveMessageRecords(ArrayList<MessageRecord> receiveMessageRecords) {
this.receiveMessageRecords = receiveMessageRecords;
}
}
abstract class ChargeMode{
private ArrayList<ChargeRule> chargeRules=new ArrayList<>();
public ArrayList<ChargeRule> getChargeRules() {
return chargeRules;
}
public void setChargeRules(ArrayList<ChargeRule> chargeRules) {
this.chargeRules = chargeRules;
}
public abstract double CalCost(UserRecords userRecords);
public abstract double getMonthlyRent();
}
class LandlinePhoneCharging extends ChargeMode{
double MonthlyRent=20;
@Override
public double CalCost(UserRecords userRecords) {
ChargeRule InCity=new LandPhoneInCityRule();
double InCityCost = InCity.calCost(userRecords);
ChargeRule InLand=new LandPhoneInlandRule();
double InLandCost = InLand.calCost(userRecords);
ChargeRule InProvince=new LandPhoneInProvince();
double InProvinceCost = InProvince.calCost(userRecords);
return InCityCost+InProvinceCost+InLandCost;
}
@Override
public double getMonthlyRent() {
return MonthlyRent;
}
}
class MobilePhoneCharging extends ChargeMode{
double MonthlyRent=15;
@Override
public double CalCost(UserRecords userRecords) {
//打出去计费+接听计费
ChargeRule InCity=new MobilePhoneInCityRule();
double InCityCost = InCity.calCost(userRecords);
ChargeRule InProvince=new MobilePhoneInProvince();
double InProvinceCost = InProvince.calCost(userRecords);
ChargeRule InLand=new MobilePhoneInlandRule();
double InLandCost = InLand.calCost(userRecords);
return InCityCost+InProvinceCost+InLandCost;
}
@Override
public double getMonthlyRent() {
return MonthlyRent;
}
}
abstract class CommunicationRecord{
private String callingNumber;
private String answerNumber;
public String getCallingNumber() {
return callingNumber;
}
public void setCallingNumber(String callingNumber) {
this.callingNumber = callingNumber;
}
public String getAnswerNumber() {
return answerNumber;
}
public void setAnswerNumber(String answerNumber) {
this.answerNumber = answerNumber;
}
}
class MessageRecord extends CommunicationRecord{
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
class CallRecord extends CommunicationRecord{
@Override
public String toString() {
return "CallRecord{" +
"startTime=" + startTime +
", endTime=" + endTime +
", callingAddressAreaCode='" + callingAddressAreaCode + '\'' +
", answerAddressAreaCode='" + answerAddressAreaCode + '\'' +
'}';
}
private Date startTime;
private Date endTime;
private String callingAddressAreaCode;
private String answerAddressAreaCode;
public String gettype()
{
String type="";
if(callingAddressAreaCode.equals("0791"))
{
type=type.concat("1");
} else if ((callingAddressAreaCode.substring(0,3).equals("079")&&!callingAddressAreaCode.equals("0791"))||callingAddressAreaCode.equals("0701")) {
type= type.concat("2");
}else {
type= type.concat("3");
}
if(answerAddressAreaCode.equals("0791"))
{
type=type.concat("1");
} else if ((answerAddressAreaCode.substring(0,3).equals("079")&&!answerAddressAreaCode.equals("0791"))||answerAddressAreaCode.equals("0701")) {
type= type.concat("2");
}else {
type=type.concat("3");
}
return type;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public String getCallingAddressAreaCode() {
return callingAddressAreaCode;
}
public void setCallingAddressAreaCode(String callingAddressAreaCode) {
this.callingAddressAreaCode = callingAddressAreaCode;
}
public String getAnswerAddressAreaCode() {
return answerAddressAreaCode;
}
public void setAnswerAddressAreaCode(String answerAddressAreaCode) {
this.answerAddressAreaCode = answerAddressAreaCode;
}
}
abstract class ChargeRule {
public abstract double calCost(UserRecords records);
}
abstract class CallChargeRule extends ChargeRule{
}
class LandPhoneInCityRule extends CallChargeRule{
@Override
public double calCost(UserRecords records) {
double sum=0;
for (CallRecord callRecord :records.getCallingInCityRecords()) {
Date endTime = callRecord.getEndTime();
Date startTime = callRecord.getStartTime();
double f1,f2;
f2=endTime.getTime();
f1=startTime.getTime();
double res = (f2 - f1) / 1000 / 60;
int res1=(int) res;
if(res-res1!=0)
{
res1=res1+1;
}
if(callRecord.gettype().equals("11"))
{
sum+=0.1*res1;
} else if (callRecord.gettype().equals("12")) {
sum+=0.3*res1;
}
else {
sum+=0.6*res1;
}
}
return sum;
}
}
class LandPhoneInProvince extends CallChargeRule{
@Override
public double calCost(UserRecords records) {
double sum=0;
for (CallRecord callRecord :records.getCallingInProvinceRecords()) {
Date endTime = callRecord.getEndTime();
Date startTime = callRecord.getStartTime();
double f1,f2;
f2=endTime.getTime();
f1=startTime.getTime();
double res = (f2 - f1) / 1000 / 60;
int res1=(int) res;
if(res-res1!=0)
{
res1=res1+1;
}
sum+=0.3*res1;
}
return sum;
}
}
class LandPhoneInlandRule extends CallChargeRule{
@Override
public double calCost(UserRecords records) {
double sum=0;
for (CallRecord callRecord :records.getCallingInLandRecords()) {
Date endTime = callRecord.getEndTime();
Date startTime = callRecord.getStartTime();
double f1,f2;
f2=endTime.getTime();
f1=startTime.getTime();
double res = (f2 - f1) / 1000 / 60;
int res1=(int) res;
if(res-res1!=0)
{
res1=res1+1;
}
sum+=0.6*res1;
}
return sum;
}
}
class MobilePhoneInCityRule extends CallChargeRule{
@Override
public double calCost(UserRecords records) {
double sum=0;
for (CallRecord callRecord :records.getCallingInCityRecords()) {
Date endTime = callRecord.getEndTime();
Date startTime = callRecord.getStartTime();
double f1,f2;
f2=endTime.getTime();
f1=startTime.getTime();
double res = (f2 - f1) / 1000 / 60;
int res1=(int) res;
if(res-res1!=0)
{
res1=res1+1;
}
if(callRecord.gettype().equals("11"))
{
sum+=0.1*res1;
} else if (callRecord.gettype().equals("12")) {
sum+=0.2*res1;
}
else {
sum+=0.3*res1;
}
}
return sum;
}
}
class MobilePhoneInProvince extends CallChargeRule{
@Override
public double calCost(UserRecords records) {
double sum=0;
for (CallRecord callRecord : records.getCallingInProvinceRecords()) {
Date endTime = callRecord.getEndTime();
Date startTime = callRecord.getStartTime();
double f1,f2;
f2=endTime.getTime();
f1=startTime.getTime();
double res = (f2 - f1) / 1000 / 60;
int res1=(int) res;
if(res-res1!=0)
{
res1=res1+1;
}
sum+=0.3*res1;
}
return sum;
}
}
class MobilePhoneInlandRule extends CallChargeRule{
@Override
public double calCost(UserRecords records) {
double sum=0;
for (CallRecord callRecord : records.getCallingInLandRecords()) {
Date endTime = callRecord.getEndTime();
Date startTime = callRecord.getStartTime();
double f1,f2;
f2=endTime.getTime();
f1=startTime.getTime();
double res = (f2 - f1) / 1000 / 60;
int res1=(int) res;
if(res-res1!=0)
{
res1=res1+1;
}
sum+=0.6*res1;
}
for (CallRecord answer : records.getAnswerInLandRecords()) {
Date endTime =answer.getEndTime();
Date startTime =answer.getStartTime();
double f1,f2;
f2=endTime.getTime();
f1=startTime.getTime();
double res = (f2 - f1) / 1000 / 60;
int res1=(int) res;
if(res-res1!=0)
{
res1=res1+1;
}
sum+=0.3*res1;
}
return sum;
}
}
类图如下:
SourceMonitor分析图如下:
代码分析:
一、分析类图可得
1.相对于上一题只涉及固定电话,本题新增加了手机通话的模块,因此需要在原先代码的基础上增加较多模块,例如市内手机拨打电话计费模块MobilePhoneInCity类,省内手机拨打漫游计费模块MobilePhoneInProvince类,国内手机拨打漫游模块MobilePhoneInLand类来分别统计不同区域拨打手机的计费。
2.同时根据题目要求,国内漫游手机接听也要单独计费,因此在UserRecord中增加了手机接听计费数组AnswerInLandRecords,在main方法中增加一次循环遍历,在对一个号码统计完拨打记录时还需要统计其在国内漫游的接听记录,最后在手机用户类的CalCost()方法中要额外将国内漫游接听的费用计算并加入结果中,这样才能基本满足题目要求。
3.在设计正则表达式时要额外增加手机用户判断,因此在原来的基础上至少要多出6种情况,具体的代码可以参考main方法。
二、分析SourceMonitor分析图可得
1.除了极个别属性例如最大复杂度没能降到理想范围内之外,本代码各项基本属性都处于正常值,代码可复用性较高。
2.本代码沿用了上一题的基本框架,采用了抽象+多态+继承机制,极大减少了代码冗余度,因此Method/Class值远远低于SourceMonitor分析的上限,代码质量较为可观。
3.平均深度与最大深度接近上限值,说明代码整体上符合质量要求,但仍有优化空间。
题目集八
7-1 电信计费系列3-短信计费
题目:
实现一个简单的电信计费程序,针对手机的短信采用如下计费方式:
1、接收短信免费,发送短信0.1元/条,超过3条0.2元/条,超过5条0.3元/条。
2、如果一次发送短信的字符数量超过10个,按每10个字符一条短信进行计算。
输入:
输入信息包括两种类型
1、逐行输入南昌市手机用户开户的信息,每行一个用户。
格式:u-号码 计费类型 (计费类型包括:0-座机 1-手机实时计费 2-手机A套餐 3-手机短信计费)
例如:u-13305862264 3
座机号码由区号和电话号码拼接而成,电话号码包含7-8位数字,区号最高位是0。
手机号码由11位数字构成,最高位是1。
本题只针对类型3-手机短信计费。
2、逐行输入本月某些用户的短信信息,短信的格式:
m-主叫号码,接收号码,短信内容 (短信内容只能由数字、字母、空格、英文逗号、英文句号组成)
m-18907910010 13305862264 welcome to jiangxi.
m-13305862264 18907910010 thank you.
注意:以上两类信息,先输入所有开户信息,再输入所有通讯信息,最后一行以“end”结束。
输出:
根据输入的详细短信信息,计算所有已开户的用户的当月短信费用(精确到小数点后2位,单位元)。假设每个用户初始余额是100元。
每条短信信息均单独计费后累加,不是将所有信息累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。
错误处理:
输入数据中出现的不符合格式要求的行一律忽略。
本题只做格式的错误判断,无需做内容上不合理的判断,比如同一个电话两条通讯记录的时间有重合、开户号码非南昌市的号码、自己给自己打电话等,此类情况都当成正确的输入计算。但时间的输入必须符合要求,比如不能输入2022.13.61 28:72:65。
本题只考虑短信计费,不考虑通信费用以及月租费。
建议类图:
参见图1、2、3:
图一
图1中User是用户类,包括属性:
userRecords (用户记录)、balance(余额)、chargeMode(计费方式)、number(号码)。
ChargeMode是计费方式的抽象类:
chargeRules是计费方式所包含的各种计费规则的集合,ChargeRule类的定义见图3。
getMonthlyRent()方法用于返回月租(monthlyRent)。
UserRecords是用户记录类,保存用户各种通话、短信的记录,
各种计费规则将使用其中的部分或者全部记录。
其属性从上到下依次是:
市内拨打电话、省内(不含市内)拨打电话、省外拨打电话、
市内接听电话、省内(不含市内)接听电话、省外接听电话的记录
以及发送短信、接收短信的记录。
图二
图2中CommunicationRecord是抽象的通讯记录类:
包含callingNumber拨打号码、answerNumber接听号码两个属性。
CallRecord(通话记录)、MessageRecord(短信记录)是它的子类。
图三
图3是计费规则的相关类,这些类的核心方法是:
calCost(ArrayList callRecords)。
该方法针根据输入参数callRecords中的所有记录计算某用户的某一项费用;如市话费。
输入参数callRecords的约束条件:必须是某一个用户的符合计费规则要求的所有记录。
SendMessageRule是发送短信的计费规则类,用于计算发送短信的费用。
LandPhoneInCityRule、LandPhoneInProvinceRule、LandPhoneInLandRule三个类分别是座机拨打市内、省内、省外电话的计费规则类,用于实现这三种情况的费用计算。
(提示:可以从UserRecords类中获取各种类型的callRecords)。
注意:以上图中所定义的类不是限定要求,根据实际需要自行补充或修改。
输入样例:
在这里给出一组输入。例如:
u-18907910010 3
m-18907910010 13305862264 aaaaaaaaaaaaaaaaaaaaaaa
end
输出样例:
在这里给出相应的输出。例如:
18907910010 0.3 99.7
输入样例1:
在这里给出一组输入。例如:
u-18907910010 3
m-18907910010 13305862264 aaaaaaaaaaaa
m-18907910010 13305862264 aaaaaaa.
m-18907910010 13305862264 bb,bbbb
end
输出样例1:
在这里给出相应的输出。例如:
18907910010 0.5 99.5
源码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
public class Main {
public static void main(String[] args) throws ParseException {
Scanner sc=new Scanner(System.in);
String s;
s=sc.nextLine();
//创建用户数组
List<User> users=new ArrayList<>();
List<MessageRecord> communicationRecords=new ArrayList<>();
while(!s.equals("end"))
{
if(s.matches("[u]-[1][0-9]{10} [0-3]") ||s.matches("m-1\\d{10}\\s1\\d{10}[ 0-9a-z,.A-Z]+")==true)
{
//创建用户
if(s.charAt(0)=='u')
{
User user = LineToUser(s);
int flag=0;
for (User user1 : users) {
if(user1.getNumber().equals(user.getNumber()))
{
flag=1;
break;
}
}
if(flag==0)
{
users.add(user);
}
}
//创建通话记录
if(s.charAt(0)=='m') {
MessageRecord messageRecord = LineToMessageRecord(s);
communicationRecords.add(messageRecord);
}
}
s=sc.nextLine();
}
List<User> temp=new ArrayList<>(users);
temp.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getNumber().compareTo(o2.getNumber());
}
});
for (User user : temp) {
UserRecords re=new UserRecords();
for (MessageRecord communicationRecord : communicationRecords) {
if(communicationRecord.getCallingNumber().equals(user.getNumber()))
{
re.addSendMessageRecords(communicationRecord);
}
}
user.setUserRecords(re);
System.out.println(user.getNumber()+" "+Double.parseDouble(String.format("%.2f",user.calCost()))+" "+Double.parseDouble(String.format("%.2f",user.calBalance())));
}
}
private static User LineToUser(String str){
str = str.substring(2);
String[] data = str.split(" ");
return new User(data[0],Integer.parseInt(data[1]));
}
private static MessageRecord LineToMessageRecord(String str) throws ParseException {
str = str.substring(2);
String[] s = str.split(" ");
MessageRecord mesg=new MessageRecord();
mesg.setCallingNumber(s[0]);
mesg.setAnswerNumber(s[1]);
mesg.setMessage(str.substring(24));
return mesg;
}
}
class User{
@Override
public String toString() {
return "User{" +
"number='" + number + '\'' +
", chargeMode=" + chargeMode +
", balance=" + balance +
", userRecords=" + userRecords +
'}';
}
public User(String s1, int s2)
{
this.number=s1;
if (s2 == 3) {
this.chargeMode=new MobilePhoneCharging();
}
}
private String number;
private ChargeMode chargeMode;
private double balance=100;
UserRecords userRecords=new UserRecords();
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public ChargeMode getChargeMode() {
return chargeMode;
}
public void setChargeMode(ChargeMode chargeMode) {
this.chargeMode = chargeMode;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public UserRecords getUserRecords() {
return userRecords;
}
public void setUserRecords(UserRecords userRecords) {
this.userRecords = userRecords;
}
public double calBalance()
{
return this.balance-calCost();
}
public double calCost()
{
double cost = chargeMode.CalCost(getUserRecords());
return cost;
}
}
class UserRecords{
private ArrayList<MessageRecord> sendMessageRecords=new ArrayList<>();
private ArrayList<MessageRecord> receiveMessageRecords=new ArrayList<>();
public ArrayList<MessageRecord> getSendMessageRecords() {
return sendMessageRecords;
}
public void addSendMessageRecords(MessageRecord sendMessageRecord) {
sendMessageRecords.add(sendMessageRecord);
}
public ArrayList<MessageRecord> getReceiveMessageRecords() {
return receiveMessageRecords;
}
public void addReceiveMessageRecords(MessageRecord receiveMessageRecord) {
receiveMessageRecords.add(receiveMessageRecord);
}
}
abstract class ChargeMode{
private ArrayList<ChargeRule> chargeRules=new ArrayList<>();
public ArrayList<ChargeRule> getChargeRules() {
return chargeRules;
}
public void setChargeRules(ArrayList<ChargeRule> chargeRules) {
this.chargeRules = chargeRules;
}
public abstract double CalCost(UserRecords userRecords);
}
class MobilePhoneCharging extends ChargeMode{
// double MonthlyRent=15;
@Override
public double CalCost(UserRecords userRecords) {
ChargeRule cal=new MessageChargeRule();
double InLandCost = cal.calCost(userRecords);
return InLandCost;
}
}
abstract class CommunicationRecord{
private String callingNumber;
private String answerNumber;
public String getCallingNumber() {
return callingNumber;
}
public void setCallingNumber(String callingNumber) {
this.callingNumber = callingNumber;
}
public String getAnswerNumber() {
return answerNumber;
}
public void setAnswerNumber(String answerNumber) {
this.answerNumber = answerNumber;
}
}
class MessageRecord extends CommunicationRecord{
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "MessageRecord{" +
"message='" + message + '\'' +
'}';
}
}
abstract class ChargeRule {
public abstract double calCost(UserRecords records);
}
class MessageChargeRule extends ChargeRule{
@Override
public double calCost(UserRecords records) {
int Messagesum=0;
for (MessageRecord sendMessageRecord :records.getSendMessageRecords()) {
if(sendMessageRecord.getMessage().length()>10)
{
Messagesum+=(sendMessageRecord.getMessage().length()/10);
if(sendMessageRecord.getMessage().length()%10!=0)
{
Messagesum++;
}
}
else {
Messagesum++;
}
}
double sum=0;
if(Messagesum<=3)
{
sum+=(Messagesum*0.1);
}
else if(Messagesum>3&&Messagesum<=5)
{
sum+=((Messagesum-3)*0.2+0.3);
}
else if(Messagesum>5)
{
sum+=0.7+(Messagesum-5)*0.3;
}
return sum;
}
}
类图如下:
SourceMonitor分析图如下:
代码分析:
一、由类图可得
1.由于本题只涉及短信业务的计费,因此我删去了代码中有关于固定电话计费的部分以及手机通话计费业务的部分,只保留创建手机用户模块,并在原先的基础上增加了Message计费模式,以适应题目的变化。
2.由于删除较多冗余模块,本类图所反映的的依赖关系较为简单,主要就是MessageCharge类实现ChargeRule类,MobilePhoneCharging类实现ChargeMode类,MessageRecord类实现CommunicationRecord类这三种,各自实现了不同模块的具体功能例如统计短信记录,计算短信长度,计算总费用等等,最后再由User类将这些零散的模块进行耦合,实现题目要求的功能。
3.本代码的main方法除了正则表达式外变化不大,基本实现逻辑与上两题类似,在此不再赘述。
二、有SourceMonitor分析图可得
1.该代码除了最大复杂度超越预期值以外,其余指数均在期望值内,说明环复杂度较为理想。
2.Method/Class的值较小,说明代码内冗余类与方法较少,再一次体现多态-继承体系的优势之处。
3.平均深度与最大深度均在理想值范围内,说明代码的冗余度较低,不存在不必要的时间损失,资源利用率较高。
3.踩坑心得
1.第七次作业由于引入了新的手机用户类,将产生更多的通信情况,如果将这些情况全部用循环来计算,将会导致程序非常的冗余与难以维护。又因为在有关于手机的通信记录中,每个手机的号码后必须加入区号,这就导致了不同情况下的字符串根据" "截取后的字符串数组的总长度不尽相同。因此在分析完利弊之后,我决定根据字符串截取" "后获得的字符串数组的长度来判断究竟是电话拨打手机、手机拨打手机还是手机拨打电话,事实证明,这种方法确实较为简洁,有效的缩减了代码冗余度。
2.第八次作业的计算短信长度,需要判断短信的长度是否大于10,如果长度大于10则每10位算一条短信,我第一次写计算算法时,少判断了最后一种短信长度大于10,却不能整除于10的情况。比如长度为36,应该分为4条短信,最后一条短信长度为6,而在最初的版本中我没有加入判断条件,仅仅将整除10后结果当做短信数量,的导致最后短信数量仅为3,与正确答案不符,进而导致整个短信计算的错误。后来增加了针对性的判断情况,程序才能正常运行。
3.第八次作业的短信截取需要将短信内容的无效空格也截取进短信中。第一次写的时候我按照空格截取,结果有三个点总是无法通过,后来通过分析,将字符串从第24位截取到最后的内容视为短信内容,顺利通过。之所以是第24位是因为我事先根据"-"来截取格式类似于"m-18907910010 13305862264 aaaaaaaaaaaa"的字符串,将其分类两部分,取第二部分为初步处理后的字符串。而手机号码固定11位,两个手机号码为22位,加上两个用于分割的空格总占24位,因此24位后的内容就是短信内容,再调用substring()处理第一次处理后字符串,即可获取正确的结果。
4.代码改进建议
1、在这三次作业中,我根据类图构建了许多暂时用不到的方法,在实际应用中,可以选择性地将其删去,以缩减代码长度,使代码总体上更加紧凑。
2、在这三次作业中,部分操作我是在main方法中实现的,这种设置并不十分让人满意。因为许多模块都混在一起,代码十分难以调试,在编程的过程中我也深有体会,为了搞清楚正则表达式的问题,我不得不新建另一个项目并将相关代码复制一份过去,以避免在原项目上修改而产生不必要的麻烦。之后便是无休止地尝试找出可能不符合我编写的正则表达式的样例。这一过程较为低效且冗余,如果一开始就优化好代码结构,尽量避免在main方法中编写大量代码而只将其作为程序的入口,我认为将更有利于后期维护与调试。
3、在第七次作业的main方法的循环遍历User这部分代码,可以修改原先的遍历两次CommunicationRecords数组改为只遍历一次,只在内层嵌套循环内增加一次判断即可,这样做可以进一步减少环复杂度。
5.总结
一、作业总结:
这几次作业总的来说难度适中,不过相较于之前的多边形大作业更侧重数学方面,这几次作业更加侧重的是对java特性的考察以及学生对于类图的还原能力和扩展能力,题目本身逻辑并不复杂。通过编写并调试代码,我对于java面向对象的特性又有了更进一步的认识,也体会到了“程序设计”的“设计”的重要性。有时候,一个合理的系统设计将对程序的可移植性、可复用性、易修改性产生巨大的正面影响,反之一个糟糕的设计,将对后期编程与维护产生巨大的阻碍。
二、近期学习总结:从期中考试开始算起到现在的这六周,我主要复习了接口的编程思想、lambda表达式的使用方法以及学习了javaFx的基本编程方法,并完后了农夫过河的图形化界面设计。之后又学习了java线程编程的原理,完成了生产者消费者这一经典线程模型的java复现,最后还学习了一些java程序设计模式的思想与理念,可以说是收获颇丰了,在学习方面,我主要对java程序设计思想的认识还不够深刻,对于设计模式的运用还不够纯熟,希望能在日后的编程中逐渐补齐这一短板。
三、给老师的建议:希望老师在讲述java设计模式的时候能详细地讲一些经典设计模式以及其代码的使用场景,以便学生真正理解并运用这些宝贵的知识。