百木园-与人分享,
就是让自己快乐。

第十到第十六周—— BLOG_3

  面对对象程序设计---第十到十六周作业总结

 引言:java的课程进入尾声,但是编程的路才刚开始。

前言:这三周的大作业主要考察各个类之间的关系,数据的封装,类的继承,多态,接口,抽象类,集合框架等多个知识的综合运用。

   自学正则表达式的使用,有些题目对于格式的判断非常的复杂,使用正则表达有效判断了输入的合法性,并且减少了大量的格式判断代码。

        通过三次大作业写完电信计费问题,这样的方式相比之前的点线型友好了太多,这样就又充分的时间去书写代码,完成代码的内容。

   三次大作业彼此关联难度又呈持平的状态,给我们编码时提供了莫大的信心。

 PTA大作业6

  各题目 设计与分析 踩坑心得 改进意见 核心代码分析:

  (1)7-1 电信计费系列1-座机计费

设计与分析:

类图如下:

  

 

 

 

 

 

 

 

 

 

  此题的难度在于不好下手,做此类图较为复杂的题,需要仔细观察类图,明确类图之间的联系

  在此题中 User类中的userRecord是这道题目的突破口,在这个userRecord中可以把用户的通话记录数据放入,便于Chargemode在中间的调用。

  然后再去理解类图中的chargeMode类,这是这个程序的核心代码,通过这一段计算出用户的花费,完成程序。

  虽然类图看起来非常的复杂,但只要将代码分解,先从user入手,自然就写到了userRecord,接着就补充到chargemode,再然后就去构思main函数,获取输入数据即可

踩坑心得:

 

 这应该是我最重要的一部分代码,看起来非常的简单,但得来是十分不容易的。

这道题目涉及到对arrayList排序的问题。我开始是先声明一个User user[]的对象数组,先历便arrayList,将数据赋值给user[],

再用compareTo方法对字符串进行冒泡排序,但是排序之后就出现了一个问题,我通过user数组赋值没有办法删除重复的元素,这就导致输出重复。

虽然最后解决了重复的问题,但是这种方法效率比较低,我又找到了以上方法。

在把数据比较入arrayList时进行,如果arratList的user.numble值比后一个要大,那么用j记录下arrayList中的位置,再用指定位置插入,将新进来的数字插入进来,这样插入完成后就已经是有序的了。

 

改进意见:

  1.由上图可知,这个代码的圈复杂度达到了19,这说明代码的质量不高。因为我在写题目的过程中,只注意需要完成题目要求的功能,而没有对代码进行优化,导致写了过多的if/else语句,据查找资料,if/else 和循环的使用会使得圈复杂度提高,改进的方法是将if/else语句换成switch语句,可以有效减少圈复杂度。

  2.可以将判断的逻辑颠倒过来,这样代码的思路就会更加的清晰,代码的可读性也会更高。

  3.可以将arrayList换成hashset,用集合简化题目。

核心代码分析:

① 数据判断的正则表达式:

        final String regex = \"^u-[0-9]{11,12} 0$\";
        final String regex0 = \"^t-[0]{1}[0-9]{9,11} [0]{1}[0-9]{9,11} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$\";
        final String regex00 = \"^079[0-9]$\";            

②arrayList的排序算法:

while(!s.equals(\"end\")) {
            if(Pattern.matches(regex, s)) 
            {
                String s0 = s.substring(2);
            String str[] = s0.split(\"[ ]\");
                int flag=0;
            User user = new User();
            user.setNumber(str[0]);
            for(User i:list){
                if(i.getNumber().equals(user.getNumber()))
                    flag=1;
            }
            int j=0;
            for(User m:list) {
                if(m.getNumber().compareTo(user.getNumber())>0)
                    break;
                j++;
            }
                if(flag==0){
            
                        list.add(j,user);
                
                }

            
            }        

 

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

 各题目 设计与分析 踩坑心得 改进意见 核心代码分析:

  (1)7-2 串口字符解析 

设计与分析:

类图如下(其他与上一题基本相同)

 

 

 

 

 此题的难点在于对于手机来说:拨打电话和接听电话都需要计费,不但需要判断是拨打还是接听,还需要判断所在的位置。

对于接听来说,所在的位置如果发生变化,相应的计费金额也会有所改变。

核心就来到了判断输入是否合法,输入的时间是否合法等问题,其意思是说如过无法判断输入数据的准确与否,就无法写出这道题目。

我同样采取了正则表达式判断时间,对于本题给出的格式yyyy-MM-dd HH:mm:ss。我写了一个简易的时间正则表达式。

在获得输入的数据后需要了解计费规则的相关类,

这些类的核心方法是: calCost(ArrayList<CallRecord> callRecords)。 该方法针根据输入参数callRecords中的所有记录计算某用户的某一项费用;

如市话费。 输入参数callRecords的约束条件:必须是某一个用户的符合计费规则要求的所有记录。 SendMessageRule是发送短信的计费规则类,用于计算发送短信的费用。

 

踩坑心得:

 

 

 这一部分的代码就是五个部分的判断,对于题目给出的五个地区不同的区号判断。

只需要从地区范围由小到大判断,就不会遗漏数据了。

改进意见:

1、由上图分析,这道题的圈复杂度为70,超过了15,这表明代码的质量还可以提高,这道题是由于写了很多个循环的结果导致。其实有些循环可以把他合并起来,这样既减少了代码的行数,又提高了代码的质量。

2、这道题存在有些代码重复率过高的问题,可以通过静态方法的调用,解决这些代码的重复问题。

核心代码分析:

①时间的正则表达式:

final String landtel = \"t-[0]{1}[0-9]{9,11} 1[0-9]{10} [0-9]{3,4} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$\";
        // 座机--电话
final String teltel = \"t-1[0-9]{10} [0-9]{3,4} 1[0-9]{10} [0-9]{3,4} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$\";
final String telland = \"t-1[0-9]{10} [0-9]{3,4} 0[0-9]{9,11} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$\";
    

②arrayList的使用:

if (Pattern.matches(regextel, s)) {
                    String s0 = s.substring(2);
                    String str[] = s0.split(\"[ ]\");
                    int flag = 0;
                    User user = new User();
                    user.setNumber(str[0]);
                    for (User i : listtel) {
                        if (i.getNumber().equals(user.getNumber()))
                            flag = 1;
                    }
                    int j = 0;
                    for (User m : listtel) {
                        if (m.getNumber().compareTo(user.getNumber()) > 0)
                            break;
                        j++;
                    }
                    if (flag == 0) {
                        listtel.add(j, user);
                    }
                }

            } else if (Pattern.matches(regex0, s) || Pattern.matches(landtel, s)) {// 座机通话记录 座机打座机 // 增加座机打手机
                if (Pattern.matches(regex0, s)) {
                    UserRecords ue = new UserRecords();
                    CallRecord callRecord = new CallRecord();
                    String str[] = s.split(\"[ ]\");
                    String d1 = str[2] + \" \" + str[3];
                    String d2 = str[4] + \" \" + str[5];
                    Date da1 = new SimpleDateFormat(\"yyyy.MM.dd HH:mm:ss\").parse(d1);
                    Date da2 = new SimpleDateFormat(\"yyyy.MM.dd HH:mm:ss\").parse(d2);
                    callRecord.setCallingNumber(str[0].substring(2));
                    callRecord.setCallingAddressAreaCode(str[0].substring(2, 6));
                    callRecord.setAnswerNumber(str[1]);
                    callRecord.setAnswerAddressAreaCode(str[1].substring(0, 4));
                    callRecord.setStartTime(da1);
                    callRecord.setEndTime(da2);
                    for (User j : list) {
                        if (j.getNumber().equals(callRecord.getCallingNumber())) {
                            ue = j.getUserRecords();
                        } else
                            continue;
                        if (callRecord.getAnswerAddressAreaCode().equals(callRecord.getCallingAddressAreaCode())) { // 判断地区
                            ue.addCallingInCityRecords(callRecord);
                        } else if (callRecord.getAnswerAddressAreaCode().equals(\"0701\")
                                || Pattern.matches(regex00, callRecord.getAnswerAddressAreaCode())) {
                            ue.addCallingInProvinceRecords(callRecord);
                        } else
                            ue.addCallingInLandRecords(callRecord);
                        j.setUserRecords(ue);
                        break;
                    }
                }
                if (Pattern.matches(landtel, s)) {
                    UserRecords ue = new UserRecords();
                    UserRecords ue0 = new UserRecords();
                    CallRecord callRecord = new CallRecord();
                    s = s.substring(2);
                    String str[] = s.split(\"[ ]\");
                    String d1 = str[3] + \" \" + str[4];
                    String d2 = str[5] + \" \" + str[6];
                    Date da1 = new SimpleDateFormat(\"yyyy.MM.dd HH:mm:ss\").parse(d1);
                    Date da2 = new SimpleDateFormat(\"yyyy.MM.dd HH:mm:ss\").parse(d2);
                    callRecord.setCallingNumber(str[0]);
                    callRecord.setCallingAddressAreaCode(str[0].substring(0,4));
                    callRecord.setAnswerNumber(str[1]);
                    callRecord.setAnswerAddressAreaCode(str[2]);
                    callRecord.setStartTime(da1);
                    callRecord.setEndTime(da2);
                    // 分区域放进去
                    for (User j : list) {
                        if (j.getNumber().equals(callRecord.getCallingNumber())) {
                            ue = j.getUserRecords();
                        } else continue;
                            
                        if (callRecord.getAnswerAddressAreaCode().equals(callRecord.getCallingAddressAreaCode())) { // 判断地区
                            ue.addCallingInCityRecords(callRecord);
                        } else if (callRecord.getAnswerAddressAreaCode().equals(\"0701\")
                                || Pattern.matches(regex00, callRecord.getAnswerAddressAreaCode())) {
                            ue.addCallingInProvinceRecords(callRecord);
                        } else
                            ue.addCallingInLandRecords(callRecord);
                        j.setUserRecords(ue);
                        break;
                    }

 

7-1 电信计费系列3-短信计费

各题目 设计与分析 踩坑心得 改进意见 核心代码分析:

(1)7—1 计算两点间的距离

设计与分析:

 

 

 

这题只需要用一个String的字符串去接收题目中的输入,再使用String中的方法spilt将字符串进行拆分

本题的难度在于判断输入的短信字符段,同意采用正则表达的,在计算字符串的长度就可以了。

 踩坑心得:

 

 实现这道题目主要就是截取到短信,在使用之前类似的方法就可以

改进建议:

1、由图可知,该题的圈复杂度为17。这里有点可惜,如果小于10代码质量就会很好。改进可以减少循环的使用。

2、这题可以将获取字符串变成一个字符数组。、此题可以将split方法改进,提高程序的执行效率

核心代码分析:

①正则表达式:

final String regex = \"^u-[0-9]{11,12} 0$\";// 座机开户u-[] 0;正则
        final String regex0 = \"^t-[0]{1}[0-9]{9,11} [0]{1}[0-9]{9,11} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$\";
        // 座机拨打电话格式正则
        final String landtel = \"t-[0]{1}[0-9]{9,11} 1[0-9]{10} [0-9]{3,4} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$\";
        // 座机--电话
        final String teltel = \"t-1[0-9]{10} [0-9]{3,4} 1[0-9]{10} [0-9]{3,4} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$\";
        final String telland = \"t-1[0-9]{10} [0-9]{3,4} 0[0-9]{9,11} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$\";
        final String regex00 = \"^079[0-9]$\";// 座机区号正则
        final String regextel = \"u-1[0-9]{10} 1$\";// 手机开户正则
        final String regexmessage= \"u-1[0-9]{10} 3$\";
        final String regexmess = \"m-1[0-9]{10} 1[0-9]{10} ([\\\\w]|[ ]|[.]|[,])+$\";

②短信计费:

class SendMessageRule extends MessageChargeRule{

    double calCost(ArrayList<MessageRecord> messageRecords) {
        int  n=0;
        for(int i=0;i<messageRecords.size();i++) {
            if(messageRecords.get(i).getMessage().length()<=10) {
                n++;
            }
            else {
                if(messageRecords.get(i).getMessage().length()%10==0)
                    n += messageRecords.get(i).getMessage().length()/10;
                else
                    n += messageRecords.get(i).getMessage().length()/10+1;
            }
                
        }
        if(n<=3)
            return n*0.1;
        else if(n<=5)
            return 0.3+(n-3)*0.2;
        else
            return 0.7+(n-5)*0.3;
    }
    
}

实验四

设计与分析:

 

 

 

 

 

 

实验4(1)主要是理解看懂给出的类图,根据类图写出代码。理解各种抽象类之间的联系,最难的应该是GaneDate类型的理解。这个类把这几个物品之间联系起来,实现游戏的功能,这几个类图之间的代码实现

实验4(2)在原有的基础上把boat类继承到abstractObjectial即可。把abstractransport变成接口,再用boat实现这个接口,这道题就算写完。

在代码实现过程中遇见的最大问题就是看懂题给出的类图,写出类图这道题目就相当于完成了一大半,在这个基础上把各个类串在一起,理解每个类之间的关系,就可以完成这道题。为了避免修改农夫过河前几次代码中代码的主干,我在游戏类中保留了这些代码,这样的代码修改难度就降低了不少,最后运行完成后debug找出了一个逻辑上的错误,就提交了这次实验。

 

 这是case31错误时的截图,这道题这个点我也是测试了非常久才给出了正确的答案。

原因在于一个函数

 

 

实验4(1)主要是理解看懂给出的类图,根据类图写出代码。理解各种抽象类之间的联系,最难的应该是GaneDate类型的理解。这个类把这几个物品之间联系起来,实现游戏的功能,这几个类图之间的代码实现

实验4(2)在原有的基础上把boat类继承到abstractObjectial即可。把abstractransport变成接口,再用boat实现这个接口,这道题就算写完。

在代码实现过程中遇见的最大问题就是看懂题给出的类图,写出类图这道题目就相当于完成了一大半,在这个基础上把各个类串在一起,理解每个类之间的关系,就可以完成这道题。为了避免修改农夫过河前几次代码中代码的主干,我在游戏类中保留了这些代码,这样的代码修改难度就降低了不少,最后运行完成后debug找出了一个逻辑上的错误,就提交了这次实验。

改进意见:

1、如复杂度分析图所示,本题的圈复杂度为33,超过了代码的一般范围,这就意味这该代码的可读性非常的差,而对与前文提到的方法都可有效减少圈复杂度。

2、本题的代码有两百多行,代码的简化就显得尤为重要,应该适当的合适代码的逻辑,让代码模块化。

3、如果能够将代码的逻辑颠倒一下,程序将更加的合理。

核心代码如下:

public abstract class AbstracTransport {
private String place =\"a\";public String departure =\"a\";
private int capacity;
private ArrayList<MaterialObject>goodses = new ArrayList<MaterialObject>();
abstract public void moveTo( String destination);
public int getCapacity() {
    return capacity;
}
public void setCapacity(int capacity) {
    this.capacity = capacity;
}
public String getPlace() {
    return place;
}
public void setPlace(String place) {
    this.place = place;
}
public ArrayList<MaterialObject> getGoodses() {
    return goodses;
}
public void setGoodses(ArrayList<MaterialObject> goodses) {
    this.goodses = goodses;
}
}
class Boat extends AbstracTransport{

    public void moveTo(String  destination) {
        if(this.getPlace() == destination)
                setPlace(departure);
        else
            setPlace(destination);
    }
    public void crossRiver(Person person) {
        if(person.isPlace())
            person.setPlace(departure);
        else
            person.setPlace(\"b\");
    }
    public void crossRiver(Person person, MaterialObject m) {
        this.crossRiver(person);
        if(m.isPlace())
            m.setPlace(departure);
        else
            m.setPlace(\"b\");
    }
    public void broad(MaterialObject m) {
        getGoodses().add(m);
    }
    
    public void disembark(MaterialObject m){
        getGoodses().remove(m);
        }
    }
public abstract class AbstractGame {
    private AbstractRule gameOverRule = new GameOverRule();
        
    private AbstractRule gameSuccessRule = new GameSuccessRule();
        
    private GameData gameDate = new GameData();
    
    public abstract void play() ;
        
    
}
public abstract class AbstractRule {
    public abstract boolean judge(GameData gameData);
}
class CrossRiverRule extends AbstractRule{
        public boolean judge(GameData gameData) {
        if(hasCross(gameData.cabbage)&&hasCross(gameData.sheep)&&hasCross(gameData.wolf)&&hasCross(gameData.farmer))
            return true;
        return false;
    }
    public boolean hasCross(MaterialObject m) {
        if( m.getPlace().equals(m.destination))
            return true;
        return false;
    }
}
class ObjectExistRule extends AbstractRule{
    public boolean judge(GameData gameData){
        if(gameData.sheep.isExist()&&gameData.wolf.isExist()&&gameData.cabbage.isExist())
            return true;
        return false;
    }
}
class GameOverRule extends AbstractRule{
    ObjectExistRule oe= new ObjectExistRule();
    CrossRiverRule cr = new CrossRiverRule();
    public boolean judge(GameData gameData) {
        if(!oe.judge(gameData))
            return true;
        if(cr.judge(gameData))
            return true;
        return false;
    }
}

学习心得:

1、通过三次作业的练习,强化了我对java语言的使用和理解,学习了很多编程时使用的技巧和方法,深化了我书写代码时的逻辑理解和对算法的实现能力,拔高了我对程序设计的眼界和深度,领悟自上而下逐步细化的编程思想更加的透彻

2、知道了写程序时细节决定成败,必须一丝不苟,让我对自己的水平有了一定的认知,提醒我今后的学习还有很多改进的地方,深刻了解了关于Java程序结构和代码书写的规范,以及要遵守的代码书写规范

3、积累了一些debug的经验,深刻认识到了调试的重要性,学会了调试的基本技巧,如何设置断点,单步进入,跟踪参数,以及更改代码的逻辑顺序,排除逻辑错误,对提高代码的质量大有改善。

4、学会了正则表达式的运用,合理的运用正则表达式能有效减少判断格式代码的行数,还可以准确的判断出输入的正确性。

5、面对复杂的类图时不要盲目的下手,要寻找到一个突破口,从一点上去逐步书写整个代码,这样写出来的代码就会条理比较清晰,效果也更好。

 


来源:https://www.cnblogs.com/zhiyixingnan/p/16385205.html
本站部分图文来源于网络,如有侵权请联系删除。

未经允许不得转载:百木园 » 第十到第十六周—— BLOG_3

相关推荐

  • 暂无文章