三次电信计费作业的总结

 

一、设计与分析

 

1. 电信计费系列1-座机计费(PTA212019班-题目集09)

 TASK实现一个简单的电信计费程序:
假设南昌市电信分公司针对市内座机用户采用的计费方式:
月租20元,接电话免费,市内拨打电话0.1元/分钟,省内长途0.3元/分钟,国内长途拨打0.6元/分钟。不足一分钟按一分钟计。
南昌市的区号:0791,江西省内各地市区号包括:0790~0799以及0701。 

 

设计策略:几道复杂的多边形题目之后,我们迎来了电信计费这个新题型,虽然看似麻烦,但在参考类图中已给出用户类,计费类与记录类的设计方式,极大的降低了本次题目的难度,对于本次题目来说,结构的设计要占大头,格式的判断居其次,涉及的计算量非常小。因此本次题目的设计策略主要是围绕如何判断输入信息的格式,以及判断后是如何将信息存储进记录类,这是我们在进行此次题目的设计中要着重、优先考虑的。

       此次格式判断,本人引入了上次多边形的格式判断类,即一个类分管所有的格式判断,并另有一个内容分析类,在进行格式判断后,将内容有效的存储到记录类中,最后又加入了一个计算类,依据用户类中的记录进行费用计算,以上是除参考类图之外笔者自行引入的类设计。

 

类图如下:

 

方法和类复杂度分析:

 

优点:

  挺好拓展的,但好像也没那么好拓展,除此之外想不出有什么优点。

 

缺点:

  1. 控制类的输入方法很复杂,这点我在第二次作业中也没处理好,弄得复杂度过高的原因是因为输入本身是在一个while循环里面,而循环里面又有多个if else判断,其实归根到底,还是单一职责没弄好,而且这是个累活。
  2. 代码的结构多少有些臃肿了,主要是因为格式类与输入分析类没设计好,理想的状况下这两个类应该是简单易懂的,但我的设计显得很散,很乱。
  3. 格式类有些笨拙,之前自己做了一堆时间判断,最后还是用了一个正则表达式,致使以前的判断没必要了。

 控制类代码:

class Controller {
    private ParseInput parseInput = new ParseInput();

    public static Scanner in = new Scanner(System.in);

    public void input(){
        ArrayList<User> users = new ArrayList<User>();
        ArrayList<CallRecord> callRecords = new ArrayList<CallRecord>();

        String end = "end";
        String u = "u-";
        String t = "t-";

        String str;
        str = in.nextLine();

        boolean inputU = true;
        boolean inputT = false;

        while (!end.equals(str)) {
            if (parseInput.inputInvalid(str)) {
                String type = str.substring(0,2);
                String information = str.substring(2);

                if (u.equals(type) && inputU) {
                    User user;
                    user = parseInput.parseUser(information);
                    if (user != null) {
                        users.add(user);
                        inputT = true;
                    }

                } else if (t.equals(type) && inputT) {
                    CallRecord callRecord;
                    ArrayList<String> list = parseInput.parseStrT(information);
                    callRecord = parseInput.parseCallByTelephone(list);
                    if (callRecord != null) {
                        callRecords.add(callRecord);
                        inputU = false;
                    }
                }
            }

            str = in.nextLine();
        }

        in.close();

    }

 格式类与分析类代码:

class Format {
    public String inputU = "^u-((079[0-9])|(0701))\\d{7,8} [0-2]$";
    public String inputT = "[t]-0791[0-9]{7,8}\\s" + "0[0-9]{9,11}\\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])";
    private String jiangXiNum = "^((079[0-9])|(0701))\\d{7,8}$";
    private String landNum = "\\d{3,4}\\d{7,8}";
    private String jiangXiArea = "^((079[0-9])|(0701))";
    private String nanChangNum = "^0791\\d{7,8}$";
    private String nanChangArea = "0791";
    private String dateRegex = "^\\d{4}.\\d{1,2}.\\d{1,2} \\d{1,2}:\\d{2}:\\d{2}$";

    public Format() {
    }

    public boolean verifyPhoneNum(String s) {
        boolean flag;
        flag = s.matches(jiangXiNum);
        return flag;
    }

    public boolean receivePhoneNum(String s) {
        boolean flag;
        flag = s.matches(landNum);
        return flag;
    }

    public boolean verifyUserType(String s) {
        String pattern = "^[0-2]$";
        boolean flag;
        flag = s.matches(pattern);
        return flag;
    }

    public boolean verifyDate(String s) {
        boolean flag;
        flag = s.matches(dateRegex);
        return flag;
    }

    public boolean inCity(String s1, String s2) {
        boolean flag;
        flag = s1.equals(s2);
        return flag;
    }

    public boolean inProvince(String s1, String s2) {
        boolean flag;
        flag = s1.matches(jiangXiArea) && s2.matches(jiangXiArea);
        return flag;
    }

    public boolean inLand(String s1, String s2) {
        boolean flag1, flag2;
        flag1 = s1.matches(jiangXiArea) && !s2.matches(jiangXiArea);
        flag2 = !s1.matches(jiangXiArea) && s2.matches(jiangXiArea);
        return flag1 || flag2;
    }
}

class ParseInput {
    private Format format = new Format();

    public boolean inputInvalid(String s) {
        boolean flag1 = s.matches(format.inputT);
        boolean flag2 = s.matches(format.inputU);

        return flag1 || flag2;
    }

    public User parseUser(String s) {
        User user = null;
        String[] strings = s.split(" ");

        String num = strings[0];
        String userNum = parseUserNum(num);
        String type = strings[1];
        ChargeMode mode = parseUserType(type);

        if (userNum != null && mode != null) {
            user = new User(new UserRecords(), mode, userNum);
        }

        return user;
    }

    public String parseUserNum(String s) {
        if (format.verifyPhoneNum(s)) {
            return s;
        }
        return null;
    }

    public ChargeMode parseUserType(String s) {
        String landPhone = "0";
        String mobilePhone = "1";
        String mobilePhoneA = "2";
        if (landPhone.equals(s)) {
            return landPhoneMode();
        } else if (mobilePhone.equals(s)) {
            return new ChargeMode(null) {
                @Override
                public double calCost(UserRecords userRecords) {
                    return 0;
                }

                @Override
                public double getMonthlyRent() {
                    return 0;
                }
            };
        } else if (mobilePhoneA.equals(s)) {
            return new ChargeMode(null) {
                @Override
                public double calCost(UserRecords userRecords) {
                    return 0;
                }

                @Override
                public double getMonthlyRent() {
                    return 0;
                }
            };
        }
        return null;
    }

    public ChargeMode landPhoneMode() {
        ArrayList<ChargeRule> chargeRules = new ArrayList<ChargeRule>() {
            {
                add(new LandPhoneInCityRule());
                add(new LandPhoneProvinceRule());
                add(new LandPhoneInlandRule());
            }
        };
        ChargeMode landlinePhoneCharging;
        landlinePhoneCharging = new LandlinePhoneCharging(chargeRules);
        return landlinePhoneCharging;
    }

    public CallRecord parseCallByTelephone(ArrayList<String> list){
        CallRecord callRecord = null;
        if (list != null) {
            String callingNum = list.get(0);
            String answerNum = list.get(1);
            String startTimeS = list.get(2);
            String endTimeS = list.get(3);
            SimpleDateFormat sf = new SimpleDateFormat("yyyy.MM.dd 
            HH:mm:ss");
            try {
                sf.setLenient(false);
                Date startTime = sf.parse(startTimeS);
                Date endTime = sf.parse(endTimeS);
                callRecord = new CallRecord(callingNum, answerNum, startTime, 
                endTime, callingNum.substring(0,4), answerNum.substring(0,4));
            } catch (ParseException ignored) {
            }
        }
        return callRecord;
    }

    public ArrayList<String> parseStrT(String s) {
        ArrayList<String> list = null;

        String[] strings = s.split("\\s+", 3);
        String dataTime = strings[2];
        int index = dataTime.indexOf(" ");

        index = dataTime.indexOf(" ", index + 1);
        String dataTime2 = dataTime.substring(index + 1);
        String dataTime1 = dataTime.substring(0, index);

        boolean flag1 = format.verifyPhoneNum(strings[0]) || 
        format.receivePhoneNum(strings[1]);
        boolean flag2 = format.verifyDate(dataTime1) && 
        format.verifyDate(dataTime2);

        if (flag1 && flag2) {
            list = new ArrayList<String>() {
                {
                    add(strings[0]);
                    add(strings[1]);
                    add(dataTime1);
                    add(dataTime2);
                }
            };
        }

        return list;
    }
}

  

2. 电信计费系列2-手机+座机计费(PTA212019班-题目集10)

 

TASK实现南昌市电信分公司的计费程序,假设该公司针对手机和座机用户分别采取了两种计费方案,分别如下:
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. 没有解决上次作业中提到的臃肿问题,如果再一次面对这样的PTA作业,有时间的话,我一定对自己说三个字:要简洁。
  2. 代码体积过大,类过多:感觉很臃肿,很混乱。
  3. 可读性非常低:注释写的太少。

 这次的控制类中的输入方法更复杂了,设计的不好

public void input() {
        String end = "end";
        String u = "u-";
        String t = "t-";

        String str;
        str = in.nextLine();

        boolean inputU = true;
        boolean inputT = false;

        while (!end.equals(str)) {
            if (parseInput.inputInvalid2(str) || parseInput.inputInvalid(str)) {
                String type = str.substring(0, 2);
                String information = str.substring(2);

                if (u.equals(type) && inputU) {
                    User user;
                    user = parseInput.parseUser(information);
                    if (user != null) {
                        users.add(user);
                        inputT = true;
                    }

                } else if (t.equals(type) && inputT) {
                    CallRecord callRecord;
                    if (str.matches(format.inputT_MM)) {
                        ArrayList<String> list =     
                        parseInput.parseStrT_MM(information);
                        callRecord = parseInput.storageRecord_MM(list);

                    } else if (str.matches(format.inputT_ML) || 
                        str.matches(format.inputT_LM)) {
                        ArrayList<String> list = 
                        parseInput.parseStrT_LM_ML(information);
                        callRecord = parseInput.storageRecord_ML_LM(list);
                    } else {
                        ArrayList<String> list = 
                        parseInput.parseStrT_LL(information);
                        callRecord = parseInput.parseCallByTelephone(list);
                    }
                    if (callRecord != null) {
                        callRecords.add(callRecord);
                        inputU = false;
                    }
                }
            }

            str = in.nextLine();
        }
    } 

  

 

3. 电信计费系列3-短信计费(PTA212019班-题目集11)

 

TASK实现一个简单的电信计费程序,针对手机的短信采用如下计费方式
1、接收短信免费,发送短信0.1元/条,超过3条0.2元/条,超过5条0.3元/条。
2、如果一次发送短信的字符数量超过10个,按每10个字符一条短信进行计算。

 

设计策略:最后一次电信作业,这次非常的简单,减去了时间格式的判断,大大降低了题目的难度,于是重新进行了一次类设计,因为这次题目所涉及到的方法非常简单,体量也非常的小,如果接着上一次拓展的话,代码体积就太大了,PTA直接过不去,这次类设计我直接减去了各个抽象父类以及格式类与分析类,代码总共才五个类。

 

类图如下:

 

方法与类复杂度分析:

 

优点:

这次代码量才200行,可以说非常的简洁,了却了我前两次题目的夙愿。

 

缺点:

  1. 简洁的代价是:不易拓展,关于java的类设计,我觉得最难想的是抽象父类该怎么设计,这直接决定了代码是如何进行拓展的。
  2. 经典问题:单一职责,单一职责不仅是个累活,我想,巧妙的代码往往非常简洁易懂,关键就在于单一职责实现的好。这也是个设计的过程。像这种题目设计好一次就能用三次。

 

二、总结

     

  1. 题目中的有些问题很有意思,比如区号是四位也可能是三位,这就像一个冷知识一样穿插在题目中,前面我没说,我认为这种东西就没什么好说的,我实在想不出有什么技术含量。
  2. PTA的意义,我认为主要是如何设计,有些极其繁琐的细节问题,我认为主要归结于出题人如何写测试点,而不是我如何设计,我设计有多好也不可能面面俱到,就像时间判断一样,我用时间类判断竟然不对,只能用正则表达式,如果这样的问题在之后的项目中乃至以后的工作中遇到,有关于繁琐细节的问题那就非常值得思考了。
  3. 做一次这样的迭代作业之后,我首先一个感悟是,最好在第一次作业就把类设计的非常完善与简洁,现在我想简洁这个东西大概是java的最大难题,不仅是迭代作业,日常作业我认为也应当追求简洁,简洁易于交流也易于代码的身心健康,简洁应该成为一种习惯。

 

posted @ 2022-06-15 23:53  Inmata  阅读(314)  评论(0)    收藏  举报