前言:
通过了前几次的大作业的洗礼后,来到这一次的大作业,感觉还是比较简单的。没有什么无从下手的地方,仿照着PTA给出的类图很快就有个初步模板了,后面自己慢慢调试就可以解决大部分问题了。
在后续的学习中又接触到了面向对象的七大原则,我接下来会总结一下作业中也会用到并且自我感觉比较重要的原则给出自己的理解。
关于面向对象的原则
面向对象有着七大基本原则,分别是开闭原则,单一职责原则,里氏代换原则,迪米特原则,接口隔离原则,依赖倒置原则和合成复用原则。接下来给我出认为最重要的两个原则,也就是前两个原则。
开闭原则:
这里给出的开闭并不是所谓门的开关闭合,而是对扩展开放,对修改闭合。对模块的功能进行开放,需要增加功能只需要新增加一个新的类去接上,不需要去修改已经完成的类的代码。根据前面的讲解可以得知,面向对象的可复用设计的基石,其他设计原则是实现开闭原则的手段和工具。
单一职责原则:
单一职责的职责可以就简单理解为行为,类是一个物体,物体的行为分为不同的职责,将不同的职责分开装入不同的类之中,负责所单一的职责来实现单一职责原则。这样做的好处有:类的复杂度降低,可读性提高,可维护性提高,扩展性提高,降低变更引起的风险。
这两种原则在接下来的PTA大作业中给出的类图中都有针对性的进行设计。
PTA中遇到的bug和问题
此处就类举一下写作业中碰到的问题与bug
PTA6-8(电信计费):
1.精度问题。
2.计费方式错误计算导致的错误。
3.类的错误或遗漏设计导致的错误。
4.对电话号码进行matches错误设计导致的错误。
总结: bug来源主要来自以下四点: 1.对计费方式理解的欠缺,一开始没有理解到一分钟多1s也算作2分钟。 2.考虑不周到,很多情况没有考虑到。 3.自己对题目的观察理解不够透彻,导致健壮性的不完善。 4.不小心写错。
代码分析
(代码量太大这里不给出全代码,部分题目给出关键的代码,只给出类图和耦合度以及对题目的分析)
PTA67-1 电信计费系列1-座机计费
类图:
耦合度:
代码:
在给出类图的类后,新增加了判断类,来对输入数据进行判断以及接收。
class InputDeal {
public int checkInput(String s)
{
int returnNum = 0;
String[] input = s.split(" ");
//输入u-xxxx的情况
if(input.length == 2) {
if (input[0].matches("^u-[0-9]{11,13}$")) {
Integer r = Integer.parseInt(input[1]);
if(r>=0 && r<=2)
returnNum = 1;
}
}
//输入t-xxxx的情况
else if(input.length == 6) {
if(judgeMent_t(input))
returnNum = 2;
}
return returnNum;
}
public boolean judgeMent_t(String[] input) {
if (valiDateDate(input[2]))
if (valiDateDate(input[4]))
if (valiDateTime(input[3]))
if (valiDateTime(input[5]))
if (input[0].matches("[t]-0791[0-9]{7,8}"))
if (input[1].matches(".[0-9]{9,11}")) {
return true;
}
return false;
}
public boolean valiDateTime(String s) {
if( s.matches("^([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$") ) {
return true;
}
return false;
}
public boolean valiDateDate(String s) {
if(!s.matches("\\d{4}+[\\.]\\d{1,2}+[\\.]\\d{1,2}"))
return false;
String[] ss = s.split("\\.");
//分出年月日
int year = Integer.parseInt(ss[0]);
int month = Integer.parseInt(ss[1]);
int day = Integer.parseInt(ss[2]);
if(month<1 && month>12)
return false;
int[] monthArray = new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31};
if( isRunNian(year) )
monthArray[3] = 29;
int monthLength = monthArray[month];
if (day < 1 || day > monthLength) {
return false;
}
return true;
}
/**
* @判断是否是闰年(是返回true,否返回false)
*/
public boolean isRunNian(int year) {
return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}
/**
* @输入用户信息
*/
public void InputUser(ArrayList<User> users,String s) {
//u-079186300001 0
User newUser = new User();
String[] input = s.split(" ");
newUser.setNumber( input[0].substring(2) );
//检查是否有号码相同,相同则结束本次输入
for( User user : users) {
if( user.getNumber().equals(newUser.getNumber()) )
return;
}
int mode = Integer.parseInt( input[1] );
//选择计费类型
switch (mode) {
case 0 : newUser.setChargeMode( new LandlinePhoneCharging() );break;
default :
}
users.add(newUser);
}
/**
* @输入用户的通讯信息
*/
public void InputRecord(ArrayList<User> users,String s) {
//t-079186330022 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:11
String[] input = s.split(" ");
String callingNumber = input[0].substring(2);
String answerNumber = input[1];
// if(!callingNumber.matches("[079[0-9]|0701]+[0-9]{7,8}"))
// return;
CallRecord callRecord = new CallRecord(input);
User calus = null;
User ansus = null;
//选择正确的用户
for( User us : users ) {
if( us.getNumber().equals(callingNumber) )
calus = us;
if( us.getNumber().equals(answerNumber) )
ansus = us;
if(calus!=null && ansus!=null)
break;
}
//打电话用户记录
if (calus != null) {
if (callRecord.getCallType() == 1)
calus.getUserRecords().addCallingInCityRecords(callRecord);
else if (callRecord.getCallType() == 2)
calus.getUserRecords().addCallingInProvinceRecords(callRecord);
else
calus.getUserRecords().addCallingInLandRecords(callRecord);
}
//接听用户记录
if (ansus != null) {
if (callRecord.getCallType() == 1)
ansus.getUserRecords().addAnswerInCityRecords(callRecord);
else if (callRecord.getCallType() == 2)
ansus.getUserRecords().addAnswerInProvinceRecords(callRecord);
else
ansus.getUserRecords().addAnswerInLandRecords(callRecord);
}
}
}
PTA77-1 电信计费系列2-手机+座机计费
类图:
耦合度:
代码:
对输入类检测输入的修改
public int checkInput(String s)
{
if (s.matches("[u]-0791[0-9]{7,8}\\s[0]") || s.matches("[u]-1[0-9]{10}\\s[1]")) {
return 1;
} else if (s.matches("(([t]-0791[0-9]{7,8}\\s" + "0[0-9]{9,11}\\s)|"
+ "([t]-0791[0-9]{7,8}\\s" + "1[0-9]{10}\\s" + "0[0-9]{2,3}\\s)|"
+ "([t]-1[0-9]{10}\\s" + "0[0-9]{2,3}\\s" + "0[0-9]{9,11}\\s)|"
+ "([t]-1[0-9]{10}\\s" + "0[0-9]{2,3}\\s" + "1[0-9]{10}\\s" + "0[0-9]{2,3}\\s))"
+ "((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]|[0-9][1-9][0-9]{2}|[1-9][0-9]{3})\\.(((0?[13578]|1[02])\\.(0?"
+ "[1-9]|[12][0-9]|3[01]))|(([469]|11)\\.([1-9]|[12][0-9]|30))|(2\\.([1-9]|[1][0-9]|2[0-8]))))|((("
+ "[0-9]{2})([48]|[2468][048]|[13579][26])|(([48]|[2468][048]|[3579][26])00))\\.2\\.29))"
+ "\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])\\s"
+ "((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]|[0-9][1-9][0-9]{2}|[1-9][0-9]{3})\\.((([13578]|1[02])\\.("
+ "[1-9]|[12][0-9]|3[01]))|(([469]|11)\\.([1-9]|[12][0-9]|30))|(2\\.([1-9]|[1][0-9]|2[0-8]))))|((("
+ "[0-9]{2})([48]|[2468][048]|[13579][26])|(([48]|[2468][048]|[3579][26])00))\\.2\\.29))"
+ "\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])")) {
return 2;
}
return 0;
}
这里由于需要考虑的情况有些多,所以直接照搬了网上的代码。
直接在CallRecord中加入的判断语句
/**
* @通话记录类
* @父类:通讯记录抽象类CommunicationRecord
*/
class CallRecord extends CommunicationRecord {
private Date startTime; //通话的起始时间
private Date endTime; //通话的结束时间
private String callingAddressAreaCode; //拨号地点的区号
private String answerAddressAreaCode; //接听地点的区号
public CallRecord() {
}
public CallRecord(String[] input) {
super();
char type = input[0].charAt(0);
input[0] = input[0].substring(2);
String d1=null,t1=null,d2=null,t2=null;
if(type == 't')
switch ( input.length ) {
case 6: d1=input[2];t1=input[3];d2=input[4];t2=input[5];
callingAddressAreaCode = input[0].substring(0,4);
answerAddressAreaCode = input[1].substring(0,4);
break;
case 7: d1=input[3];t1=input[4];d2=input[5];t2=input[6];
if(input[0].charAt(0) != '0') {
if(input[2].length() == 10) {
answerAddressAreaCode = input[2].substring(0,3);
} else {
answerAddressAreaCode = input[2].substring(0,4);
}
callingAddressAreaCode = input[1];
}
else {
if(input[0].length() == 10) {
callingAddressAreaCode = input[0].substring(0,3);
} else {
callingAddressAreaCode = input[0].substring(0,4);
}
answerAddressAreaCode = input[2];
}
break;
case 8: d1=input[4];t1=input[5];d2=input[6];t2=input[7];
callingAddressAreaCode = input[1];
answerAddressAreaCode = input[3];
break;
default : break;
}
else if(type=='m'){
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.getDefault());
try {
startTime = simpleDateFormat.parse(d1 + " " + t1);
endTime = simpleDateFormat.parse(d2 + " " + t2);
} catch (ParseException e) {
}
}
/**
* @return String 前面表示拨打电话(1表示市内,2表示省内,3表示省外)后面表示接听电话(1表示市内,2表示省内,3表示省外)
*/
public String getCallType() {
// System.out.println(callingAddressAreaCode+" "+answerAddressAreaCode);
String s = "";
if (callingAddressAreaCode.equals("0791")) {
s = s.concat("1");
}
else if (callingAddressAreaCode.matches("^079[023456789]$") || callingAddressAreaCode.equals("0701")) {
s = s.concat("2");
}
else {
s = s.concat("3");
}
if (answerAddressAreaCode.equals("0791")) {
s = s.concat("1");
}
else if (answerAddressAreaCode.matches("^079[023456789]$") || answerAddressAreaCode.equals("0701")) {
s = s.concat("2");
}
else {
s = s.concat("3");
}
return s;
}
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;
}
/**
* @return double类型的通话时长(单位s)
*/
public double getTalkTime() {
return (endTime.getTime() - startTime.getTime())/1000;
}
}
PTA8:
7-1 电信计费系列3-短信计费
类图:
耦合度:
因为每道题目所需要更改的部分基本都一至,按照要求进行相应的修改即可,并且最后一次很简单,这边也不给出代码了
对电信计费系列的分析: 这次电信计费系列还是很简单的,按照给定的类图进行相应的设计后即可实现大体上的功能,需要注意的地点一共有三点,一个就是输入的检测,检测是User还是被打电话的人,其中的判断在PTA中占有的分值也是挺高的。第二点就是在设计计费方式的方面,要注意到多1s多记1分钟的。最后一点其实是在检测计费方式的方面,以为t-后的电话类型可能分很多种,也就是要考虑自己的位置和对面的位置所相对应出相应的计费方式,这一点非常重要,其中我也因为考虑不周到踩到了不少的坑。总之这次pta只要将这几点考虑周到应该是没什么太大问题的,将第一次的pta6设计完后,后面几次只要做相应的修改还是比较简单的。总结与感想:
三次大作业虽然难度不大,但也让我学到了很多。从自己硬写高复杂度的烂代码,到从类图中提取有效信息,按照类图来写高质量代码,并由此更深刻地体会到面向对象的魅力、面向对象编程所带来的简洁、代码修改的方便,java的学习让我编程时的思路更加清晰、明朗。期末考试的顺利结束,意味着这个学期的java学习正式告一段落。这是本学期的最后一篇java博客,也是java课的最后一个任务。我在这个学期的java学习过程中,遇到了大大小小的困难,被一次次地搞心态,但是最后还是挺过来了。学习嘛,不遇到几次困难你永远不知道自己几斤几两,也永远不会进步。java课程的结束,并不意味着java学习的结束。技术发展日新月异,我们在短短一学期内所学到的,永远只是java宇宙的冰山一角。其它的学科也一样。学习也不能局限于学校里学的知识。学习不是一蹴而就的过程,更要的是博识多学、坚持不懈。千淘万漉虽辛苦,吹尽狂沙始到金。人生也是这样,克服所遇到的困难,让自己闪现出金子般的光芒!最后,在此感谢老师同学们对我java学习所提供的帮助!