JAVA学习--第三次作业总结

 

目录

前言

      本次主要分析电信计费系列的题目,在给出类图的条件下实现代码难度不大,主要是实现设计的可扩展性较难,通过本次的练习了解到设计的重要性,一个好的设计模式是一套可以经过反复使用的代码设计经验,可以保证代码的复用性,使代码更容易被别人理解,保证代码的可靠性。在本次的课程中尝试简单使用了几种设计模式,比如策略模式,状态模式。了解到设计模式的六大原则:开闭原则(Open Close Principle),依赖倒转原则(Dependence Inversion Principle),里氏替换原则(Liskov Substitution),接口隔离原则(Interface Segregation Principle),迪米特原则(Demeter Principle),合成复合原则(Composite Reuse Principle)设计模式于己于人于系统都是多赢的,它使得代码编写真正工程化,它是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。

电信计费系列1-座机计费:


  • 主要考察类图理解能力,以及字符串的处理,正则表达式的使用
  • 题量不大,主要是电信计费系列的实现

电信计费系列2-手机+座机计费:


  • 在电信计费系列一上添加了手机用户,增加了漫游的方式
  • 题量不大,较容易实现

电信计费系列3-短信计费:


  • 添加针对短信的收费方式,主要是对字符串的处理
  • 难度不大

设计与分析

电信计费系列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,可根据理解自行调整:

image.png

                                    图1
图1中User是用户类,包括属性:
userRecords (用户记录)、balance(余额)、chargeMode(计费方式)、number(号码)。

ChargeMode是计费方式的抽象类:
chargeRules是计费方式所包含的各种计费规则的集合,ChargeRule类的定义见图3。
getMonthlyRent()方法用于返回月租(monthlyRent)。

UserRecords是用户记录类,保存用户各种通话、短信的记录,    
各种计费规则将使用其中的部分或者全部记录。
其属性从上到下依次是:
市内拨打电话、省内(不含市内)拨打电话、省外拨打电话、
市内接听电话、省内(不含市内)接听电话、省外接听电话的记录
以及发送短信、接收短信的记录。
 

image.png

                                     图2
图2中CommunicationRecord是抽象的通讯记录类:
包含callingNumber拨打号码、answerNumber接听号码两个属性。
CallRecord(通话记录)、MessageRecord(短信记录)是它的子类。

CallRecord(通话记录类)包含属性:
通话的起始、结束时间以及
拨号地点的区号(callingAddressAreaCode)、接听地点的区号(answerAddressAreaCode)。
区号用于记录在哪个地点拨打和接听的电话。座机无法移动,就是本机区号,如果是手机号,则会有差异。
 

image.png

                                        图3
图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
show code
import java.util.ArrayList;
import java.util.Date;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class Main {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
        Processdata p = new Processdata();
        p.work();
	}

}
abstract class CallChargeRule extends ChargeRule {
    public abstract  double calCost(ArrayList<CallRecord> callrecords); 
    
    
}
class CallRecord extends CommunicationRecord {
     private Date  stateTime;
     private Date  endTime;
     private String callingAddressAreaCode;
     private String answerAddressAreaCode;
     
	public CallRecord( String callingNumber,String answerNumber,Date stateTime, Date endTime, String callingAddressAreaCode, String answerAddressAreaCode) {
		
		super();
		setCallingNumber(callingNumber);
		setAnswerNumber(answerNumber);
		this.stateTime = stateTime;
		this.endTime = endTime;
		this.callingAddressAreaCode = callingAddressAreaCode;
		this.answerAddressAreaCode = answerAddressAreaCode;
	}
	public Date getStateTime() {
		return stateTime;
	}
	public void setStateTime(Date stateTime) {
		this.stateTime = stateTime;
	}
	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 ChargeMode {
	private ArrayList<ChargeRule> chargeRules = new ArrayList<ChargeRule>();

	

	

	public ArrayList<ChargeRule> getChargeRules() {
		return chargeRules;
	}

	public void setChargeRules(ArrayList<ChargeRule> chargeRules) {
		this.chargeRules = chargeRules;
	}

	public abstract double calCost(UserRecords useRecords);

	public abstract double getMonthlyRent();

}
abstract class ChargeRule {
	public  abstract double calCost(ArrayList<CallRecord> callrecords);
}
abstract class CommunicationRecord {
	protected String callingNumber;
	protected 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 LandlinePhoneCharging extends ChargeMode {
	private double monthlyRent = 20;

	public LandlinePhoneCharging() {
		super();

		getChargeRules().add(new LandPhoneInCityRule());
		getChargeRules().add(new LandPhoneInProvinceRule());
		getChargeRules().add(new LandPhoneInLandRule());
	}

	@Override
	public double calCost(UserRecords useRecords) {
		// TODO 自动生成的方法存根
		double cost= 0;
		cost+= getChargeRules().get(0).calCost(useRecords.getCallinginCityRecords());
		cost+= getChargeRules().get(1).calCost(useRecords.getCallinginProvinceRecords());
		cost+= getChargeRules().get(2).calCost(useRecords.getCallinginLandRecords());
		return cost;

	}

	@Override
	public double getMonthlyRent() {
		// TODO 自动生成的方法存根
		return this.monthlyRent;
	}

}
class LandPhoneInCityRule extends CallChargeRule {
   
	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		double cost = 0;
		for(CallRecord c : callrecords) {
			cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.1;
		}
		return cost;
	} 
    

}
class LandPhoneInLandRule extends CallChargeRule {

	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		double cost = 0;
		for(CallRecord c : callrecords) {
			cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.6;
		}
		return cost;
	}



}
 class LandPhoneInProvinceRule extends CallChargeRule {

	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		double cost = 0;
		for(CallRecord c : callrecords) {
			cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.3;
		}
		return cost;
	}



}
class MessageRecord extends CommunicationRecord {
	private String message;

	public MessageRecord(String callingNumber, String answerNumber, String message) {
		super();
		setCallingNumber(callingNumber);
		setAnswerNumber(answerNumber);
		this.message = message;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

}
 class Processdata {
	
   
	public Processdata() {
		super();
		// TODO 自动生成的构造函数存根
	}

		public  void work() {
		TreeMap<String, User> users = new TreeMap<>();   
		Scanner input = new Scanner(System.in);
		String regex1 ="[u]-[0-9]{11,12}\\s[0]";
		String regex2 ="t-(\\d){11,12}\\s(\\d){10,12}\\s((((1[6-9]|[2-9]\\d)\\d{2}).([13578]|1[02]).([1-9]|[12]\\d|3[01]))|(((1[6-9]|[2-9]\\d)\\d{2}).([13456789]|1[012]).([1-9]|[12]\\d|30))|(((1[6-9]|[2-9]\\d)\\d{2})-2-([1-9]|1\\d|2[0-8]))|(((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-2-29-)) (20|21|22|23|[0-1]\\d):[0-5]\\d:[0-5]\\d\\s((((1[6-9]|[2-9]\\d)\\d{2}).([13578]|1[02]).([1-9]|[12]\\d|3[01]))|(((1[6-9]|[2-9]\\d)\\d{2}).([13456789]|1[012]).([1-9]|[12]\\d|30))|(((1[6-9]|[2-9]\\d)\\d{2})-2-([1-9]|1\\d|2[0-8]))|(((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-2-29-)) (20|21|22|23|[0-1]\\d):[0-5]\\d:[0-5]\\d";
		String s = input.nextLine();
	    while(!s.equals("end")) {
	    	if(s.matches(regex1)) {
	    		String data[] = s.substring(2).split(" ");
	    		String number = data[0].substring(4);
	    		ChargeMode changeMode = new LandlinePhoneCharging();
	    		User user = new User(changeMode,number);
	    		users.put(number, user);
	    		
	    	}
	    	else if(s.matches(regex2)) {
	    		String data[] = s.substring(2).split(" ");
	    		String callingAddressAreaCode = data[0].substring(0,4);
	    		String answerAddressAreaCode = data[1].substring(0,4);
	    		String callingNumber = data[0].substring(4);
	    		String answerNumber = data[1].substring(4);
	    		String stateDate = data[2];
	    		String stateTime = data[3];
	    		String endDate  = data[4];
	    		String endTime = data[5];
	    		String state = stateDate+" "+stateTime;
	    		String end = endDate + " " + endTime;
	    		Date statedate = new Date();
	    		Date enddate = new Date();
	    		String strDateFormat = "yyyy.MM.dd HH:mm:ss";
	    		 
	    		SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
	    		try {
					statedate = sdf.parse(state);
				} catch (ParseException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
	    		
	    		try {
					enddate = sdf.parse(end);
				} catch (ParseException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
	    		
	    		CallRecord callRecord = new CallRecord(callingNumber, answerNumber, statedate, enddate, callingAddressAreaCode, answerAddressAreaCode);
	    		Long address = Long.parseLong(callRecord.getAnswerAddressAreaCode());
//	    		if(callRecord.getCallingNumber().equals(callRecord.getAnswerNumber())) {
//	    			break;
//	    		}
	    		if(users.get(callingNumber) != null){
                    if(address==791) {
	    			users.get(callingNumber).getUserRecords().addCallinginCityRecords(callRecord);
	    		}
	    		
	    		else if(address==701||(address>= 790 &&address<=799&&address!= 791)) {
	    			users.get(callingNumber).getUserRecords().addCallinginProvinceRecords(callRecord);
	    		
	    	    }
	    		else {
	    			users.get(callingNumber).getUserRecords().addCallinginLandRecords(callRecord);
	    		}
                }
                
	    		
	    		
	    		   
	    		  
	    		   
	    		  }

	      s = input.nextLine();
	    }

	    for (User user: users.values()) {
			  System.out.println("0791"+user.getNumber()+" "+Math.round(user.calCost()*100)/100.0+" "+Math.round(user.calBanlance()*100)/100.0);
			  }
	}
	}
	


class User {
	private UserRecords userRecords = new UserRecords();// 用户记录
	private double banlance = 100; // 余额
	private ChargeMode changeMode; // 计费方式
	private String number; // 电话号码
    
	

	

	public User(ChargeMode changeMode, String number) {
		super();
		this.changeMode = changeMode;
		this.number = number;
	}

	public UserRecords getUserRecords() {
		return userRecords;
	}

	public void setUserRecords(UserRecords userRecords) {
		this.userRecords = userRecords;
	}

	public double getBanlance() {
		return banlance;
	}

	public void setBanlance(double banlance) {
		this.banlance = banlance;
	}

	public ChargeMode getChangeMode() {
		return changeMode;
	}

	public void setChangeMode(ChargeMode changeMode) {
		this.changeMode = changeMode;
	}

	public String getNumber() {
		return number;
	}

	public void setNumber(String number) {
		this.number = number;
	}

	public double calBanlance() {
		return this.banlance-changeMode.calCost(userRecords)-changeMode.getMonthlyRent();

	}

	public double calCost() {
		return changeMode.calCost(userRecords);

	}
}
class UserRecords {
    
	public UserRecords() {
		super();
		// TODO 自动生成的构造函数存根
	}

	private ArrayList<CallRecord> callinginCityRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> callinginProvinceRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> callinginLandRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> answennCityRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> answerinProvinceRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> answernLandRecords = new ArrayList<CallRecord>();
	private ArrayList<MessageRecord> sendMessageRecords = new ArrayList<MessageRecord>();
	private ArrayList<MessageRecord> receiveMessageRecords = new ArrayList<MessageRecord>();

	public ArrayList<CallRecord> getCallinginCityRecords() {
		return callinginCityRecords;
	}

	public ArrayList<CallRecord> getCallinginProvinceRecords() {
		return callinginProvinceRecords;
	}

	public ArrayList<CallRecord> getCallinginLandRecords() {
		return callinginLandRecords;
	}

	public ArrayList<CallRecord> getAnswennCityRecords() {
		return answennCityRecords;
	}

	public ArrayList<CallRecord> getAnswerinProvinceRecords() {
		return answerinProvinceRecords;
	}

	public ArrayList<CallRecord> getAnswernLandRecords() {
		return answernLandRecords;
	}

	public ArrayList<MessageRecord> getSendMessageRecords() {
		return sendMessageRecords;
	}

	public ArrayList<MessageRecord> getReceiveMessageRecords() {
		return receiveMessageRecords;
	}

	public void addCallinginCityRecords(CallRecord callRecord) {
            callinginCityRecords.add(callRecord);
	}

	public void addCallinginLandRecords(CallRecord callRecord) {
            callinginLandRecords.add(callRecord);
	}

	public void addCallinginProvinceRecords(CallRecord callRecord) {
             callinginProvinceRecords.add(callRecord);
	}

	public void addAnswennCityRecords(CallRecord callRecord) {
             answennCityRecords.add(callRecord);
	}

	public void addAnswerinProvinceRecords(CallRecord callRecord) {
             answerinProvinceRecords.add(callRecord);
	}

	public void addAnswernLandRecords(CallRecord callRecord) {
             answernLandRecords.add(callRecord);
	}

	public void addSendMessageRecords(MessageRecord messageRecord) {
            sendMessageRecords.add(messageRecord);
	}

	public void addReceiveMessageRecords(MessageRecord messageRecord) {
          receiveMessageRecords.add(messageRecord);
	}

}

      最开始看到这类图时并不能马上理解类图想表达的意思,后面经过慢慢的思考明白了这种设计具备的可扩展性,也是后续学习过程中自己需要学习的地方,在用户类User中包含私有属性  用户记录类 useRecords浮点型用户余额 balance计费模式类chargeMode字符串类型电话号码 number,以及计算余额,计算消费的方法。收费模式类是一个抽象类,包含计费方式的集合,以及计费的抽象方法。在后续的扩展中我们可以添加计费模式,就是继承抽象的计费模式类然后实现这些方法,比如座机收费模式,短信收费模式,手机收费模式,而在这些收费模式中可以包括一些计费方式比如市内计费方式,省内计费方式,省外计费方式,在用户记录类当中包括市内拨号记录,市内接听记录,短信发送记录,短信接收记录等等。本题还是要依赖正则表达式处理字符串,利用正则表达式来过滤不符合格式的输入,在一些测试用例中出现由于正则表达式的误判导致无法通过,采用了菜鸟工具中的正则表达式测试,合理的使用工具可以提高实现代码的效率。

      SourceMonitor的生成报表内容

     

     根据报表内容我们可以看出由于类设计的合理性,代码质量较好,主要是实现是导致函数的最大复杂度较高。

电信计费系列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:

image.png
图1

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

image.png

图2

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

image.png
图3

图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
show code
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class Main {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
        Processdata p = new Processdata();
        p.work();
	}

}
abstract class AnswerChargeRule extends ChargeRule {

	 public abstract  double calCost(ArrayList<CallRecord> callrecords); 
}
class AnswerRoamMobilePhoneInCityRule extends AnswerChargeRule {

	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		double cost = 0;
		for(CallRecord c : callrecords) {
			cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.3;
		}
		return cost;
	}

}
abstract class CallChargeRule extends ChargeRule {
    public abstract  double calCost(ArrayList<CallRecord> callrecords); 
    
    
}
class CallRecord extends CommunicationRecord {
     private Date  stateTime;
     private Date  endTime;
     private String callingAddressAreaCode;
     private String answerAddressAreaCode;
     
	public CallRecord( String callingNumber,String answerNumber,Date stateTime, Date endTime, String callingAddressAreaCode, String answerAddressAreaCode) {
		
		super();
		setCallingNumber(callingNumber);
		setAnswerNumber(answerNumber);
		this.stateTime = stateTime;
		this.endTime = endTime;
		this.callingAddressAreaCode = callingAddressAreaCode;
		this.answerAddressAreaCode = answerAddressAreaCode;
	}
	public Date getStateTime() {
		return stateTime;
	}
	public void setStateTime(Date stateTime) {
		this.stateTime = stateTime;
	}
	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 ChargeMode {
	private ArrayList<ChargeRule> chargeRules = new ArrayList<ChargeRule>();

	

	

	public ArrayList<ChargeRule> getChargeRules() {
		return chargeRules;
	}

	public void setChargeRules(ArrayList<ChargeRule> chargeRules) {
		this.chargeRules = chargeRules;
	}

	public abstract double calCost(UserRecords useRecords);

	public abstract double getMonthlyRent();

}
abstract class ChargeRule {
	public  abstract double calCost(ArrayList<CallRecord> callrecords);
}
 abstract class CommunicationRecord {
	protected String callingNumber;
	protected 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 LandlinePhoneCharging extends ChargeMode {
	private double monthlyRent = 20;

	public LandlinePhoneCharging() {
		super();

		getChargeRules().add(new LandPhoneInCityRule());
		getChargeRules().add(new LandPhoneInProvinceRule());
		getChargeRules().add(new LandPhoneInLandRule());
	}

	@Override
	public double calCost(UserRecords useRecords) {
		// TODO 自动生成的方法存根
		double cost= 0;
		cost+= getChargeRules().get(0).calCost(useRecords.getCallinginCityRecords());
		cost+= getChargeRules().get(1).calCost(useRecords.getCallinginProvinceRecords());
		cost+= getChargeRules().get(2).calCost(useRecords.getCallinginLandRecords());
		return cost;

	}

	@Override
	public double getMonthlyRent() {
		// TODO 自动生成的方法存根
		return this.monthlyRent;
	}

}
class LandPhoneInCityRule extends CallChargeRule {
   
	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		double cost = 0;
		for(CallRecord c : callrecords) {
			cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.1;
		}
		return cost;
		
	} 
    

}
class LandPhoneInLandRule extends CallChargeRule {

	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		double cost = 0;
		for(CallRecord c : callrecords) {
			cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.6;
		}
		return cost;
	}



}
class LandPhoneInProvinceRule extends CallChargeRule {

	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		double cost = 0;
		for(CallRecord c : callrecords) {
			cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.3;
		}
		return cost;
	}



}
class MessageRecord extends CommunicationRecord {
	private String message;

	public MessageRecord(String callingNumber, String answerNumber, String message) {
		super();
		setCallingNumber(callingNumber);
		setAnswerNumber(answerNumber);
		this.message = message;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

}
class MobilePhoneCharging extends ChargeMode {
	private double monthlyRent = 15;
	
	
	public MobilePhoneCharging() {
		super();
		getChargeRules().add(new MobilePhoneInCityRule());
		getChargeRules().add(new MobilePhoneInProvinceRule());
		getChargeRules().add(new MobilePhoneInLandRule());
		getChargeRules().add(new RoamMobilePhoneInLandRule());
		getChargeRules().add(new RoamMobilePhoneInProvinceRule());
		getChargeRules().add(new AnswerRoamMobilePhoneInCityRule());
	}

	@Override
	public double calCost(UserRecords useRecords) {
		// TODO 自动生成的方法存根
		double cost= 0;
		cost+= getChargeRules().get(0).calCost(useRecords.getCallinginCityRecords());
		cost+= getChargeRules().get(1).calCost(useRecords.getCallinginProvinceRecords());
		cost+= getChargeRules().get(2).calCost(useRecords.getCallinginLandRecords());
		cost+= getChargeRules().get(3).calCost(useRecords.getRoamCallingInLandRecords());
		cost+= getChargeRules().get(4).calCost(useRecords.getRoamCallingInProvinceRecords());
		cost+= getChargeRules().get(5).calCost(useRecords.getRoamAnswernLandRecords());
		return cost;
	}

	@Override
	public double getMonthlyRent() {
		// TODO 自动生成的方法存根
		return this.monthlyRent;
	}

}

class MobilePhoneInCityRule extends CallChargeRule {

	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		double cost = 0;
		for(CallRecord c : callrecords) {
			cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.1;
		}
		return cost;
	}

}

class MobilePhoneInLandRule extends CallChargeRule {

	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		double cost = 0;
		for(CallRecord c : callrecords) {
			cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.3;
		}
		return cost;
	}

}
class MobilePhoneInProvinceRule extends CallChargeRule {

	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		// TODO 自动生成的方法存根
				double cost = 0;
				for(CallRecord c : callrecords) {
					cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.2;
				}
				return cost;
	}

}

class Processdata {

	public Processdata() {
		super();
		// TODO 自动生成的构造函数存根
	}

	public void work() {
		TreeMap<String, User> users = new TreeMap<>();
		Scanner input = new Scanner(System.in);
		String regex1 = "[u]-0[0-9]{10,11}\\s[0]";
		String regex5 = "[u]-1(\\d){10}\\s[1]";
		String regex2 = "t-0[0-9]{9,11}\\s0[0-9]{9,11}\\s\\d{4}.\\d{1,2}.\\d{1,2}\\s\\d{2}:\\d{2}:\\d{2}\\s\\d{4}.\\d{1,2}.\\d{1,2}\\s\\d{2}:\\d{2}:\\d{2}";
		String regex3 = "t-0[0-9]{9,11}\\s1(\\d){10}\\s(\\d){3,4}\\s\\d{4}.\\d{1,2}.\\d{1,2}\\s\\d{2}:\\d{2}:\\d{2}\\s\\d{4}.\\d{1,2}.\\d{1,2}\\s\\d{2}:\\d{2}:\\d{2}";
		String regex6 = "t-1(\\d){10}\\s(\\d){3,4}\\s0[0-9]{9,11}\\s\\d{4}.\\d{1,2}.\\d{1,2}\\s\\d{2}:\\d{2}:\\d{2}\\s\\d{4}.\\d{1,2}.\\d{1,2}\\s\\d{2}:\\d{2}:\\d{2}";
		String regex4 = "t-1(\\d){10}\\s(\\d){3,4}\\s1(\\d){10}\\s(\\d){3,4}\\s\\d{4}.\\d{1,2}.\\d{1,2}\\s\\d{2}:\\d{2}:\\d{2}\\s\\d{4}.\\d{1,2}.\\d{1,2}\\s\\d{2}:\\d{2}:\\d{2}";
		String s = input.nextLine();
		while (!s.equals("end")) {
			if (s.matches(regex1)) {//座机用户
				String data[] = s.substring(2).split(" ");
				String number = data[0];
				ChargeMode changeMode = new LandlinePhoneCharging();
				User user = new User(changeMode, number);
				users.put(number, user);
//				t-13986300001 0799 079186300001 2022.12.31 23:50:25 2023.1.1 00:05:11

			}
			else if (s.matches(regex5)) {//手机用户
				String data[] = s.substring(2).split(" ");
				String number = data[0];
				ChargeMode changeMode = new MobilePhoneCharging();
				User user = new User(changeMode, number);
				users.put(number, user);
			}
			else if(s.matches(regex6)) {//手机打座机
				String data[] = s.substring(2).split(" ");
				String callingNumber = data[0];
				String callingAddressAreaCode = data[1];
				String answerAddressAreaCode = data[2].substring(0, 4);
				String answerNumber = data[2];
				String stateDate = data[3];
				String stateTime = data[4];
				String endDate = data[5];
				String endTime = data[6];
				String state = stateDate + " " + stateTime;
				String end = endDate + " " + endTime;
				Date statedate = new Date();
				Date enddate = new Date();
				String strDateFormat = "yyyy.MM.dd HH:mm:ss";

				SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
				try {
					statedate = sdf.parse(state);
				} catch (ParseException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}

				try {
					enddate = sdf.parse(end);
				} catch (ParseException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
            if(statedate.before(enddate)) {
            	
            
				CallRecord callRecord = new CallRecord(callingNumber, answerNumber, statedate, enddate,
						callingAddressAreaCode, answerAddressAreaCode);
				Long address1 = Long.parseLong(callRecord.getCallingAddressAreaCode());
				Long address2 = Long.parseLong(callRecord.getAnswerAddressAreaCode());
				
					if (address1 == 791) {
						if(address2==791) {
							users.get(callingNumber).getUserRecords().addCallinginCityRecords(callRecord);
						}
						else if( (address2 == 701 ||address2 >= 790 && address2 <= 799 )) {
							users.get(callingNumber).getUserRecords().addCallinginProvinceRecords(callRecord);
						}
						else {
							users.get(callingNumber).getUserRecords().addCallinginLandRecords(callRecord);
						}
					}

					else if (address1 == 701 || (address1 >= 790 && address1 <= 799 )) {
						users.get(callingNumber).getUserRecords().addRoamCallingInProvinceRecords(callRecord);

					} else {
						users.get(callingNumber).getUserRecords().addRoamCallingInLandRecords(callRecord);
					}
				
            }
			    
			}
			else if (s.matches(regex2)) {//座机打座机
				String data[] = s.substring(2).split(" ");
				String callingAddressAreaCode = data[0].substring(0, 4);
				String answerAddressAreaCode = data[1].substring(0, 4);
				String callingNumber = data[0];
				String answerNumber = data[1].substring(4);
				String stateDate = data[2];
				String stateTime = data[3];
				String endDate = data[4];
				String endTime = data[5];
				String state = stateDate + " " + stateTime;
				String end = endDate + " " + endTime;
				Date statedate = new Date();
				Date enddate = new Date();
				String strDateFormat = "yyyy.MM.dd HH:mm:ss";

				SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
				try {
					statedate = sdf.parse(state);
				} catch (ParseException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}

				try {
					enddate = sdf.parse(end);
				} catch (ParseException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}

				CallRecord callRecord = new CallRecord(callingNumber, answerNumber, statedate, enddate,
						callingAddressAreaCode, answerAddressAreaCode);
				Long address = Long.parseLong(callRecord.getAnswerAddressAreaCode());
//	    		if(callRecord.getCallingNumber().equals(callRecord.getAnswerNumber())) {
//	    			break;
//	    		}
				
					if (address == 791) {
						users.get(callingNumber).getUserRecords().addCallinginCityRecords(callRecord);
					}

					else if (address == 701 || (address >= 790 && address <= 799 )) {
						users.get(callingNumber).getUserRecords().addCallinginProvinceRecords(callRecord);

					} else {
						users.get(callingNumber).getUserRecords().addCallinginLandRecords(callRecord);
					}
				

			} else if (s.matches(regex3)) {//座机打手机
				String data[] = s.substring(2).split(" ");
				String callingNumber = data[0];
				String answerNumber = data[1];
				String callingAddressAreaCode = data[0].substring(0, 4);
				String answerAddressAreaCode = data[2];
				String stateDate = data[3];
				String stateTime = data[4];
				String endDate = data[5];
				String endTime = data[6];
				String state = stateDate + " " + stateTime;
				String end = endDate + " " + endTime;
				Date statedate = new Date();
				Date enddate = new Date();
				String strDateFormat = "yyyy.MM.dd HH:mm:ss";

				SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
				try {
					statedate = sdf.parse(state);
				} catch (ParseException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}

				try {
					enddate = sdf.parse(end);
				} catch (ParseException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
				CallRecord callRecord = new CallRecord(callingNumber, answerNumber, statedate, enddate,
						callingAddressAreaCode, answerAddressAreaCode);
				Long address = Long.parseLong(callRecord.getAnswerAddressAreaCode());
                if(users.get(answerNumber)!=null) {
						if (!(address == 701 || (address >= 790 && address <= 799 ))) {
							users.get(answerNumber).getUserRecords().addRoamAnswernLandRecords(callRecord);
						}
					}
				if (users.get(callingNumber) != null) {
					if (address == 791) {
						users.get(callingNumber).getUserRecords().addCallinginCityRecords(callRecord);
					}

					else if (address == 701 || (address >= 790 && address <= 799)) {
						users.get(callingNumber).getUserRecords().addCallinginProvinceRecords(callRecord);

					} else {
						users.get(callingNumber).getUserRecords().addCallinginLandRecords(callRecord);
						
					}
				}
			} else if (s.matches(regex4)) {//手机打手机
				String data[] = s.substring(2).split(" ");
				String callingNumber = data[0];
				String callingAddressAreaCode = data[1];
				String answerNumber = data[2];
				String answerAddressAreaCode = data[3];
				String stateDate = data[4];
				String stateTime = data[5];
				String endDate = data[6];
				String endTime = data[7];
				String state = stateDate + " " + stateTime;
				String end = endDate + " " + endTime;
				Date statedate = new Date();
				Date enddate = new Date();
				String strDateFormat = "yyyy.MM.dd HH:mm:ss";

				SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
				try {
					statedate = sdf.parse(state);
				} catch (ParseException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}

				try {
					enddate = sdf.parse(end);
				} catch (ParseException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
				if(statedate.before(enddate)) {
					
				
				CallRecord callRecord = new CallRecord(callingNumber, answerNumber, statedate, enddate,
						callingAddressAreaCode, answerAddressAreaCode);
				Long address1 = Long.parseLong(callRecord.getCallingAddressAreaCode());
				Long address2 = Long.parseLong(callRecord.getAnswerAddressAreaCode());
				if(!callingNumber.equals(answerNumber)) {
					if(users.get(answerNumber)!=null) {
						if (!(address2 == 701 || (address2 >= 790 && address2 <= 799 ))) {
							users.get(answerNumber).getUserRecords().addRoamAnswernLandRecords(callRecord);
						}
					}
					if(users.get(callingNumber)!=null) {
						
					
					if (address1 == 791 && address2 == 791) {
						users.get(callingNumber).getUserRecords().addCallinginCityRecords(callRecord);

					} else if (address1 == 791
							&& (address2 == 701 || (address2 >= 790 && address2 <= 799 && address2 != 791))) {
						users.get(callingNumber).getUserRecords().addCallinginProvinceRecords(callRecord);
					} else if (address1 == 791
							&& !(address2 == 701 || (address2 >= 790 && address2 <= 799 && address2 != 791))) {
						users.get(callingNumber).getUserRecords().addCallinginLandRecords(callRecord);
						
					}else if ((address1==701||(address1>= 790 &&address1<=799))) {
						users.get(callingNumber).getUserRecords().addRoamCallingInProvinceRecords(callRecord);
					}
					else if (!(address1==701||(address1>= 790 &&address1<=799))) {
						users.get(callingNumber).getUserRecords().addRoamCallingInLandRecords(callRecord);
					}
					}
				}
					
					 
				}
			}
			

			s = input.nextLine();
		}

		for (User user : users.values()) {
			System.out.printf("%s %.1f %.1f\n",user.getNumber(),user.calCost(),user.calBanlance());
		}
	}
}
class RoamMobilePhoneInLandRule extends CallChargeRule {

	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		double cost = 0;
		for(CallRecord c : callrecords) {
			cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.6;
		}
		return cost;
	}

}

class RoamMobilePhoneInProvinceRule extends CallChargeRule {

	@Override
	public double calCost(ArrayList<CallRecord> callrecords) {
		// TODO 自动生成的方法存根
		double cost = 0;
		for(CallRecord c : callrecords) {
			cost+=Math.ceil((c.getEndTime().getTime()-c.getStateTime().getTime())/1000.0/60.0)*0.3;
		}
		return cost;
	}

}
class User {
	private UserRecords userRecords = new UserRecords();// 用户记录
	private double banlance = 100; // 余额
	private ChargeMode changeMode; // 计费方式
	private String number; // 电话号码
    
	

	

	public User(ChargeMode changeMode, String number) {
		super();
		this.changeMode = changeMode;
		this.number = number;
	}

	public UserRecords getUserRecords() {
		return userRecords;
	}

	public void setUserRecords(UserRecords userRecords) {
		this.userRecords = userRecords;
	}

	public double getBanlance() {
		return banlance;
	}

	public void setBanlance(double banlance) {
		this.banlance = banlance;
	}

	public ChargeMode getChangeMode() {
		return changeMode;
	}

	public void setChangeMode(ChargeMode changeMode) {
		this.changeMode = changeMode;
	}

	public String getNumber() {
		return number;
	}

	public void setNumber(String number) {
		this.number = number;
	}

	public double calBanlance() {
		return this.banlance-changeMode.calCost(userRecords)-changeMode.getMonthlyRent();

	}

	public double calCost() {
		return changeMode.calCost(userRecords);

	}
}

class UserRecords {
    
	public UserRecords() {
		super();
		// TODO 自动生成的构造函数存根
	}

	private ArrayList<CallRecord> callinginCityRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> callinginProvinceRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> callinginLandRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> answerinCityRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> answerinProvinceRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> answerinLandRecords = new ArrayList<CallRecord>();
	private ArrayList<MessageRecord> sendMessageRecords = new ArrayList<MessageRecord>();
	private ArrayList<MessageRecord> receiveMessageRecords = new ArrayList<MessageRecord>();
    private ArrayList<CallRecord> roamcallinginProvinceRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> roamcallinginLandRecords = new ArrayList<CallRecord>();
	private ArrayList<CallRecord> roamanswernLandRecords = new ArrayList<CallRecord>();
	public ArrayList<CallRecord> getCallinginCityRecords() {
		return callinginCityRecords;
	}

	public ArrayList<CallRecord> getCallinginProvinceRecords() {
		return callinginProvinceRecords;
	}

	public ArrayList<CallRecord> getCallinginLandRecords() {
		return callinginLandRecords;
	}

	public ArrayList<CallRecord> getAnswerinCityRecords() {
		return answerinCityRecords;
	}

	public ArrayList<CallRecord> getAnswerinProvinceRecords() {
		return answerinProvinceRecords;
	}

	public ArrayList<CallRecord> getAnswerinLandRecords() {
		return answerinLandRecords;
	}

	public ArrayList<MessageRecord> getSendMessageRecords() {
		return sendMessageRecords;
	}

	public ArrayList<MessageRecord> getReceiveMessageRecords() {
		return receiveMessageRecords;
	}
	
	public ArrayList<CallRecord> getRoamCallingInProvinceRecords() {
	     return roamcallinginProvinceRecords;
	}
	
	public ArrayList<CallRecord>  getRoamCallingInLandRecords() {
		return roamcallinginLandRecords;
	}
	
	public ArrayList<CallRecord> getRoamAnswernLandRecords(){
		return roamanswernLandRecords;
	}
	

	public void addCallinginCityRecords(CallRecord callRecord) {
            callinginCityRecords.add(callRecord);
	}

	public void addCallinginLandRecords(CallRecord callRecord) {
            callinginLandRecords.add(callRecord);
	}

	public void addCallinginProvinceRecords(CallRecord callRecord) {
             callinginProvinceRecords.add(callRecord);
	}

	public void addAnswennCityRecords(CallRecord callRecord) {
             answerinCityRecords.add(callRecord);
	}

	public void addAnswerinProvinceRecords(CallRecord callRecord) {
             answerinProvinceRecords.add(callRecord);
	}

	public void addAnswernLandRecords(CallRecord callRecord) {
             answerinLandRecords.add(callRecord);
	}

	public void addSendMessageRecords(MessageRecord messageRecord) {
            sendMessageRecords.add(messageRecord);
	}

	public void addReceiveMessageRecords(MessageRecord messageRecord) {
          receiveMessageRecords.add(messageRecord);
	}
	public void addRoamCallingInProvinceRecords(CallRecord callRecord) {
        roamcallinginProvinceRecords.add(callRecord);
	}
	public void addRoamCallingInLandRecords(CallRecord callRecord) {
        roamcallinginLandRecords.add(callRecord);
	}
	public void addRoamAnswernLandRecords(CallRecord callRecord) {
        roamanswernLandRecords.add(callRecord);
	}

}

  本系列添加了手机用户的计费方式,在手机用户的计费方式中添加了漫游的计费方式,漫游指的是在南昌市的用户,在外地拨打电话或者接听电话等,在处理这种的通讯机录主要是区号,根据区号来判断位置,其他的与座机的收费模式类似。

  SourceMonitor的生成报表内容

   

    根据报表的内容可知,函数的平均深度,最大深度,以及最大复杂度也较大。

电信计费系列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:

image.png

图1

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

image.png

图2

    图2中CommunicationRecord是抽象的通讯记录类:
包含callingNumber拨打号码、answerNumber接听号码两个属性。
CallRecord(通话记录)、MessageRecord(短信记录)是它的子类。
 

image.png

图3
图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
show code

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import java.text.ParseException;
public class Main {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
        Processdata p = new Processdata();
        p.work();
	}

}
 abstract class ChargeMode {
	private ArrayList<ChargeRule> chargeRules = new ArrayList<ChargeRule>();

	public ArrayList<ChargeRule> getChargeRules() {
		return chargeRules;
	}

	public void setChargeRules(ArrayList<ChargeRule> chargeRules) {
		this.chargeRules = chargeRules;
	}

	public abstract double calCost(UserRecords useRecords);



}

abstract class ChargeRule {
	public  abstract double calCost(ArrayList<MessageRecord> messageRecord);
}
abstract class MessageChargeRule extends ChargeRule {

	public abstract  double calCost(ArrayList<MessageRecord> messageRecord);
	

}class MessageRecord extends CommunicationRecord {
	private String message;

	public MessageRecord(String callingNumber, String answerNumber, String message) {
		super();
		setCallingNumber(callingNumber);
		setAnswerNumber(answerNumber);
		this.message = message;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

}
class MessageRule extends MessageChargeRule{

	@Override
	public double calCost(ArrayList<MessageRecord> messageRecord) {
		// TODO 自动生成的方法存根
		double cost = 0;
		int sum = 0;
		for(MessageRecord message :messageRecord) {
			 sum += (int)Math.ceil(message.getMessage().length()/10.0);
		}
		if(sum<=3) {
			cost=sum*0.1;
		}
		else if(sum<=5) {
			cost=0.3+(sum-3)*0.2;
		}
		else {
			cost=0.7+(sum-5)*0.3;
		}
		return cost;
		
	}

	public MessageRule() {
		super();
		// TODO 自动生成的构造函数存根
	}
      
}
class PhoneMessageCharging extends ChargeMode {
	
	public PhoneMessageCharging() {
		super();
		// TODO 自动生成的构造函数存根
		getChargeRules().add(new MessageRule());
		
	}

	@Override
	public double calCost(UserRecords useRecords) {
		// TODO 自动生成的方法存根
		return getChargeRules().get(0).calCost(useRecords.getSendMessageRecords());
	}

	

}
class Processdata {

	public Processdata() {
		super();
		// TODO 自动生成的构造函数存根
	}

	public void work() {
		TreeMap<String, User> users = new TreeMap<>();
	     Scanner input = new Scanner (System.in);
		String regex1 = "[u]-1[0-9]{10}\\s[3]";
		String regex2 = "[m]-1(\\d){10}\\s1(\\d){10}\\s[A-Za-z0-9,. ]{1,}";
		String s = input.nextLine();
		while(!s.equals("end")) {
			
		
		if(s.matches(regex1)) {
			String data[] = s.substring(2).split(" ");
			String number = data[0];
			ChargeMode changeMode = new PhoneMessageCharging();
			User user = new User(changeMode, number);
			users.put(number, user);
		}
		else if(s.matches(regex2)) {
			    String s1 = s.substring(0, 25);
			
				String message = s.substring(26);
				String data[] = s1.substring(2).split(" ");
				String callingNumber = data[0];
				
			
				String answerNumber = data[1];
			
				MessageRecord messageRecord = new MessageRecord(callingNumber, answerNumber, message);
				users.get(callingNumber).getUserRecords().addSendMessageRecords(messageRecord);
				
				
			}
		s=input.nextLine();
		}
	
		
		for (User user : users.values()) {
			System.out.printf("%s %.1f %.1f\n",user.getNumber(),user.calCost(),user.calBanlance());
		}
		}
	
}class User {
	private UserRecords userRecords = new UserRecords();// 用户记录
	private double banlance = 100; // 余额
	private ChargeMode changeMode; // 计费方式
	private String number; // 电话号码
    
	

	

	public User(ChargeMode changeMode, String number) {
		super();
		this.changeMode = changeMode;
		this.number = number;
	}

	public UserRecords getUserRecords() {
		return userRecords;
	}

	public void setUserRecords(UserRecords userRecords) {
		this.userRecords = userRecords;
	}

	public double getBanlance() {
		return banlance;
	}

	public void setBanlance(double banlance) {
		this.banlance = banlance;
	}

	public ChargeMode getChangeMode() {
		return changeMode;
	}

	public void setChangeMode(ChargeMode changeMode) {
		this.changeMode = changeMode;
	}

	public String getNumber() {
		return number;
	}

	public void setNumber(String number) {
		this.number = number;
	}

	public double calBanlance() {
		return this.banlance-changeMode.calCost(userRecords);

	}

	public double calCost() {
		return changeMode.calCost(userRecords);

	}
}
class UserRecords {
    
	public UserRecords() {
		super();
		// TODO 自动生成的构造函数存根
	}


	private ArrayList<MessageRecord> sendMessageRecords = new ArrayList<MessageRecord>();

	public ArrayList<MessageRecord> getSendMessageRecords() {
		return sendMessageRecords;
	}



	public void addSendMessageRecords(MessageRecord messageRecord) {
            sendMessageRecords.add(messageRecord);
	}

	

}
abstract class CommunicationRecord {
	protected String callingNumber;
	protected 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;
	}

}

本系列中添加短信计费模式较简单,在上次的基础上容易实现。

PowerDesigner生成类图:

SourceMonitor的生成报表内容

踩坑心得

  • 电信计费系列1-座机计费:


      问题1 :未考虑到未开户的用户产生的通讯记录

      解决方案:添加判断条件(若用户集合中不含拨号号码则跳过)

 if(users.get(callingNumber) != null)

     问题2:未考虑到一些特殊地区的区号为三位数导致正则表达式出现误判

     解决方案: 更改正则表达式,将对区号的限制改为(d\\){3,4}

   

  • 电信计费系列2-手机+座机计费:


      问题1:未考虑到座机用户给省外手机用户打长途电话时手机用户应收漫游接听费

      解决方案:添加对应的情况

 

  • 电信计费系列3-短信计费:


      问题1: 对于用户发出短信内容的字数统计后计费出现问题导致收费异常

      解决方案:

     

public double calCost(ArrayList<MessageRecord> messageRecord) {
		// TODO 自动生成的方法存根
		double cost = 0;
		int sum = 0;
		for(MessageRecord message :messageRecord) {
			 sum += (int)Math.ceil(message.getMessage().length()/10.0);
		}
		if(sum<=3) {
			cost=sum*0.1;
		}
		else if(sum<=5) {
			cost=0.3+(sum-3)*0.2;
		}
		else {
			cost=0.7+(sum-5)*0.3;
		}
		return cost;
		
	}

改进建议:


  • 短信计费模式应该并入手机用户的计费模式中,相当于手机套餐收费方式
  • 对于用户集合采用TreeMap
  • 可以避免在正则表达式中校验日期,校验时间,另加一个日期格式的判断类减短正则表达式,增加正则表达式的可读性

总结:


     在这几周最大的收获就是了解了许多设计模式,也明白了设计模式的重要性, 设计模式是一套经过反复使用的代码设计经验,目的是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 设计模式于己于人于系统都是多赢的,它使得代码编写真正工程化,它是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。总体来说,设计模式分为三大类:
    创建型模式:共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
    结构型模式:共7种:适配器模式、装饰器模式、代理模式、桥接模式、外观模式、组合模式、享元模式
    行为型模式:共11种:策略模式、模板方法模式、观察者模式、责任链模式、访问者模式、中介者模式、迭代器模式、命令模式、状态模式、备忘录模式、解释器模式
 在这几周我们利用策略模式设计了酒店客房的预定系统,明白了策略模式的优点在于可以动态改变对象的行为;但缺点是会产生很多策略类,并且策略模式的决定权在用户,系统只是提供不同算法的实现,所以客户端必须知道所有的策略类,并自行决定使用哪一个策略类; 

 策略模式适用用于以下几种场景:

(1)应用程序需要实现特定的功能服务,而该程序有多种实现方式使用,所以需要动态地在几种算法中选择一种
(2)一个类定义了多种行为算法,并且这些行为在类的操作中以多个条件语句的形式出现,就可以将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

 

我们可以在这个链接中详细了解一下策略模式:https://blog.csdn.net/a745233700/article/details/83661703

在后续的学习中多了解设计模式并将其用于代码中提高代码质量

posted @ 2022-06-16 18:34  贶怡  阅读(126)  评论(0)    收藏  举报