第三次大作业总结

前言

  本次是对于前3次的PTA以及所学习内容进行阶段性总结,题目主要是对于PTA给出类图的复现,难度尚可,让我对于专业去设计类图拥有了一次宝贵的经验,除此之外我也用到了一些lambda的语法糖,对于一些JAVA扩展的学习还是分成有趣的。

设计与分析

电信计费(座机计费)

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

这是一道非常具有代表性的JAVA类图复现,让我们从给与信息一点一点来进行复现

  

  首先是对于该类图的分析,可以得知设计师路上将分划出一个用户类,拥有电话、余额等,通过对于不同方式构建一个ChargeMode类,这样也是为了后面几次PTA电信计费大作业方便。可以通过不同的几个支付方式子项来继承Chargrmode,在本道题目使用了座机的支付方式,拥有基础属性的基础以月租20。

User(点开以展示全部代码细节)
public class User {
    private UserRecords userRecords = new UserRecords(); //用户记录
    private double balance = 100; //余额
    private ChargeMode chargeMode; //计费方式
    private String number; //号码

    public double calBalance() {
        return getBalance() - getChargeMode().getMonthlyRent() - calCost();
    }

    public double calCost() {
        return getChargeMode().calCost(userRecords);
    }

    public UserRecords getUserRecords() {
        return userRecords;
    }

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

    public double getBalance() {
        return balance;
    }

    public ChargeMode getChargeMode() {
        return chargeMode;
    }

    public void setChargeMode(ChargeMode chargeMode) {
        this.chargeMode = chargeMode;
    }

    public String getNumber() {
        return number;
    }

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


    @Override
    public String toString() {
        return getNumber() + " " + (float)calCost() + " " + (float)calBalance();
    }
}
ChargeMode(点开以展示全部)
 import java.util.ArrayList;

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

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

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

    public abstract double calCost(UserRecords userRecords);

    public abstract double getMonthlyRent();
}

上述对于座机计费方式的设计,便牵扯到题目对于不同跨区的计费方式不同,如下类图所示

设计一个通话计费方式来继承ChargeRule的抽象父类,而calChargreMode有三个不同的座机计费子类,其实可以分析题目意思得出,这样一个三级结构是为了后续电信计费题目更好引入一些其他的情况,比如短信计费、手机计费、套餐计费等,在这个时候通过对于类图设计的观察,可以得出一些后续题目加强的猜想,也为后续题目多改进刘下设计空间。

  ChargeMode的设计在本处是较为基础的,因为并没有靠它实现什么功能

两个支付的父类结构(点击展开全部细节)
 public abstract class ChargeRule {
    public abstract double calCost(ArrayList<CallRecord> callRecords);
}

public  abstract class CallChargeRule extends ChargeRule{
}

对于3钟不同座机拨号计价可知相应,通过修改计费标价即可,唯一需要注意的的是未满一分钟要以一分钟计价,所以可以对于分钟统计使用Math包的ceil函数,以市内通信计费举例

LandPhoneInCityRule(点击以展示全部细节)
 public class LandPhoneInCityRule extends CallChargeRule {

    @Override
    public double calCost(ArrayList<CallRecord> callRecords) {
        double sum = 0;
        for (CallRecord callRecord : callRecords) {
            long startTime = callRecord.getStartTime().getTime();
            long endTime = callRecord.getEndTime().getTime();
            double minute = (endTime - startTime) / 60000;
            sum += Math.ceil(minute) * 0.1;
        }
        return sum;
    }
}

其他三类如是则可,完成相应的calCost方式,为了后续对于计价统计

三类不同区域通信的calCost方式完善完毕后,即可对于之前座机通话计费模式进行编程

LandlinePhoneCharging(点击以展示全部细节)
 public class LandlinePhoneCharging extends ChargeMode {
    Double monthlyRent = 20.0;

    public LandlinePhoneCharging() {
        getChargeRules().add(new LandPhoneInCityRule());
        getChargeRules().add(new LandPhoneInProvinceRule());
        getChargeRules().add(new LandPhoneInLandRule());
    }

    @Override
    public double calCost(UserRecords userRecords) {
        double cost = 0;
        cost += getChargeRules().get(0).calCost(userRecords.getCallingInCityRecords());
        cost += getChargeRules().get(1).calCost(userRecords.getCallingInProvinceRecords());
        cost += getChargeRules().get(2).calCost(userRecords.getCallingInLandRecords());
        return cost;
    }

    @Override
    public double getMonthlyRent() {
        return monthlyRent;
    }
}

其余很多其他工具类放到后续讲解,首先可以观察一下对于一些工具函数的调用

public static void PrintUsers() {
        users.sort((user1, user2) -> user1.getNumber().compareTo(user2.getNumber()));
        users.forEach(System.out::println);
    }

这是为了对于开户用户的顺序输出,因为排序需要根据用户电话号码进行排序,通过使用lambda可以有效节省代码长度,但可读性也较差,不过代码足够简单,我个人还是比较喜欢使用这类语法糖的。

public static String uPattern = "u-0791\\d{7,8}\\s[0-2]";
    public static String tPattern = "(t-)([0-9]{11,12} ){2}" +
            "[1-9]\\d{3}\\.([1-9]|1[0-2])\\.([1-9]|(2\\d|3[0-1]))\\s([0|1]?[0-9]|2[0-3]):[0-5]\\d:[0-5]\\d\\s?";

    public static void judgeString(String string) {
        if (string.matches(uPattern)) Houses(string.substring(2));
        else if (string.matches(tPattern)) Call(string.substring(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

这是对于上一道题目的增强,除了使用座机,还使用了电话拨号

首先可以观测一下UserRecord,其实上一道题目也有使用到,但是只是基础的利用,并没有对于这个类进行复杂包装,而目前手机的计费方式拥有更多样的通讯方式,对于用户通话记录使用也会更加频繁。

UserRecords(点击以展示全部)
 import java.util.ArrayList;

public class UserRecords {
    private final ArrayList<CallRecord> callingInCityRecords = new ArrayList<>();
    private final ArrayList<CallRecord> callingInProvinceRecords = new ArrayList<>();
    private final ArrayList<CallRecord> callingInLandRecords = new ArrayList<>();
    private final ArrayList<CallRecord> callingNationRoaming = new ArrayList<>();
    private final ArrayList<CallRecord> callingProvinceRoaming = new ArrayList<>();
    private final ArrayList<CallRecord> answerInCityRecords = new ArrayList<>();
    private final ArrayList<CallRecord> answerInProvinceRecords = new ArrayList<>();
    private final ArrayList<CallRecord> answerInLandRecords = new ArrayList<>();
    private final ArrayList<MessageRecord> sendMessageRecords = new ArrayList<>();
    private final ArrayList<MessageRecord> receiveMessageRecords = new ArrayList<>();

    public void addCallingInCityRecords(CallRecord callRecord) {
        callingInCityRecords.add(callRecord);
    }

    public void addCallingInProvinceRecords(CallRecord callRecord) {
        callingInProvinceRecords.add(callRecord);
    }

    public void addCallingInLandRecords(CallRecord callRecord) {
        callingInLandRecords.add(callRecord);
    }

    public void addAnswerInCityRecords(CallRecord answerRecord) {
        answerInCityRecords.add(answerRecord);
    }

    public void addAnswerInProvinceRecords(CallRecord answerRecord) {
        answerInProvinceRecords.add(answerRecord);
    }

    public void addAnswerInLandRecords(CallRecord answerRecord) {
        answerInLandRecords.add(answerRecord);
    }

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

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

    public void addCallingNationRoaming(CallRecord callRecord) {
        callingNationRoaming.add(callRecord);
    }

    public void addCallingProvinceRoaming(CallRecord callRecord) {
        callingProvinceRoaming.add(callRecord);
    }

    public ArrayList<CallRecord> getCallingNationRoaming() {
        return callingNationRoaming;
    }

    public ArrayList<CallRecord> getCallingProvinceRoaming() {
        return callingProvinceRoaming;
    }

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

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

    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;
    }
}

而对于计费方式方面,由于移动电话的引入带来了更多不同的关系

加入一个接听的计费方式以及六种电话计费方式,一下不一一举例,就拿接听计费来说

MobilePhoneChargingNationalAnswer(点击以展示全部细节)
import java.util.ArrayList;

public class MobilePhoneChargingNationalAnswer extends AnswerChargeRule{
    @Override
    public double calCost(ArrayList<CallRecord> callRecords) {
        double sum = 0;
        for (CallRecord callRecord : callRecords) {
            long startTime = callRecord.getStartTime().getTime();
            long endTime = callRecord.getEndTime().getTime();
            double minute = (endTime - startTime) / 60000.0;
            sum += Math.ceil(minute) * 0.3;
        }
        return sum;
    }
}

在前期正则判断上,需要更加具体的判断电话类型

public static String uPattern = "(^u-0[0-9]{10,11} [0]$)|(^u-1[0-9]{10} [1-3]$)";
    public static String tPattern = "(^t-[0]{1}[0-9]{9,11} ((0[0-9]{9,11})|(1[0-9]{10} 0[0-9]{2,3})) " +
            "[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])$)|" +
            "(^t-1[0-9]{10} 0[0-9]{2,3} ((0[0-9]{9,11})|(1[0-9]{10} 0[0-9]{2,3})) [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])$)";

    public static void judgeString(String string) {
        if (string.matches(uPattern)) Houses(string.substring(2));
        else if (string.matches(tPattern)) Call(string.substring(2));
    }

开户时,对于不同的计费模式,要进行不同的外源实现

private static void Houses(String string) {
        User user = new User();
        user.setNumber(string.split(" ")[0]);
        if (string.split(" ")[1].equals("0"))
            user.setChargeMode(new LandlinePhoneCharging());
        else if (string.split(" ")[1].equals("1"))
            user.setChargeMode(new MobilePhoneCharging());
        for (User u : users) {
            if (u.getNumber().equals(user.getNumber())) return;
        }
        users.add(user);
    }

下述是关于具体单条通话记录的细节,要记录拨号和接听的区号,来区分不同计费方式,对于座机这点很简单,仅仅是前3-4位即是区号,而电话需要额外记录

CallRecord(点击以展示全部细节)
 public class CallRecord extends CommunicationRecord {
    private Date startTime;
    private Date endTime;
    private String callingAddressAreaCode;
    private String answerAddressAreaCode;

    public Date getStartTime() {
        return startTime;
    }

    public Date getEndTime() {
        return endTime;
    }

    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    public String getCallingAddressAreaCode() {
        return callingAddressAreaCode;
    }

    public String getAnswerAddressAreaCode() {
        return answerAddressAreaCode;
    }

    public void setCallingAddressAreaCode(String callingAddressAreaCode) {
        this.callingAddressAreaCode = callingAddressAreaCode;
    }

    public void setAnswerAddressAreaCode(String answerAddressAreaCode) {
        this.answerAddressAreaCode = answerAddressAreaCode;
    }
}

在判定关于通话座机还是电话方面,通话记录的函数,也需要特别注意

private static void Call(String string) {
        CallRecord callRecord = new CallRecord();
        String[] timeStr = new String[4];
        if (string.split(" ")[0].charAt(0) == '0') {
            callRecord.setCallingNumber(string.split(" ")[0]);
            callRecord.setAnswerNumber(string.split(" ")[1]);
            callRecord.setCallingAddressAreaCode(string.split(" ")[0].substring(0, 4));
            if (string.split(" ")[1].charAt(0) == '0') {
                callRecord.setAnswerAddressAreaCode(string.split(" ")[1].substring(0, 4));
                System.arraycopy(string.split(" "), 2, timeStr, 0, 4);
            } else {
                callRecord.setAnswerAddressAreaCode(string.split(" ")[2]);
                System.arraycopy(string.split(" "), 3, timeStr, 0, 4);
            }
        } else if (string.split(" ")[0].charAt(0) == '1') {
            callRecord.setCallingNumber(string.split(" ")[0]);
            callRecord.setCallingAddressAreaCode(string.split(" ")[1]);
            callRecord.setAnswerNumber(string.split(" ")[2]);
            if (string.split(" ")[2].charAt(0) == '0') {
                callRecord.setAnswerAddressAreaCode(string.split(" ")[2].substring(0, 4));
                System.arraycopy(string.split(" "), 3, timeStr, 0, 4);
            } else {
                callRecord.setAnswerAddressAreaCode(string.split(" ")[3]);
                System.arraycopy(string.split(" "), 4, timeStr, 0, 4);
            }
        }
        try {
            callRecord.setStartTime(dateFormat.parse(timeStr[0] + " " + timeStr[1]));
            callRecord.setEndTime(dateFormat.parse(timeStr[2] + " " + timeStr[3]));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        for (User user : users) {
            if (user.getNumber().equals(callRecord.getCallingNumber())) {
                if (callRecord.getCallingAddressAreaCode().equals("0791")) {
                    if (callRecord.getCallingAddressAreaCode().equals(callRecord.getAnswerAddressAreaCode()))
                        user.getUserRecords().addCallingInCityRecords(callRecord);
                    else if (callRecord.getAnswerAddressAreaCode().matches("07(9[0-9]|01)"))
                        user.getUserRecords().addCallingInProvinceRecords(callRecord);
                    else user.getUserRecords().addCallingInLandRecords(callRecord);
                } else if (callRecord.getCallingAddressAreaCode().matches("07(9[0-9]|01)"))
                    user.getUserRecords().addCallingProvinceRoaming(callRecord);
                else user.getUserRecords().addCallingNationRoaming(callRecord);
            } else if (user.getNumber().equals(callRecord.getAnswerNumber())) {
                if (!callRecord.getAnswerAddressAreaCode().matches("07(9[0-9]|01)")) {
                    user.getUserRecords().addAnswerInLandRecords(callRecord);
                }
            }
        }
    }

本题类图在后续一同给出

 

电信计费(短信计费)

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

这道题目区分前两道,属于是不同的电信计费方式,首先对于开户以及短信记录的函数就要进行更改或是添加

public static String uPattern = "(^u-0[0-9]{10,11} [0]$)|(^u-1[0-9]{10} [1-3]$)";
public static String tPattern = "(^t-[0]{1}[0-9]{9,11} ((0[0-9]{9,11})|(1[0-9]{10} 0[0-9]{2,3})) " +
        "[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])$)|" +
        "(^t-1[0-9]{10} 0[0-9]{2,3} ((0[0-9]{9,11})|(1[0-9]{10} 0[0-9]{2,3})) [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])$)";
public static String mPattern = "m-(1\\d{10}\\s){2}[\\w\\.,]*";

public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");

public static void judgeString(String string) {
    if (string.matches(uPattern)) Houses(string.substring(2));
    else if (string.matches(tPattern)) Call(string.substring(2));
    else if (string.matches(mPattern)) Send(string.substring(2));
}

private static void Houses(String string) {
    User user = new User();
    user.setNumber(string.split(" ")[0]);
    if (string.split(" ")[1].equals("0"))
        user.setChargeMode(new LandlinePhoneCharging());
    else if (string.split(" ")[1].equals("1"))
        user.setChargeMode(new MobilePhoneCharging());
    else if (string.split(" ")[1].equals("3"))
        user.setChargeMode(new MessageCharging());
    for (User u : users) {
        if (u.getNumber().equals(user.getNumber())) return;
    }
    users.add(user);
}

private static void Send(String string){
    MessageRecord messageRecord = new MessageRecord();
    messageRecord.setCallingNumber(string.split(" ")[0]);
    messageRecord.setAnswerNumber(string.split(" ")[1]);
    messageRecord.setMessage(string.substring(24));
    for (User user : users) {
        if (user.getNumber().equals(messageRecord.getCallingNumber())) {
            user.getUserRecords().addSendMessageRecords(messageRecord);
        }
        if (user.getNumber().equals(messageRecord.getAnswerNumber())) {
            user.getUserRecords().addReceiveMessageRecords(messageRecord);
        }
    }

}
private static void Call(String string) {
    CallRecord callRecord = new CallRecord();
    String[] timeStr = new String[4];
    if (string.split(" ")[0].charAt(0) == '0') {
        callRecord.setCallingNumber(string.split(" ")[0]);
        callRecord.setAnswerNumber(string.split(" ")[1]);
        callRecord.setCallingAddressAreaCode(string.split(" ")[0].substring(0, 4));
        if (string.split(" ")[1].charAt(0) == '0') {
            callRecord.setAnswerAddressAreaCode(string.split(" ")[1].substring(0, 4));
            System.arraycopy(string.split(" "), 2, timeStr, 0, 4);
        } else {
            callRecord.setAnswerAddressAreaCode(string.split(" ")[2]);
            System.arraycopy(string.split(" "), 3, timeStr, 0, 4);
        }
    } else if (string.split(" ")[0].charAt(0) == '1') {
        callRecord.setCallingNumber(string.split(" ")[0]);
        callRecord.setCallingAddressAreaCode(string.split(" ")[1]);
        callRecord.setAnswerNumber(string.split(" ")[2]);
        if (string.split(" ")[2].charAt(0) == '0') {
            callRecord.setAnswerAddressAreaCode(string.split(" ")[2].substring(0, 4));
            System.arraycopy(string.split(" "), 3, timeStr, 0, 4);
        } else {
            callRecord.setAnswerAddressAreaCode(string.split(" ")[3]);
            System.arraycopy(string.split(" "), 4, timeStr, 0, 4);
        }
    }
    try {
        callRecord.setStartTime(dateFormat.parse(timeStr[0] + " " + timeStr[1]));
        callRecord.setEndTime(dateFormat.parse(timeStr[2] + " " + timeStr[3]));
    } catch (ParseException e) {
        e.printStackTrace();
    }
    for (User user : users) {
        if (user.getNumber().equals(callRecord.getCallingNumber())) {
            if (callRecord.getCallingAddressAreaCode().equals("0791")) {
                if (callRecord.getCallingAddressAreaCode().equals(callRecord.getAnswerAddressAreaCode()))
                    user.getUserRecords().addCallingInCityRecords(callRecord);
                else if (callRecord.getAnswerAddressAreaCode().matches("07(9[0-9]|01)"))
                    user.getUserRecords().addCallingInProvinceRecords(callRecord);
                else user.getUserRecords().addCallingInLandRecords(callRecord);
            } else if (callRecord.getCallingAddressAreaCode().matches("07(9[0-9]|01)"))
                user.getUserRecords().addCallingProvinceRoaming(callRecord);
            else user.getUserRecords().addCallingNationRoaming(callRecord);
        } else if (user.getNumber().equals(callRecord.getAnswerNumber())) {
            if (!callRecord.getAnswerAddressAreaCode().matches("07(9[0-9]|01)")) {
                user.getUserRecords().addAnswerInLandRecords(callRecord);
            }
        }
    }
}

除此之外便是关于计费方式的引入

题目给出的类图是

但是由于ArrayList<MessageRecords>与ArrayList<CallRecords>不存在直接共同继承导致需要对于三级父类结构进行一定的修改

import java.util.ArrayList;

public abstract class ChargeRule {
    public abstract double calCostSelect(ArrayList<? extends CommunicationRecord> communicationRecords);

}
public abstract class CallChargeRule extends ChargeRule {
    public abstract double calCost(ArrayList<CallRecord> communicationRecords);

    public double calCostSelect(ArrayList<? extends CommunicationRecord> communicationRecords) {
        return calCost((ArrayList<CallRecord>) communicationRecords);
    }

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

    public double calCostSelect(ArrayList<? extends CommunicationRecord> communicationRecords){
        return calCost((ArrayList<MessageRecord>)communicationRecords);
    }
}

对于计费方式也得进行一定的修改,如短信计费类如下

public class SendMessageRule extends MessageChargeRule {

    @Override
    public double calCost(ArrayList<MessageRecord> messageRecords) {
        double sum = 0;
        int messageNum = 0;
        for (MessageRecord messageRecord : messageRecords) {
            messageNum += Math.ceil(messageRecord.getMessage().length() / 10.0);
        }
        if (messageNum <= 3) sum = messageNum * 0.1;
        else if (messageNum > 5) sum = messageNum * 0.3 -0.8;
        else sum = messageNum * 0.2 - 0.3;
        return sum;
    }

}

给出题目类图如下

采坑心得

对于类图复现还是较为生疏,很多次能正常运行代码,但展示类图和要求还是有一定差距,虽说功能实现即可,但是通过练习扭转设计思路的不足才是最重要的事情,踩坑的主要在于ArrayList<>的括号内各个类的继承关系并非有直接关联,不构成直接的继承关系,所以在一开始遇到如下问题

所以得通过一个中间的选择器方法来对于短信记录和通话记录分类首先对于最父类ChargeRule进行改造

public abstract class ChargeRule {
    public abstract double calCostSelect(ArrayList<? extends CommunicationRecord> communicationRecords);

}

其次便是在短信类和通话类进行重构

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

    public double calCostSelect(ArrayList<? extends CommunicationRecord> communicationRecords){
        return calCost((ArrayList<MessageRecord>)communicationRecords);
    }
}
public abstract class CallChargeRule extends ChargeRule {
    public abstract double calCost(ArrayList<CallRecord> communicationRecords);

    public double calCostSelect(ArrayList<? extends CommunicationRecord> communicationRecords) {
        return calCost((ArrayList<CallRecord>) communicationRecords);
    }

}

 

改进建议

其实题目设计上存在一些类的或者属性,又或者是方法的未使用,这些属性方法让代码变得复杂且难以理解,可以对于这些未使用的属性进行精简,其次是代码设计上,需要有更好全局意识,为了后续设计猜想留下改动空间,要不然每次添加一点点小功能便要重新设计是非常划不来的。

总结

本次是对于前3次的PTA以及所学习内容进行阶段性总结,因为在之前已经对于JAVA的基础知识点进行了全方位扫盲,所以这次主要是在加强JAVA继承多态应用的前提下,多学习一点JAVA的语法糖和一些其他用法,比如多线程、lambda表达式和JAVAFX等。后几次PTA相较之前课程设计来说要简单很多,更加侧重于对于给予类图的复现,这是一项非常重要的技巧,也可能是未来工作生活的最重要的技术包。仅仅是类图复现,这听起来非常简单,但其实也有很多难点,因为之前对于面向对象类设计规划上,自己就存在很大弊端,导致这种半吊子的项目设计思路在复现给与相较完善的类图时,总感觉有点力不从心。除此之外,lambda表达式等JAVA语法糖激起我对于JAVA学习的很大兴趣,因为之前觉得相比PYTHON、GO等,JAVA语法相较严谨的特点,导致书写看起来较为复杂。但因为lambda表达式的应用,让JAVA可以牺牲一些可读性来换取极大是书写便利,当然,使用lambda表达式需要极大的熟练度,否则可读性差到影响自己写是极为糟糕的😟。而JAVAFX的学习,我总感觉学习意义没那么大,因为学习过CSS等其他GUI编程语言,让我觉得JAVAFX存在太多不便利的地方,当然,但是一种经历是不错的。

posted @ 2022-12-10 16:50  帅气仔厘子  阅读(43)  评论(0)    收藏  举报