OOP第二次Blog作業

ylzq發表於2024-06-01

OOP第二次Blog作業

前言

知識點

這三次程式設計的作業對於類與類之間的關係的設計要求較高,如果不能很好的設計出合理的類間關係,那麼寫起這個題目來無疑是這補補,那看看。所以,對於程式的大致框架設計顯得十分重要。三次pta作業涉及的知識點很多,要求平時基本功要很好。在答題程式4中,家居強電電路模擬程式-1家居強電電路模擬程式-2這三次作業中,都以繼承為背景,考察了繼承的相關知識,例如繼承中多型的使用,方法的重寫,以及子類的構造方法呼叫父類的無參構造進行資料的初始化等。同時,對於輸入資料的解析和處理又考察了對於正規表示式的使用,利用捕獲和分組對解析相應的資訊進行建立物件的初始化。在儲存物件資料時又考察了集合類的使用,例如ArrayList,,HashMap等資料結構的使用,對於儲存的資料要求不同要選擇對應的集合類進行儲存,HashMap可以儲存一個鍵值對,這就可以用來實現編號和物件一一對應的關係。

題量和難度

總的來說,題量不是很大,每週就是寫一個大的pta作業,但題目的需求是越來越複雜了。不過題目是迭代的,難度在一開始不會很大,所以只要做好每一次的pta題目,那麼下一次的題目也就是在上一次寫好的程式中加上一些新的需求。雖然有時候類間關係的設計真的很難。我覺得這些題的難點有兩個:1.在於字母合理的設計類間關係;2.在於對於題目資訊的解析和處理,剩下的就是時間問題了,把這兩個搞清楚了,寫起程式來那自然是不在話下。

設計與分析

答題程式4

新增需求:在本題中,相對與答題程式3,新增了選擇題和填空題,並且對於這兩道題還引入了部分正確的情況,新增輸入順序打亂,多個學生多張答卷情況。

設計:因為出現了多種題目,所以抽取出一個題目類作為父類,讓具體的題型繼承題目類,在每一個子類題目中重寫判斷答案是否正確的isRight方法,對於一般的問答題,結果只有兩種,所以只要比較標準答案和學生答案是否一致就行,對於多選題和填空題,新增了答案部分正確的情況判斷,處理方法是將標準答案的字串進行處理。基本思想透過迴圈遍歷比較學生答案和標準答案,統計學生的子答案與標準答案匹配個數,如果出現一個不正確的,返回-1,如果匹配數量小於答案數量,部分正確,則返回2,如果匹配數量等於答案數量,返回1,最後根據返回值來計算得分。

對於題目刪除順序打亂的處理也很簡單,在主函式中,先用一個ArrayList集合儲存被刪除題目的題號,輸入完成後,在對於題目集合中被刪除的題目進行標記,標記完之後在用題目集合對每張試卷的題目資訊進行初始化。

然後對於多張試卷的處理,只需在答卷類中將原來的單張試卷改成用集合儲存即可。

整體框架:1.在主函式中,先建立集合物件儲存所有題目,試卷,答卷,學生資訊,被刪除的題目編號,然後解析輸入資料,先對資料進行格式判斷,格式錯誤輸出對應資訊,格式正確建立物件.2.輸入結束後,對於集合中的資料進一步處理,標記刪除的題目,對試卷中的題目初始化,對於答卷中的資料域進一步初始化.3.然後對試卷進行排序。4.輸出結果。

類圖:

方法:

Method CogC ev(G) iv(G) v(G)
Main3.isFormat(String) 16 11 8 13
Main3.main(String[]) 55 1 24 24
Main3.sortAnswer(ArrayList) 11 1 6 6
answer.answer() 0 1 1 1
answer.getMyAnswer() 0 1 1 1
answer.getNum() 0 1 1 1
answer.getP() 0 1 1 1
answer.getResult() 0 1 1 1
answer.getStuID() 0 1 1 1
answer.input(String) 0 1 1 1
answer.paperByNum(TreeMap<Integer, paper>) 1 1 1 2
answer.searchID(int) 3 3 3 3
answer.searchName(TreeMap<String, String>) 1 2 2 2
answer.setMyAnswer(HashMap<Integer, String>) 0 1 1 1
answer.setMyAnswer(String) 1 1 2 2
answer.setNum(String) 1 1 2 2
answer.setNum(int) 0 1 1 1
answer.setP(paper) 0 1 1 1
answer.setRealMyAnswer() 4 2 3 4
answer.setResult() 11 6 5 9
answer.setResult(HashMap<Integer, String>) 0 1 1 1
answer.setStuID(String) 1 1 2 2
answer.signScore(ArrayList) 10 2 8 9
answer.sumScore(TreeMap<String, String>, ArrayList) 11 3 11 11
paper.getNum() 0 1 1 1
paper.getQ() 0 1 1 1
paper.getScore() 0 1 1 1
paper.initQ(TreeMap<Integer, question>) 1 1 2 2
paper.input(String) 2 1 3 3
paper.isFull() 1 1 1 2
paper.isRight(String, int) 0 1 1 1
paper.paper() 0 1 1 1
question.getDetail() 0 1 1 1
question.getNum() 0 1 1 1
question.getStandardAnswer() 0 1 1 1
question.input(String) 1 1 2 2
question.isDelete() 0 1 1 1
question.isRight(String) 0 1 1 1
question.question() 0 1 1 1
question.setDelete(boolean) 0 1 1 1
questionK.isRight(String) 9 6 3 6
questionK.questionK() 0 1 1 1
questionN.isRight(String) 1 2 1 2
questionN.questionN() 0 1 1 1
questionZ.isRight(String) 9 6 3 6
questionZ.questionZ() 0 1 1 1
類:
Class OCavg OCmax WMC
Main3 13.67 24 41
answer 2.43 9 51
paper 1.5 3 12
question 1.12 2 9
questionK 3.5 6 7
questionN 1.5 2 3
questionZ 3.5 6 7

家居強電電路模擬程式-1

需求:這次是一個全新的題目,要求模擬簡單串聯電路,裝置分兩種:受控裝置控制裝置。受控裝置包括白熾燈,日光燈和吊扇,每個裝置在不同的電壓下都會有不同的引數狀態。控制裝置包括:開關,連續調速器和分檔調速器,其中開關用來控制電路連通與否,調速器用來改變輸入的電壓值。本次題目不考慮電阻。輸入資訊包括連線資訊調節資訊

設計:首先定義一個裝置類來儲存不同裝置的共同資訊,包括名稱,編號,輸入引腳和輸出引腳電壓值,在父類中定義display和update方法,讓子類重寫這兩個方法,為後續統一處理提供便利。
然後分別定義裝置子類開關類,為其新增int型別的資料成員,判斷開關的狀態;連續調速器分檔調速器類,為它們新增擋位資料成員來實現擋位的變化;燈類,新增亮度資料成員,來實現不同電壓下的亮度變化,然後日關燈類白熾燈類繼承燈類;吊扇類,新增轉速資料成員,實現不同電壓下的轉速變化。在受控裝置中,重寫update方法,功能是根據拿到的電壓設定裝置物件的引數,同時重寫display方法,輸出裝置的狀態引數。

最後,在主函式中,建立一個ArrayList集合儲存所有的電器裝置,利用正規表示式解析連線資訊,根據資訊建立物件,然後解析調節資訊,呼叫物件的update方法進行更新引數。

類圖:

方法:

Method CogC ev(G) iv(G) v(G)
B.B() 0.0 1.0 1.0 1.0
B.B(int, String) 0.0 1.0 1.0 1.0
B.display() 0.0 1.0 1.0 1.0
D.D(double) 0.0 1.0 1.0 1.0
D.D(int, String) 0.0 1.0 1.0 1.0
D.display() 0.0 1.0 1.0 1.0
D.getSpeeds() 0.0 1.0 1.0 1.0
Device.Device() 0.0 1.0 1.0 1.0
Device.Device(int, String) 0.0 1.0 1.0 1.0
Device.display() 0.0 1.0 1.0 1.0
Device.getId() 0.0 1.0 1.0 1.0
Device.getInput() 0.0 1.0 1.0 1.0
Device.getName() 0.0 1.0 1.0 1.0
Device.getOutput() 0.0 1.0 1.0 1.0
Device.outSubIn() 0.0 1.0 1.0 1.0
Device.setId(int) 0.0 1.0 1.0 1.0
Device.setInput(double) 0.0 1.0 1.0 1.0
Device.setName(String) 0.0 1.0 1.0 1.0
Device.setOutput(double) 0.0 1.0 1.0 1.0
Device.update() 0.0 1.0 1.0 1.0
F.F(int) 0.0 1.0 1.0 1.0
F.F(int, String) 0.0 1.0 1.0 1.0
F.display() 0.0 1.0 1.0 1.0
F.setOutput() 0.0 1.0 1.0 1.0
F.update() 0.0 1.0 1.0 1.0
K.K() 0.0 1.0 1.0 1.0
K.K(int, String) 0.0 1.0 1.0 1.0
K.getIsOpen() 0.0 1.0 1.0 1.0
L.L(double) 0.0 1.0 1.0 1.0
L.L(int, String) 0.0 1.0 1.0 1.0
L.display() 0.0 1.0 1.0 1.0
L.getCount() 0.0 1.0 1.0 1.0
L.setCount(double) 0.0 1.0 1.0 1.0
L.setOutput() 0.0 1.0 1.0 1.0
L.update() 0.0 1.0 1.0 1.0
Light.Light() 0.0 1.0 1.0 1.0
Light.Light(double) 0.0 1.0 1.0 1.0
Light.Light(int, String) 0.0 1.0 1.0 1.0
Light.getBrightness() 0.0 1.0 1.0 1.0
Light.getIsOpen() 0.0 1.0 1.0 1.0
Light.setBrightness(double) 0.0 1.0 1.0 1.0
Light.setIsOpen(int) 0.0 1.0 1.0 1.0
R.R() 0.0 1.0 1.0 1.0
R.R(double) 0.0 1.0 1.0 1.0
R.R(int, String) 0.0 1.0 1.0 1.0
R.display() 0.0 1.0 1.0 1.0
F.add() 1.0 1.0 1.0 2.0
F.sub() 1.0 1.0 1.0 2.0
K.display() 1.0 1.0 1.0 2.0
K.setIsOpen() 1.0 1.0 1.0 2.0
K.update() 1.0 1.0 2.0 2.0
Main.findNum(String) 1.0 1.0 2.0 2.0
B.update() 2.0 1.0 2.0 2.0
R.update() 2.0 1.0 2.0 2.0
Main.find(String, ArrayList) 3.0 3.0 2.0 3.0
D.update() 4.0 1.0 1.0 4.0
Device.init(String) 7.0 1.0 8.0 8.0
Main.main(String[]) 30.0 1.0 19.0 19.0
Total 54.0 60.0 88.0 96.0
Average 0.9310344827586207 1.0344827586206897 1.5172413793103448 1.6551724137931034

類:

Class OCavg OCmax WMC
L 1.0 1.0 7.0
Light 1.0 1.0 7.0
R 1.2 2.0 6.0
B 1.25 2.0 5.0
F 1.2857142857142858 2.0 9.0
D 1.4 3.0 7.0
Device 1.5 8.0 21.0
K 1.5 2.0 9.0
Main 8.0 19.0 24.0

家居強電電路模擬程式-2

新增需求:這次加入了並聯的情況,還新增了一個落地扇類,還為裝置加上了電阻

設計:在上次的基礎上,為設備父類新增電阻資料成員,然後在每一個子類的構造方法中為電阻成員賦予題目給定的電阻值,新增串聯電路類並聯電路類繼承裝置父類

首先在串聯電路類中,成員變數為裝置的ArrayList集合,儲存一條串聯電路上所有的裝置,新增判斷串聯電路是否開路方法,即遍歷裝置,判斷開關數量和開啟的開關數量是否相等,不相等返回false,否則返回true,新增計算總電流方法,因為要計算總電流,所以定義一個介面來計算總電阻,串聯電路類並聯電路類都實現這個介面,重寫計算總電阻的方法,新增對於電路上的裝置計算分壓的方法,即遍歷裝置集合,呼叫不同裝置的dy方法,在dy方法中,引數是幹路中的電流,如果該裝置不是並聯裝置,那麼用電流×電阻等到電壓,如果該裝置是並聯裝置,那麼遍歷並聯裝置裡面的裝置,計算電壓,從而實現對於每個裝置分壓的計算,拿到分壓就可以改變裝置的引數狀態。

然後在並聯裝置中,新增一個判斷並聯電路是否每條路都是開路情況。

因為要實現對裝置的排序,因此讓裝置類實現Comparable介面,同時為每一個不同的裝置設計比較值資料成員,開關先輸出,所以比較值為1,以此類推,重寫比較方法,如果裝置的比較值不同返回兩裝置比較值之差,如果相同,返回裝置編號之差,從而實現了排序。

最後,在主函式中,建立三個集合來分別儲存所有的裝置,串聯電路,並聯電路,另外定義一個串聯電路類來儲存幹路的裝置資訊,然後解析連線資訊,對於型別不同的連線進行對應的解析,建立物件,然後解析調節資訊,根據解析到的資訊,呼叫方法找到該裝置,然後呼叫改裝置的調節方法改變對應裝置狀態。然後,呼叫Collections類的sort方法對裝置集合排序,再呼叫幹路電路的方法,對所有裝置進行分壓計算和引數變化,最後,輸出結果。

類圖:

方法:

Method CogC ev(G) iv(G) v(G)
A.A(int, String) 0.0 1.0 1.0 1.0
A.display() 0.0 1.0 1.0 1.0
B.B(int, String) 0.0 1.0 1.0 1.0
B.display() 0.0 1.0 1.0 1.0
Connection.Connection(String) 0.0 1.0 1.0 1.0
Connection.I(double) 0.0 1.0 1.0 1.0
Connection.add(Device) 0.0 1.0 1.0 1.0
D.D(int, String) 0.0 1.0 1.0 1.0
D.display() 0.0 1.0 1.0 1.0
Device.Device() 0.0 1.0 1.0 1.0
Device.Device(int, String) 0.0 1.0 1.0 1.0
Device.display() 0.0 1.0 1.0 1.0
Device.dy(double) 0.0 1.0 1.0 1.0
Device.getId() 0.0 1.0 1.0 1.0
Device.getName() 0.0 1.0 1.0 1.0
Device.setName(String) 0.0 1.0 1.0 1.0
F.F(int, String) 0.0 1.0 1.0 1.0
F.display() 0.0 1.0 1.0 1.0
F.getCount() 0.0 1.0 1.0 1.0
K.K(int, String) 0.0 1.0 1.0 1.0
K.getIsOpen() 0.0 1.0 1.0 1.0
L.L(int, String) 0.0 1.0 1.0 1.0
L.display() 0.0 1.0 1.0 1.0
L.getCount() 0.0 1.0 1.0 1.0
L.setCount(double) 0.0 1.0 1.0 1.0
Light.Light(int, String) 0.0 1.0 1.0 1.0
Light.getBrightness() 0.0 1.0 1.0 1.0
Light.setBrightness(double) 0.0 1.0 1.0 1.0
Paralle.add(Connection) 0.0 1.0 1.0 1.0
Paralle.getPara() 0.0 1.0 1.0 1.0
R.R(int, String) 0.0 1.0 1.0 1.0
R.display() 0.0 1.0 1.0 1.0
Connection.update(double) 1.0 1.0 2.0 2.0
F.add() 1.0 1.0 1.0 2.0
F.sub() 1.0 1.0 1.0 2.0
K.display() 1.0 1.0 1.0 2.0
K.setIsOpen() 1.0 1.0 1.0 2.0
B.dy(double) 2.0 1.0 1.0 2.0
Device.compareTo(Device) 2.0 2.0 2.0 2.0
R.dy(double) 2.0 1.0 2.0 2.0
Connection.getM() 3.0 3.0 2.0 3.0
Paralle.dy(double) 3.0 1.0 3.0 3.0
Paralle.isOpen() 3.0 1.0 2.0 3.0
D.dy(double) 4.0 1.0 1.0 4.0
T4.find(String, ArrayList) 4.0 4.0 3.0 4.0
A.dy(double) 7.0 1.0 1.0 8.0
Connection.isAllClose() 7.0 1.0 4.0 5.0
Connection.sumR() 7.0 1.0 4.0 4.0
Paralle.sumR() 7.0 2.0 3.0 6.0
Device.init(String) 8.0 1.0 9.0 9.0
Connection.connectionAll() 10.0 3.0 6.0 8.0
T4.main(String[]) 49.0 4.0 20.0 20.0
Total 123.0 64.0 101.0 125.0
Average 2.3653846153846154 1.2307692307692308 1.9423076923076923 2.4038461538461537

類:

Class OCavg OCmax WMC
L 1.0 1.0 4.0
Light 1.0 1.0 3.0
B 1.3333333333333333 2.0 4.0
R 1.3333333333333333 2.0 4.0
F 1.4 2.0 7.0
K 1.5 2.0 6.0
D 1.6666666666666667 3.0 5.0
Device 2.0 9.0 18.0
A 2.3333333333333335 5.0 7.0
Connection 2.625 5.0 21.0
Paralle 2.8 6.0 14.0
T4 11.0 18.0 22.0
Total 115.0
Average 2.2115384615384617 4.666666666666667 9.583333333333334

踩坑心得

1.對用空指標的引用,沒有考慮到產生空指標的情況

家居強電電路模擬程式-2

在得到並聯裝置的的方法中,返回值是物件,在透過返回值呼叫其成員方法的過程發生引用空指標
改進前的程式碼:

if (allConnection.getM().sumR() != 0 && allConnection.isAllClose()) {
            //進行電路裝置狀態更新
            allConnection.connectionAll();
        }

結果如下:

改進後的程式碼:

if (allConnection.isAllClose()){
            if (allConnection.getM()!=null&&allConnection.getM().sumR()!=0){
                allConnection.connectionAll();
            }
            allConnection.connectionAll();
        }

結果如下:

2.對於Collections的排序方法不熟練

答題程式4

在排序答卷資訊時,自己寫了一個排序的函式,經過這些天的學習後,我發現可以用更加簡短的程式碼來實現排序,即利用Collections裡面的sort方法,讓答卷實現Comparable介面

改進前:

//對答卷排序
    //現學號,後試卷號
    public static void sortAnswer(ArrayList<answer> An) {
        for (int i = 0; i < An.size(); i++) {
            for (int j = i; j < An.size(); j++) {
                if (Integer.parseInt(An.get(i).getStuID()) > Integer.parseInt(An.get(j).getStuID())) {
                    answer temp = An.get(i);
                    An.set(i, An.get(j));
                    An.set(j, temp);
                } else if (Integer.parseInt(An.get(i).getStuID()) == Integer.parseInt(An.get(j).getStuID())) {
                    if (An.get(i).getNum() > An.get(j).getNum()) {
                        answer temp = An.get(i);
                        An.set(i, An.get(j));
                        An.set(j, temp);
                    }
                }
            }
        }
    }

改進後:

在答卷類中:

public int compareTo(answer o) {
        if (Integer.parseInt(this.getStuID()) != Integer.parseInt(o.getStuID()))
            return Integer.parseInt(this.getStuID()) - Integer.parseInt(o.getStuID());
        else
            return this.getNum() - o.getNum();
    }

在主函式中:

Collectios.sort(An);

3.對於類設計的缺陷

家居強電電路模擬程式-1

在該題目中,我沒有建立一個連線類模擬串聯電路的效果,導致只能處理兩個裝置的連線情況,因此我還加了許多判斷,使程式碼的可讀性降低,因為在第一次設計中我一直在想怎麼透過引腳傳遞電壓,感覺很複雜。

程式碼如下:

//更新連線裝置電壓
        //更新電壓之後,還要更新引數
        for (int i = 0; i < devices.size(); i++)
            devices.get(i).update();
        if (flag == 0)
            devices.get(1).setInput(devices.get(0).getOutput());
        for (int i = 0; i < devices.size(); i++)
            devices.get(i).update();
        //輸出狀態
        if (ks.size()!=0)
        for (K k : ks) {
            k.display();
        }
        for (int i = 0; i < devices.size(); i++) {
            if (!(devices.get(i) instanceof K))
            devices.get(i).display();
        }

因此我在第二次迭代的程式中加上了串聯裝置這個類,同時不再考慮透過引腳傳遞電壓,直接計算每個裝置的分壓即可。

//連線類
class Connection extends Device implements kisAllOpen, sumR {
    private ArrayList<Device> sd = new ArrayList<>();

    public Connection(String s) {
        setName(s);
    }

    //連線裝置
    public void connectionAll() {
        if (!isAllClose())
            return;
        if (getM()!=null&&getM().isOpen())
            return;
        Device first = sd.get(0);
        double initInput;
        if (first.getName() != null && (first.getName().equals("L") || first.getName().equals("F"))) {
            if (first instanceof L)
                initInput = ((L) first).getCount() * 100 * 220 / 100;
            else
                initInput = ((F) first).getCount() * 220 * 3 / 10.0;
            //計算總電流
        } else
            initInput = 220;
        double I = I(initInput);
        update(I);
    }

    //新增裝置
    public void add(Device d) {
        sd.add(d);
    }

    //得到並聯電路
    public Paralle getM() {
        for (Device device : sd) {
            if (device.getId() == 0)
                return (Paralle) device;
        }
        return null;
    }

    @Override
    public boolean isAllClose() {
        int cnt = 0;
        int open = 0;
        for (Device value : sd) {
            if (value.getName() != null && value.getName().equals("K")) {
                cnt++;
                K k = (K) value;
                if (k.getIsOpen() == 1) {
                    open++;
                }
            }
        }
        return cnt == open;
    }

    @Override
    public double sumR() {
        double sum = 0;
        if (isAllClose()) {
            for (Device value : sd) {
                if (value instanceof Paralle)
                    sum += ((Paralle) value).sumR();
                else
                    sum += value.r;
            }
        }
        return sum;
    }
    //計算總電流
    public double I(double iu) {
        return iu / sumR();
    }

    //拿到總電流之後遍歷串聯裝置 呼叫dy方法把裝置引數更新
    public void update(double I) {
        for (Device device : sd) {
            device.dy(I);
        }
    }
}

改進建議

1.答題程式4

對於資料的儲存不僅可以透過建立類來實現,但最優的方法還是透過一些資料結構來儲存題目,這樣可以減少程式的複雜性,ArrayList,HashMap等,都是儲存引用資料型別的好手,可以在程式中根據不同的資料需求來選擇對應的資料結構來儲存資料。

2.家居強電電路模擬程式-1

類之間的關係應該去仔細分析,不能為了得分而使得程式越來越臃腫,新增串聯電路類,用來處理裝置連線,再類中加上判斷該電路是否開路的方法,如果開路,直接輸出結果,否則根據電壓更新裝置狀態。

3. 家居強電電路模擬程式-1

類的成員方法仍有改進的餘地,要服從單一職責原則,每個方法不能做太多的事,可以把相同的操作抽取出來作為一個獨立的方法,同時,對於類間的關係還要再仔細考慮考慮。

總結

透過這三次作業,對於物件導向程式設計有了進一步的瞭解。三次作業的難度不小,後兩次作業需要自己設計類,設計成員方法和成員變數,對問題分析能力有一定的要求。在編寫程式的過程中,開始逐漸的考慮類的規範設計,同時解決問題的能力逐步提高。

學到了什麼
透過這三次作業,我熟悉了集合排序的方法,以及怎麼設計合理的類間關係,以及如何使用繼承來實現程式的可擴充套件性,但在三次作業的過程中,我也發現了自己的問題,基礎不是很牢靠,學習方法可能存在一些問題,我正在慢慢的改正。同時,仍需要進一步的學習java,瞭解設計原則,多多思考,多動手敲程式碼。以便日後解決需求更加複雜的程式設計。