前言:

  本次博客包含的内容有pta题目集6-8(电信计费)三次题目集。这次的难点主要是理解老师给的类图的内容,明白各部分的作用,以及如何使各类之间联系起来。只要能够理解老师给的类图结构之后就可以比较轻松的完成本次的作业。

设计与分析:

  题目6-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元。
每条通讯信息单独计费后累加,不是将所有时间累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。

错误处理:
输入数据中出现的不符合格式要求的行一律忽略。

输入样例:

在这里给出一组输入。例如:

u-079186300001 0
t-079186300001 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:25
end
 

输出样例:

在这里给出相应的输出。例如:

079186300001 3.0 77.0

题目分析:

  这题老师给了相关的类图建议,我们来分析一下老师给的类图建议。

建议类图:
参见图1、2、3,可根据理解自行调整:

                                    图1
图1中User是用户类,包括属性:
userRecords (用户记录)、balance(余额)、chargeMode(计费方式)、number(号码)。 ChargeMode是计费方式的抽象类:
chargeRules是计费方式所包含的各种计费规则的集合,ChargeRule类的定义见图3。
getMonthlyRent()方法用于返回月租(monthlyRent)。 UserRecords是用户记录类,保存用户各种通话、短信的记录,
各种计费规则将使用其中的部分或者全部记录。
其属性从上到下依次是:
市内拨打电话、省内(不含市内)拨打电话、省外拨打电话、
市内接听电话、省内(不含市内)接听电话、省外接听电话的记录
以及发送短信、接收短信的记录。
 
 

                                     图2
图2中CommunicationRecord是抽象的通讯记录类:
包含callingNumber拨打号码、answerNumber接听号码两个属性。
CallRecord(通话记录)、MessageRecord(短信记录)是它的子类。 CallRecord(通话记录类)包含属性:
通话的起始、结束时间以及
拨号地点的区号(callingAddressAreaCode)、接听地点的区号(answerAddressAreaCode)。
区号用于记录在哪个地点拨打和接听的电话。座机无法移动,就是本机区号,如果是手机号,则会有差异。
 
 

                                        图3
图3是计费规则的相关类,这些类的核心方法是:
calCost(ArrayList<CallRecord> callRecords)。
该方法针根据输入参数callRecords中的所有记录计算某用户的某一项费用;如市话费。
输入参数callRecords的约束条件:必须是某一个用户的符合计费规则要求的所有记录。 LandPhoneInCityRule、LandPhoneInProvinceRule、LandPhoneInLandRule三个类分别是
座机拨打市内、省内、省外电话的计费规则类,用于实现这三种情况的费用计算。
(提示:可以从UserRecords类中获取各种类型的callRecords)。

我的分析:

这里的User类是用户类,用户开户时就要new一个User类,new User时要注意设置他的计费方式,用setChargeMode函数设置。
用户记录类UserRecords是用来记录户主所有的通信记录的。后面的计费就是通过这些记录来收费的。
ChargeMode是计费方式类,对不同的用户设置不同的计费规则,座机用户就是座机用户的计费规则,计费时传入用户通信记录的对象,对每一条记录进行计费。
CommunicationRecord类是所有通信记录的父类,短信记录和通话记录是具体的子类。记录了主叫电话号码和接收的电话号码,UserRecord类记录的通信记录就是这个类。
ChargeRule就是计费规则类,CallChargeRule就是打电话的计费规则,下面子类分别为座机在市内的呼叫计费,座机在省内的呼叫计费,座机在省外的呼叫计费。以后还需要添加手机在省内呼叫,手机在省外呼叫,手机的漫游接听计费的内容。还要添加一个Message的ChargeRule,里面的计算收费方法calCost传入的就要变成MessageRecord类的动态数组了,底下也跟之前一样,添加各类收费方式。
 
以上各类按照老师给的类图逐一实现各类里面的方法就可以了,大家的代码估计大同小异,主要的工作就是计费规则的设置,我就主要展示我的主类的处理和计费规则的设置。
这是主类

public class Main{
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
ArrayList<String> a=new ArrayList<String>();
String s=in.nextLine();
ArrayList<User> users=new ArrayList<User>();
while (!s.equals("end")){
if (s.matches("^u-[0-9]{11,12} 0$"))//开户匹配成功
{
String[] m=s.split("-");
User user=new User();
addU(m[1],user);
int count=0;
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getNumber().equals(user.getNumber()))
count++;
}
if (count==0)
users.add(user); } else if (s.matches("^t-\\d{10,12}\\s\\d{10,12}\\s([1-9][0-9]*\\.[1-9][0-9]?\\.[1-9][0-9]? ([0|1]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}$")) {//输入信息匹配成功
String[] m=s.split("-");
String[] n=m[1].split(" ");
for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(n[0])){//找到该号码
addT(m[1],users.get(i));
} }
}
s=in.nextLine();
}
DecimalFormat d=new DecimalFormat();
String style="#0.0#";
d.applyPattern(style);
sort(users);
for (int i = 0; i < users.size(); i++) {
double balance=users.get(i).calBalance();
double cost=users.get(i).calCost();
System.out.println(users.get(i).getNumber()+" "+d.format(cost)+" "+d.format(balance));
} }
static ArrayList<User> sort(ArrayList<User> users){
for (int i = 0; i < users.size(); i++) {
for (int j = i+1; j < users.size(); j++) {
if (Long.parseLong(users.get(i).getNumber())>Long.parseLong(users.get(j).getNumber())){
Collections.swap(users,i,j);
}
}
}
return users;
}
static void addU(String s, User user){//开户
String[] n=s.split(" ");
if (n[1].equals("0")){//座机用户
LandlinePhoneCharge landlinePhoneCharge=new LandlinePhoneCharge();
ArrayList<ChargeRule> chargeRules=new ArrayList<ChargeRule>();
landlinePhoneCharge.setChargeRules(chargeRules);
user.setChargeMode(landlinePhoneCharge);
user.setNumber(n[0]);
}
}
static void addT(String s, User user){//找到是主叫号码后添加记录
String[] n=s.split(" ");
CallRecord callRecord =new CallRecord();
callRecord.setCallingNumber(n[0]);
callRecord.setAnswerNumber(n[1]);
SimpleDateFormat time=new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
String start=n[2]+" "+n[3];
String end=n[4]+" "+n[5];
try {
callRecord.setStartTime(time.parse(start));
callRecord.setEndTime(time.parse(end));
} catch (ParseException e) {
throw new RuntimeException(e);
}
callRecord.setCallingAddressAreaCode(n[0].substring(0,4));
callRecord.setAnswerAddressAreaCode(n[1].substring(0,4));
if (callRecord.getCallingAddressAreaCode().equals("0791")){
if (callRecord.getAnswerAddressAreaCode().equals("0791")){//市内通话
user.userRecords.addCallingInCityRecords(callRecord);
} else if (callRecord.getAnswerAddressAreaCode().matches("079[0-9]")||callRecord.getAnswerAddressAreaCode().equals("0701")) {//省内通话
user.userRecords.addCallingInProvinceRecords(callRecord);
} else{//国内通话
user.userRecords.addCallingInLandRecords(callRecord);
}
} }
}

这是计费规则的设置:

class LandPhoneInCityRule extends CallingChargeRule{//市内计费规则
@Override
double calCost(ArrayList<CallRecord> callRecords){//计算花费
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.1*minutes;
}
return cost;
} }
class LandPhoneInProvinceRule extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.3*minutes;
}
return cost;
}
} class LandPhoneInLandRule extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.6*minutes;
}
return cost; }
}

这是座机的计费方式设置:


class LandlinePhoneCharge extends ChargeMode{//座机计费规则
double monthlyRent=20;
@Override
public void setChargeRules(ArrayList<ChargeRule> chargeRules) {//设置座机计费规则
chargeRules.add(new LandPhoneInCityRule());
chargeRules.add(new LandPhoneInProvinceRule());
chargeRules.add(new LandPhoneInLandRule());
this.chargeRules = chargeRules;
} @Override
double calCost(UserRecords userRecords){//计算花费
double cost=0;
cost+=chargeRules.get(0).calCost(userRecords.getCallingInCityRecords());
cost+=chargeRules.get(1).calCost(userRecords.getCallingInProvinceRecords());
cost+=chargeRules.get(2).calCost(userRecords.getCallingInLandRecords());
return cost;
}
@Override
double getMonthlyRent(){//得到月租
return monthlyRent;
}
}

这是我的各类之间的联系图

这是SourceMonitor生成的报表内容

踩坑心得:

  主要难点就是理解老师的类图,接下来一步一步实现就可以了。注意日期使用dateformat的数据类型来记录。开户时要注意把计费规则设置好。

题目7-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元。
每条通讯、短信信息均单独计费后累加,不是将所有信息累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。
错误处理:
输入数据中出现的不符合格式要求的行一律忽略。

输入样例:

在这里给出一组输入。例如:

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

题目分析:

主要的类图分析在上一题已经给大家分析过了,这题只需要在上一题的基础上添加手机的计费规则和主类中开户和记录的处理。接下来给大家展示我写的代码。

这是主类:


public class Main {
public static void main(String[] args) {
//座机呼叫座机
String tPattern = "t-0\\d{9,11}\\s" + "0\\d{9,11}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//座机打手机
String tPattern1 = "t-\\d{10,12}\\s" + "1\\d{10}\\s\\d{3,4}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//手机互打
String tPattern2 = "t-\\d{11}\\s\\d{3,4}\\s" + "\\d{11}\\s\\d{3,4}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//手机打座机
String tPattern3 = "t-\\d{11}\\s\\d{3,4}\\s" + "\\d{10,12}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}"; Scanner in=new Scanner(System.in); String s=in.nextLine();
ArrayList<User> users=new ArrayList<User>();
while (!s.equals("end")){
if (s.matches("^u-[0-9]{11,12} 0$")||s.matches("[u]-1[0-9]{10}\\s[1]"))//开户匹配成功
{
String[] m=s.split("-");
User user=new User();
addU(m[1],user);
int count=0;
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getNumber().equals(user.getNumber()))
count++;
}
if (count==0)
users.add(user);
} else if (s.matches(tPattern)) {//输入信息匹配成功//座机互打
String[] m=s.split("-");
String[] n=m[1].split(" ");
String callNumber = n[0];
String callAddress = n[0].substring(0,4);
String answerNumber = n[1];
String answerAddress = n[1].substring(0,4);
String beginTime = n[2]+" "+n[3];
String endTime = n[4]+" "+n[5];
for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(n[0])){//找到该号码主叫
addT(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}if (user.getNumber().equals(n[1])){//接听
addTAnswer(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
}
} else if (s.matches(tPattern1)) {//座机打手机
String[] m=s.split("-");
String[] n=m[1].split(" ");
String callNumber = n[0];
String callAddress = n[0].substring(0,4);
String answerNumber = n[1];
String answerAddress = n[2];
String beginTime = n[3]+" "+n[4];
String endTime = n[5]+" "+n[6]; for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(n[0])){//找到该号码主叫
addT(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
if (user.getNumber().equals(n[1])){//接听
addTAnswer(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
}
} else if (s.matches(tPattern2)) {//手机互打
String[] m=s.split("-");
String[] n=m[1].split(" ");
String callNumber = n[0];
String callAddress = n[1];
String answerNumber = n[2];
String answerAddress = n[3];
String beginTime = n[4]+" "+n[5];
String endTime = n[6]+" "+n[7]; for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(n[0])){//找到该号码主叫
addTMCalling(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
if (user.getNumber().equals(n[2])){//接听
addTAnswer(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
} } else if (s.matches(tPattern3)) {//手机打座机
String[] m=s.split("-");
String[] n=m[1].split(" ");
String callNumber = n[0];
String callAddress = n[1];
String answerNumber = n[2];
String answerAddress = n[2].substring(0,4);
String beginTime = n[3]+" "+n[4];
String endTime = n[5]+" "+n[6];
for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(n[0])){//找到该号码主叫
addTMCalling(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
if (user.getNumber().equals(n[2])){//接听
addTAnswer(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
}
} s=in.nextLine();
}
DecimalFormat d=new DecimalFormat();
String style="#0.0#";
d.applyPattern(style);
sort(users);
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getNumber().substring(0,4).equals("0791")) {
double balance = users.get(i).calBalance();
double cost = users.get(i).calCost();
System.out.println(users.get(i).getNumber() + " " + d.format(cost) + " " + d.format(balance));
}
}
for (int i = 0; i < users.size(); i++) {
if (!users.get(i).getNumber().substring(0,4).equals("0791")) {
double balance = users.get(i).calBalance();
double cost = users.get(i).calCost();
System.out.println(users.get(i).getNumber() + " " + d.format(cost) + " " + d.format(balance));
}
} }
static ArrayList<User> sort(ArrayList<User> users){ for (int i = 0; i < users.size(); i++) {
for (int j = i+1; j < users.size(); j++) {
if (Long.parseLong(users.get(i).getNumber())>Long.parseLong(users.get(j).getNumber())){
Collections.swap(users,i,j);
}
}
}
return users;
}
public static void addU(String s, User user){//开户
String[] n=s.split(" ");
if (n[1].equals("0")){//座机用户
LandlinePhoneCharge landlinePhoneCharge=new LandlinePhoneCharge();
ArrayList<ChargeRule> chargeRules=new ArrayList<ChargeRule>();
landlinePhoneCharge.setChargeRules(chargeRules);
user.setChargeMode(landlinePhoneCharge);
user.setNumber(n[0]);
}
if (n[1].equals("1")){//手机
MobilePhoneCharge mobilePhoneCharge= new MobilePhoneCharge();
ArrayList<ChargeRule> chargeRules = new ArrayList<>();
mobilePhoneCharge.setChargeRules(chargeRules);
user.setChargeMode(mobilePhoneCharge);
user.setNumber(n[0]); }
}
static void addT(String callNumber, String callAddress,String answerNumber,String answerAddress,String beginTime, String endTime, User user){//找到是主叫号码后添加记录
CallRecord callRecord =new CallRecord();
callRecord.setCallingNumber(callNumber);
callRecord.setAnswerNumber(answerNumber);
SimpleDateFormat time=new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
String start=beginTime;
String end=endTime;
try {
callRecord.setStartTime(time.parse(start));
callRecord.setEndTime(time.parse(end));
} catch (ParseException e) {
throw new RuntimeException(e);
}
callRecord.setCallingAddressAreaCode(callAddress);
callRecord.setAnswerAddressAreaCode(answerAddress);
if (callRecord.getCallingAddressAreaCode().equals("0791")){
if (callRecord.getAnswerAddressAreaCode().equals("0791")){//市内通话
user.userRecords.addCallingInCityRecords(callRecord);
} else if (callRecord.getAnswerAddressAreaCode().matches("079[0-9]")||callRecord.getAnswerAddressAreaCode().equals("0701")) {//省内通话
user.userRecords.addCallingInProvinceRecords(callRecord);
} else{//国内通话
user.userRecords.addCallingInLandRecords(callRecord);
}
}
}
static void addTAnswer(String callNumber, String callAddress,String answerNumber,String answerAddress,String beginTime, String endTime, User user){//找到是接听号码后添加记录 CallRecord callRecord =new CallRecord();
callRecord.setCallingNumber(callNumber);
callRecord.setAnswerNumber(answerNumber); SimpleDateFormat time=new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
String start=beginTime;
String end=endTime;
try {
callRecord.setStartTime(time.parse(start));
callRecord.setEndTime(time.parse(end));
} catch (ParseException e) {
throw new RuntimeException(e);
}
callRecord.setCallingAddressAreaCode(callAddress);
callRecord.setAnswerAddressAreaCode(answerAddress);
if (callRecord.getAnswerAddressAreaCode().equals("0791")){//市内接听电话
user.userRecords.addAnswerInCityRecords(callRecord);
}
else if (callRecord.getAnswerAddressAreaCode().matches("079[0-9]")||callRecord.getAnswerAddressAreaCode().equals("0701")){//省内接听电话
user.userRecords.addAnswerInProvinceRecords(callRecord);
}else {//省外接听电话
user.userRecords.addAnswerInLandRecords(callRecord);
}
} static void addTMCalling(String callNumber, String callAddress, String answerNumber, String answerAddress, String beginTime, String endTime, User user) {//找到是手机主叫号码后添加记录 CallRecord callRecord = new CallRecord();
callRecord.setCallingNumber(callNumber);
callRecord.setAnswerNumber(answerNumber);
SimpleDateFormat time = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
String start = beginTime;
String end = endTime;
try {
callRecord.setStartTime(time.parse(start));
callRecord.setEndTime(time.parse(end));
} catch (ParseException e) {
throw new RuntimeException(e);
}
callRecord.setCallingAddressAreaCode(callAddress);
callRecord.setAnswerAddressAreaCode(answerAddress);
if (callRecord.getCallingAddressAreaCode().equals("0791")) {//市内拨打电话
user.userRecords.addCallingInCityRecords(callRecord);
} else if (callRecord.getCallingAddressAreaCode().matches("079[0-9]") || callRecord.getCallingAddressAreaCode().equals("0701")) {//省内拨打电话
user.userRecords.addCallingInProvinceRecords(callRecord);
} else {//省外接听电话
user.userRecords.addCallingInLandRecords(callRecord);
}
}
}

这是手机的各种计费规则的设置


class MobilePhoneCallCityToCity extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.1*minutes;
}
return cost;
}
} class MobilePhoneCallCityToProvince extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.2*minutes;
}
return cost;
}
} class MobilePhoneCallCityToLand extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.3*minutes;
}
return cost;
}
}
class MobilePhoneCallInProvince extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.3*minutes;
}
return cost;
}
} class MobilePhoneAnswerInLand extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.3*minutes;
}
return cost;
}
} class MobilePhoneCallInLand extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.6*minutes;
}
return cost;
}
}

这是手机的计费方式的设置


class MobilePhoneCharge extends ChargeMode{//移动手机的计费规则
double monthlyRent=15;
@Override
public void setChargeRules(ArrayList<ChargeRule> chargeRules) {//设置座机计费规则
chargeRules.add(new MobilePhoneCallCityToCity());
chargeRules.add(new MobilePhoneCallCityToProvince());
chargeRules.add(new MobilePhoneCallCityToLand());
chargeRules.add(new MobilePhoneCallInLand());
chargeRules.add(new MobilePhoneCallInProvince());
chargeRules.add(new MobilePhoneAnswerInLand()); this.chargeRules = chargeRules;
} @Override
double calCost(UserRecords userRecords){//计算花费
double cost=0;
cost+=chargeRules.get(0).calCost(userRecords.getCallingInCityToCityRecords());
cost+=chargeRules.get(1).calCost(userRecords.getCallingInCityToProvinceRecords());
cost+=chargeRules.get(2).calCost(userRecords.getCallingInCityToLandRecords());
cost+=chargeRules.get(3).calCost(userRecords.getCallingInLandRecords());
cost+=chargeRules.get(4).calCost(userRecords.getCallingInProvinceRecords());
cost+=chargeRules.get(5).calCost(userRecords.getAnswerInLandRecords()); return cost;
}
@Override
double getMonthlyRent(){//得到月租
return monthlyRent;
}
}

代码的类图和sourceMonitor报表已经在上题放出。

踩坑心得:

  本题主要是在上题的基础上添加了手机的计费规则,手机的计费规则比座机计费规则复杂度提升了许多,所以在计费时也有不同的计费规则。注意要对不同的拨打方式设置不同的函数来记录。还有在展示时要将座机的优先展示再按号码大小排序。

题目8-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。

本题只考虑短信计费,不考虑通信费用以及月租费。

输入样例:

在这里给出一组输入。例如:

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

题目分析:

  本题的要求是只记录短信的相较于前面两题较为简单,接下来直接给出相应的代码。

这是主类:

public class Main {
public static void main(String[] args) {
//座机呼叫座机
String tPattern = "t-0\\d{9,11}\\s" + "0\\d{9,11}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//座机打手机
String tPattern1 = "t-\\d{10,12}\\s" + "1\\d{10}\\s\\d{3,4}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//手机互打
String tPattern2 = "t-\\d{11}\\s\\d{3,4}\\s" + "\\d{11}\\s\\d{3,4}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//手机打座机
String tPattern3 = "t-\\d{11}\\s\\d{3,4}\\s" + "\\d{10,12}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
String message_regex_3 = "[m]-1[0-9]{10}\\s" + "1[0-9]{10}\\s" + "[0-9a-zA-Z\\s\\.,]+";
Scanner in=new Scanner(System.in); String s=in.nextLine();
ArrayList<User> users=new ArrayList<User>();
while (!s.equals("end")){
if (s.matches("[u]-1[0-9]{10}\\s[3]")||s.matches("^u-[0-9]{11,12} 0$")||s.matches("[u]-1[0-9]{10}\\s[1]"))//开户匹配成功
{
String[] m=s.split("-");
User user=new User();
addU(m[1],user);
int count=0;
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getNumber().equals(user.getNumber()))
count++;
}
if (count==0)
users.add(user);
}
else if (s.matches(message_regex_3)){
String m=s.substring(2,26);
String[] n = m.split(" ");
String sendNumber = n[0];
String receiveNumber = n[1];
String message = s.substring(26);
for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(sendNumber)){//找到该号码主叫
addMSend(sendNumber,receiveNumber,message,user);
}
if (user.getNumber().equals(receiveNumber)){
addMReceive(sendNumber,receiveNumber,message,user);
}
}
} s=in.nextLine();
}
DecimalFormat d=new DecimalFormat();
String style="#0.0#";
d.applyPattern(style);
sort(users);
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getNumber().substring(0,4).equals("0791")) {
double balance = users.get(i).calBalance();
double cost = users.get(i).calCost();
System.out.println(users.get(i).getNumber() + " " + d.format(cost) + " " + d.format(balance));
}
}
for (int i = 0; i < users.size(); i++) {
if (!users.get(i).getNumber().substring(0,4).equals("0791")) {
double balance = users.get(i).calBalance();
double cost = users.get(i).calCost();
System.out.println(users.get(i).getNumber() + " " + d.format(cost) + " " + d.format(balance));
}
} } static ArrayList<User> sort(ArrayList<User> users){ for (int i = 0; i < users.size(); i++) {
for (int j = i+1; j < users.size(); j++) {
if (Long.parseLong(users.get(i).getNumber())>Long.parseLong(users.get(j).getNumber())){
Collections.swap(users,i,j);
}
}
}
return users;
}
public static void addU(String s, User user){//开户
String[] n=s.split(" ");
if (n[1].equals("3")){//短信
MessageCharge messageCharge = new MessageCharge();
ArrayList<ChargeRule> chargeRules = new ArrayList<>();
messageCharge.setChargeRules(chargeRules);
user.setChargeMode(messageCharge);
user.setNumber(n[0]);
}
}
static void addMSend(String sendNumber,String receiveNumber,String message,User user){
MessageRecord messageRecord = new MessageRecord();
messageRecord.setMessage(message);
messageRecord.setCallingNumber(sendNumber);
messageRecord.setAnswerNumber(receiveNumber);
user.userRecords.addSendMessageRecords(messageRecord);
}
static void addMReceive(String sendNumber, String receiveNumber, String message, User user){
MessageRecord messageRecord = new MessageRecord();
messageRecord.setMessage(message);
messageRecord.setCallingNumber(sendNumber);
messageRecord.setAnswerNumber(receiveNumber);
user.userRecords.addReceiveMessageRecords(messageRecord);
} }

这是message的计费规则的设置


class MessageChargeRule extends ChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords) {
return 0;
} double mesCost(ArrayList<MessageRecord> messageRecords){
double cost=0;
int num=0;
for (int i = 0; i < messageRecords.size(); i++) {
int count=messageRecords.get(i).getMessage().length()/10;
if (messageRecords.get(i).getMessage().length()%10>0)
count++;
num+=count;
}
if (num<=3){
cost=0.1*num;
} else if (num > 3 && num <= 5) {
cost = 0.3+0.2*(num-3);
} else if (num > 5) {
cost = 0.3+0.4+0.3*(num-5);
}
return cost;
}
}

这是message计费方式的设置

class MessageCharge extends ChargeMode{
@Override
public void setChargeRules(ArrayList<ChargeRule> chargeRules){
chargeRules.add(new MessageChargeRule());
this.chargeRules=chargeRules;
} @Override
double calCost(UserRecords userRecords) {
return chargeRules.get(0).mesCost(userRecords.getSendMessageRecords());
} @Override
double getMonthlyRent() {
return 0;
}

各类的联系图和sourceMonitor生成的报表内容再第一题给出了。

踩坑心得:

  本题的难度相较于前面两题要简单一点。只需要考虑短信这一点就可以了,不过在设置MessageChargeRule时不能使用CallRecord的方法,可以选择使用下转型或者单独添加一个计算message的方法。

总结:

  本次的三个题目主要的难点就是能过够读懂老师的类图并将它实现,总体难度并不高,比较容易能够完成,这三题还是要求比较熟练的使用继承和多态以及动态数组的使用,算是对以前的知识的一次比较完整的总结测试了。经过这几次的作业以后,确实使我对java有了更深的理解,也让我能够更加熟练的使用java来编写程序了。感谢您的阅读!

nchu第三次面向对象编程博客作业的更多相关文章

  1. OO--第三单元规格化设计 博客作业

    OO--第三单元规格化设计 博客作业 前言 第三单元,我们以JML为基础,先后完成了 PathContainer -> Graph -> RailwaySystem 这是一个递进的过程,代 ...

  2. [BUAA OO]第三次博客作业

    OO第三次博客作业 1. 规格化设计的发展 我认为,规格化设计主要源自于软件设计的两次危机.第一次是由于大量存在的goto语句,让当时被广泛应用的面向过程式的编程语言臃肿不堪,在逻辑性上与工程规模上鱼 ...

  3. OO第三次博客作业——规格

    OO第三次博客作业——规格 一.调研结果: 规格的历史: 引自博文链接:http://blog.sina.com.cn/s/blog_473d5bba010001x9.html 传统科学的特点是发现世 ...

  4. 进击的Python【第六章】:Python的高级应用(三)面向对象编程

    Python的高级应用(三)面向对象编程 本章学习要点: 面向对象编程介绍 面向对象与面向过程编程的区别 为什么要用面向对象编程思想 面向对象的相关概念 一.面向对象编程介绍 面向对象程序设计(英语: ...

  5. [2017BUAA软工]第三次博客作业:案例分析

    第三次博客作业:案例分析 1. 调研和评测 1.1 BUG及设计缺陷描述 主要测试博客园在手机端上的使用情况. [BUG 01] 不能后退到上一界面(IOS) 重现步骤:打开博客首页中任意博文,点击博 ...

  6. 第三篇Scrum冲刺博客

    第三篇Scrum冲刺博客 一.站立式会议 提供当天站立式会议照片一张 二.每个人的工作 成员 已完成工作 明天计划完成的工作 遇到的困难 林剑峰 初步完成用户界面 用户界面跳转到用户信息页面的按钮,设 ...

  7. Python学习-第三天-面向对象编程基础

    Python学习-第三天-面向对象编程基础 类和对象 简单的说,类是对象的蓝图和模板,而对象是类的实例.这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的 ...

  8. 第三篇Scrum冲刺博客--Interesting-Corps

    第三篇Scrum冲刺博客 站立式会议 1.会议照片 2.队友完成情况 团队成员 昨日完成 今日计划 鲍鱼铭 主页页面跳转社区功能及社区设计及布局实现 搜索页面跳转.设计及布局实现 叶学涛 编写个人页面 ...

  9. Objective-C 基础教程第三章,面向对象编程基础知

    目录 Objective-C 基础教程第三章,面向对象编程基础知 0x00 前言 0x01 间接(indirection) 0x02 面向对象编程中使用间接 面向过程编程 面向对象编程 0x03 OC ...

  10. OO第二次博客作业—17373247

    OO第二次博客作业 零.写在前面 OO第二单元宣告结束,在这个单元里自己算是真正对面向对象编程产生了比较深刻的理解,也认识到了一个合理的架构为编程带来的极大的便利. (挂三次评测分数 看出得分接近等差 ...

随机推荐

  1. 【转】【善用佳软】文件复制软件评测:FastCopy、TeraCopy、ExtremeCopy、Supercopier

    文件复制软件评测:FastCopy.TeraCopy.ExtremeCopy.Supercopier 原文:https://xbeta.info/fastcopy-teracopy-extremeco ...

  2. Nginx结合tomcat 负载均衡

    负载均衡是我们大流量网站要做的一个东西,下面我来给大家介绍在Nginx服务器上进行负载均衡配置方法,希望对有需要的同学有所帮助哦. 负载均衡 先来简单了解一下什么是负载均衡,单从字面上的意思来理解就可 ...

  3. js match方法

    1.用法 match()方法可以字符串中检索指定的值,或者是匹配一个或多个正则表达式 2.返回值 该方法类似于indexOf()/lastIndexOf(),区别就是返回值不一样 indexOf()/ ...

  4. Linux下添加启动项并简化操作命令-nginx为例

    1.添加nginx为启动项        1. vi /etc/rc.d/rc.local        2.将启动命令直接添加到最后即可 *注:通过下图可知  /etc/rc.d/rc.local和 ...

  5. LocalDateTime与LocalDate

    时间新特性 新生事物出现,必定是对旧事物的完善或者是缺陷的弥补. 本文章介绍LocalDate.LocalDateTime.在多线程的情况,相比较于Date.Calendar.SimpleDateFo ...

  6. stl算法汇总

  7. centos 服务器配置网络ifconfig位置

    path :   /etc/sysconfig/network-scripts/ifcfg-enoxxx

  8. A Novel Sequential Method to Train Physics Informed Neural Networks for Allen Cahn and Cahn Hilliard Equations

    一种新的顺序方法去求解关于时间的方程.个人感觉论文很差.(方法不新颖,写作很无聊,排版也有问题,内容也表述不清). 本文提出一种利用单个神经网络,在连续时间段上顺序求解偏微分方程的新型方案.关键思想是 ...

  9. Linux系统修改静态ip

    查看所有网卡 ip信息 ipconfig 修改网卡文件 vim /etc/sysconfig/network-scripts/ifcfg-eno1(网卡名) 新增语句 IPADDR=192.168.1 ...

  10. select remove option safari 兼容

    select 移除某一 option 的 javascript 公司用的代码是 var ddlPrimaryResource = document.getElementById(ddlPrimaryR ...