OOP第二次blog

搞笑的啊發表於2024-06-09

前言

(1) 第四次題集的第一題已經經過了三次迭代,需要考慮到的情況越來越複雜,難度也越來越大,這讓我感受到物件導向程式設計的基本原則的重要性,此前每一次迭代都應該謹慎,切忌為了偷懶就破壞類之間的關係(我第二次迭代就偷懶過了所有測試點,然後最後一次就狂改)。

(2) 第五次題集只有三道題,也是第一次迭代難度不大,但是進過前面幾次大作業的磨練,我對類的使用明顯愈發熟練,本次大作業用到了抽象類,我將電路裝置定義為了抽象類,很輕鬆的AC這道題。

​ 然後是這題集的第三題,首次接觸了集合型別,這道題是改錯題,宣告型別是Collection,實際型別是ArrayList,然後使用迭代器遍歷所以員工,難度一般。

(3) 第六次題集只有一道題(看起來挺唬人),難度也沒有很大,經過第一次迭代,加入了並聯電路,電路裝置也變多了,但是有一個關於精度的計算很難考慮到,花了很長時間除錯。

第四次題集

設計與分析:

本次題集除了第三題其他都是很基礎的題目,就分析一下最後一題,以下是類圖:

本次大作業我設計的主要的幾個類分別是

(1) 題目類(Question):用於儲存一道題目的資訊以及處理的方法。(與系列 1 相同,無變化)

private int num;// 題號

private String content, // 題目內容

standardAnswer;// 標準答案

boolean matchingStandardAnswers(String answer):判斷是否符合標準答案

boolean isValid=true;//是否是有效的題目

void disabled() //當題目被刪除,設定題目為失效狀態

ArrayList answers //記錄所有引用了該題目的答案

(2)試卷題目類(Question_Paper):用於儲存試卷中的題目資訊。由於試卷中的題目序號與題

目本身的題號不一致。且題目在不同試卷中的分值可能不一樣,因此,設計試卷題目類。

int p_q_num;//試卷中題目的順序號

Question question;//題目類的物件,儲存題目資訊

int question_score;// 題目分值

int judge_markAnswer(String answer) // 判斷題目得分

(3)試卷類(TestPaper):儲存和處理一張試卷的資訊以及處理的方法,

int questionNum;// 題目數量

HashMap<String, Question_Paper> questions;//題目清單

void inputQuestion(int num, Question question):新增一道題目

void printQuestions():輸出題目的內容~標準答案

boolean markQuestion(int num, String answer):判斷第 num 題的正確性。

int sum-儲存總分

int questionQuantity-儲存題目數量

int getSum()****:獲得總分

(4)答案類(Answer):用於儲存答卷中一道答案的資訊。

Question_Paper question;

String answer;

boolean mark;// 每一題的正確性

int score=0;// 每一題的得分

void calScore()//計算得分

void disable() //答案對應的題目失效,判分為 0

(5)答卷類(AnswerPaper):儲存和處理一張答卷的資訊以及處理的方法

TestPaper paper;//試卷資訊

String[] answers;//每一題的答案資訊boolean[] marks;//每一題的判題結果(對/錯)

void printQ_A(int num):輸出第 num 題的題目和答案(卷面答案,非標準答案)

boolean getJudge(int num):獲得第 num 題的判題結果

void printJudges() :// 輸出所有的得分以及總分,以空格分隔

String stuNum;// 學號

void** printErrorNumQuestion(Answer answer) // 輸出試卷中錯誤題目號的題目

void** printInvalidQuestion(Answer answer)// 輸出失效題目資訊

(5)學生類(Student):儲存學生的資訊

String stuNum, stuName;

踩坑心得:

(1) 首先這次的大作業,我最後還有一個測試點沒有過,現在我弄明白了,但是時間已經過了,這個題目的空白卷輸入,就是隻有一個卷子,沒有題目序號,應該按正常的卷子輸出,我的正規表示式有問題,沒有捕捉到這種情況。

例如:

輸入:#N:1 #Q:1+1= #A:2

T:1 1-5

X:20201103 Tom

S:1 20201103 #A:

end

應該將這種情況正常輸出。

(2) 我這次又犯了上次同一個錯誤,正規表示式寫錯了,導致捕捉不準確。

public static boolean checkquestion(String str)
    {
        String regstr="^#N:\\d*\\s*#Q:.*#A:.*";
        Pattern pattern= Pattern.compile(regstr);
        Matcher matcher=pattern.matcher(str);
        if(matcher.find())
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    public static boolean checktest(String str)
    {
        String regstr1="(\\d*)-(\\d*)";
        Pattern pattern1=Pattern.compile(regstr1);
        Matcher matcher1=pattern1.matcher(str);

        String regstr2="^#T:\\s*\\d+\\s*((\\d+\\s*-\\s*\\d+\\s*)+)$";
        Pattern pattern2=Pattern.compile(regstr2);
        Matcher matcher2=pattern2.matcher(str);
        if(matcher2.find())
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    public static boolean checkstudent(String str)
    {

        String regstr2="^#X:\\s*\\d+\\s(.*?)+(?:-\\d+\\s(.*?)+)*$";
        Pattern pattern1=Pattern.compile(regstr2);
        Matcher matcher1=pattern1.matcher(str);
        if(matcher1.find())
        {
            return true;
        }
        else
        {
            return false;
        }
    }

這幾個check方法最後在同學的幫助下終於是改對了,下一次一定要仔細讀取題目意思,謹慎考慮好之後在開始,不然寫到一半出了問題很崩潰的。

改進建議:

這次寫的大作業,還是出現前三次的問題,就是類之間的關係有些模糊不清,寫到後面經常性用錯,但是比起前幾次的亂七八糟的程式碼,至少類的功能分的清楚了。

(1) 這次大作業如果將同學與答卷號碼用HashMap儲存起來,後續只需要透過迭代器就可以遍歷所以答卷和學生,不用再像現在這樣遍歷所以答卷找同學,可以增強程式碼的可讀性,而且程式碼也會更簡潔,維護起來也容易。

(2) 試卷題目類也可以使用List泛型代替ArraysList儲存試卷與題目的關係,這樣可以直接使用Collections.sort方法進行排序。

第五次題集:

設計與分析:

​ 首先是這次題集的第一題,這是第一次出現,還沒有進行迭代,特殊條件比較多難度一般,以下是類圖:

這裡主要設計了兩個類:

(1) 電路裝置類(Equipment):描述所有電路裝置的公共特徵。

abstract class Equipment {
    int switchstyle;//如果是開關 開關的狀態

    public int getSwitchstyle() {
        return switchstyle;
    }


    public boolean IfPathway()
    {
        return false;
    }
    double voltage;//裝置分到的電壓

    public double getVoltage() {
        return voltage;
    }

    abstract public void setVoltage(double c);

    double resistor;//電阻

    abstract double getResistor();

    public double getContact1() {
        return contact1;
    }

    public void setContact1(double contact1) {
        this.contact1 = contact1;
    }

    String name;//裝置名

    public String getName() {
        return name;
    }

    double contact1;//觸腳1的電壓

    public double getContact2() {
        return contact2;
    }

    public void setContact2(double contact2) {
        this.contact2 = contact2;
    }

    double contact2;//觸腳2的電壓

    abstract void display();//輸出裝置的狀態

    abstract void close();

}

(2) 串聯電路類(Series):一條由多個電路裝置構成的串聯電路,也看成是一個獨立的電路裝置。以下是部分程式碼:

class Series {
    public double getVoltage() {
        return voltage;
    }

    public void setVoltage(double voltage) {
        this.voltage = voltage;
    }

    private double voltage;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String name;

    public void VoltageDivision()
    {
        double aver=getVoltage()/getResistor();
        for(int i=0;i<list.size();i++)
        {
            Equipment e= list.get(i);
            if(e instanceof Switch || e instanceof Governor || e instanceof ContinueGovernor)
                continue;
            else
            {
                e.setVoltage(aver*e.getResistor());
            }
        }
    }

    private double resistor;

    public double getResistor() {
        resistor = 0;
        for (int i = 0; i < list.size(); i++) {
            Equipment e = list.get(i);
            if (e instanceof Switch || e instanceof Governor || e instanceof ContinueGovernor)
                continue;
            else
                resistor += e.getResistor();
        }
        return resistor;
    }
}

將裝置類定義為抽象類,其餘所有電器繼承與裝置類,然後使用串聯電路類,然後將每個電路元件存入其中,
判斷控制電器的狀態,判斷電路是否通路,模擬各種情況,最後輸出各個元件的狀態。

其中串聯電路類內部使用ArrayList型別儲存每一個裝置,最後確定沒一個開關的狀態,最後進行排序輸出即可。

然後是這次題集的第三道題,首次接觸到了集合型別,這道題是一道改錯題,使用Collection c = new ArrayList();
儲存每一個員工,使用迭代器遍歷輸出,難度也一般。

//主函式
public class Main {

    public static void main(String[] args) {
        // 1、建立有序集合物件
        Collection <Employee>c  = new ArrayList();
        Scanner sc = new Scanner(System.in);
        // 建立3個員工元素物件
        for (int i = 0; i < 3; i++) {

            String employeeName = sc.nextLine();
            int employeeAge = sc.nextInt();
             sc.nextLine();
            Employee employee = new Employee(employeeName, employeeAge);
            c.add(employee);
        }



        // 2、建立迭代器遍歷集合
        Iterator <Employee>it=c.iterator();

        //3、遍歷
        while (it.hasNext()) {

            //4、集合中物件未知,向下轉型
            Employee e = (Employee) it.next();

            System.out.println(e.getName() + "---" + e.getAge());
        }
    }

這是Main方法部分,宣告瞭一個迭代器it,接收集合c的迭代器,然後遍歷。

踩坑心得:

(1) 這次的大作業難度不大,但是由於看漏了條件,導致最後有幾個測試點一直沒有透過,這道題允許開關多次出現,最後輸出結果要排序。後來我就宣告瞭一個ArrayList型別專門儲存所有開關,只有一遇見開關,就將這開關物件存入,判斷是否有開關存在,如果有就排序輸出。同時開關類也實現了Comparable介面。

@Override
    public int compareTo(Switch o) {
        if (name.compareTo(o.getName()) < 0)
            return -1;
        else
            return 1;
    }

(2) 還有本次大作業的分檔變速器是必須在0-3之間,不能炒出這個範圍的,需要加上特判條件。

例如:

 else if(Line.contains("#F"))
            {
                if(Line.contains("+")&&gears<3)
                {
                    gears++;
                }
                else if(Line.contains("-")&&gears>0)
                {
                    gears--;
                }
            }

這樣保證檔位不會超出範圍。

改進建議:

(1) 下一次迭代肯會將入並聯電路,所以應將串聯電路繼承於電器裝置類,這樣方便下次迭代。

(2) 對於其他電路裝置可以像對待開關一樣操作,這次大作業說明了其餘裝置只出現一次,但是下次不一定,所以全部使用List泛型儲存,在實現Comparable介面,方便排序。

以吊扇類為例:

 @Override
    public int compareTo(Fan o) {
        if(name.compareTo(o.getName())<0)
            return -1;
        else
            return 1;
    }
    if(D.size()!=0)
        {
            Collections.sort(D);
            for(int i=0;i< D.size();i++)
                D.get(i).display();
        }

這樣所有電路裝置多次出現排序的問題就可以解決。

第六次題集:

設計與分析:

這次大作業比起上次大作業多了一個並聯電路,將並聯電路視為一種特殊的電路元件即可,但同時也需要主要並聯電路的電阻和電壓的計算涉及到精度問他。以下是新的類圖:

這次主要的增加的新類:

(1) 並聯電路類(Parallel):繼承電路裝置類,也看成是一個獨立的類。

以下是主要屬性和方法:

class Parallel extends Equipment {

    private ArrayList<Series> seriesList;

    Parallel(ArrayList<Series> seriesList) {
        this.seriesList = new ArrayList<>();
        this.seriesList = seriesList;
    }

    @Override
    public void setVoltage(double c) {
        for(int i=0;i<seriesList.size();i++)
        {
            Series series=seriesList.get(i);
            if(series.IfPathway())
            {
                series.setVoltage(c);
                series.VoltageDivision();
            }
        }
    }


    @Override
    public boolean IfPathway()//檢查並聯是否通路
    {
        for(int i=0;i<seriesList.size();i++)
        {
            if(seriesList.get(i).IfPathway())
            {
                return true;
            }
        }
        return false;
    }

    @Override
    double getResistor() {
        for(int i=0;i<seriesList.size();i++)
        {
            if(seriesList.get(i).Ifshort())
            {
                return 0;
            }
        }
        double r = 0;
        double R = 0;
        for (int i = 0; i < seriesList.size(); i++) {
            Series s = seriesList.get(i);
            if(s.IfPathway())
            {
                R += (1.0 / s.getResistor());
            }
        }
        r =  (1.0 / R);
        return r;
    }


    @Override
    void display() {

    }

    @Override
    void close() {

    }
}

並聯電路也是一個特殊的電路裝置,所以他也有名字,電阻,電壓。需要注意的是並聯的電阻計算和電壓的計算會有精度問題。

題幹中說明了並聯電路所包含的串聯電路會在其之前輸入,所以只要將每一條路(出了連線電源的串聯)存在一起,建立並聯電路時一起加入就行。

踩坑心得:

(1 )這道題涉及到並聯電路電阻的計算,那麼就肯定會出現一些,無限迴圈小數等非常規小數,導致計算結果出錯。

例如:

輸入:

#T1:[IN K1-1] [K1-2 D2-1] [D2-2 D3-1] [D3-2 OUT]
#T2:[IN K2-1] [K2-2 D1-1] [D1-2 OUT]
#M1:[T1 T2]
#T3:[VCC L1-1] [L1-2 M1-IN] [M1-OUT GND]
#K1
#K2
#L1:1.00
end

錯誤輸出:

@K1:closed
@K2:closed
@L1:1.00
@D1:360
@D2:199
@D3:199

這是一個人標準錯誤的答案。

正確輸出:

@K1:closed
@K2:closed
@L1:1.00
@D1:360
@D2:200
@D3:200

當D2的轉速為199.99999時最後應輸出200,而不是199。這是計算電壓是,沒有處理好。需要進行精度判斷,如:

double TF=c1-Math.floor(c1);
        double TC=Math.ceil(c1)-c1;
        if(Math.min(TF,TC)<1e-10)
        {
            c1=Math.round(c1);
        }

這需要每次計算電壓時加上這個判斷即可AC。

改進建議:

​ 下次迭代是可能會加入並聯電路串聯的情況,所以在加入並聯電路時需要進行遍歷所有此前的串聯,找到符合的串聯,再加入並聯電路中。為了應對各種迭代情況,所以類之間的耦合性一定要設法降低,不能導致牽一髮而動全身,這樣修改程式碼的工作量太大了。

總結:

對物件導向程式設計(OOP)概念理解加深了,Java是一種物件導向的語言,透過這三次作業,更深入地理解類、物件等概念。

然後程式碼組織與結構的能力增強,編寫大型Java程式需要良好的程式碼組織和結構。

逐漸熟練如何使用介面、類組織程式碼,以及如何使用註釋來提高程式碼的可讀性。

除錯能力提高,除錯是開發過程中不可或缺的一部分。學會如何使用除錯工具和技巧來定位和修復程式碼中的錯誤。

還精通了許多類的使用例如LinkList,ArraysList,HashMap等等。

然後是一些建議:
對於模擬家用電器控制系統可以在並聯電路中加入一些特殊的元件,如電阻器,變壓器等等,這樣更符合實際情況。

之後的PTA作用可以加入一些演算法資料結構的知識,促進我們自學,這幾次作用都沒有什麼演算法知識。

可以加入更多關聯實際的題目,增強對物件導向的理解。