OOP第三次Blog

我超厉害的發表於2024-06-30

前言:

(1) 第七次題集只有一道題目——家居強電電路模擬程式-3,這是第三次迭代,這次迭代主要的點有四個。

​ 首先本次迭代新增了線路中存在多個串聯起來的並聯電路。不同於上次的單並聯,本次更復雜。

​ 然後本次還新迭代了一種控制器——互斥開關,互斥開關一共有三個觸腳,預設狀態是1,2相連,但是互斥開關可以反接所以互斥開關還需要考慮接入的方向,這顯然需要需要引入一個新的變數進行判定。然後就是這個互斥開關是有電阻的,並不是理想開關,所以對於短路的判斷需要重新考慮。

​ 再然後是串聯中包含串聯電路,不論是主路還是支路都有可能出現串聯電路,這一點還是比較好處理的,本身就是將串聯電路設定為了電路裝置。

​ 最後是引入了一種受控電器——受控窗簾,收到電壓和室內亮度的控制,預設狀態為全開。

總體而言本次題解較上次迭代而言難度增幅不算太大。

(2) 第八次題集同樣只有一道題目——家居強電電路模擬程式-4,這是本系列題目的第四次迭代,這次迭代引入了很多新的功能,主要有五點。

​ 首先是增加管腳電壓的顯示,在輸出每個電器的狀態資訊後,再依次輸出該電器每個管腳的電壓。這一點是最讓人頭疼的地方,需要好好回憶一下高中的電路,同時有幾種特殊情況需要注意情況,不然很容易錯。

​ 然後是電流限制,電器在工作時,過大的電流會引起電器過熱,從而燒壞電路。本次迭代,每個元器件都有最大電流的設定,當實時電流超過最大電流時,在該電器輸出資訊的最後加入提示“exceeding current limit error”,與前面的資訊之間用英文空格分隔。

​ 再次是短路檢測,如果電路出現無窮大的電流造成短路,所有元器件資訊不輸出,僅輸出提示“short circuit error”。

​ 再然後是並聯電路中包含並聯,本次迭代考慮並聯電路中包含並聯電路的情況,即構成並聯電路的串聯電路可以包含別的並聯電路。這一點比較好處理,因為並聯電路本身也別看作一個電路裝置,在支路的輸入時,加入這種情況就行。

​ 最後是二極體,增加二極體元件,其電路特性為:正向導通,反向截止;其電器符號如圖4所示,當電流從左至右流過時,二極體導通”conduction”,電阻為0;電流從右至左流動時,二極體截止”cutoff”,電阻無窮大,相當於開關開啟。

​ 總體而言,本次迭代的難度增加了很多,讓人眼前一黑,主要是管腳電壓的顯示,確實需要考慮很多情況。

第七次題集

設計與分析:

首先本次迭代比起上次需要將串聯電路也設定為一個電路裝置類,以下是一個簡易類圖:

對串聯類同時也加入了很多新的方法,以下是部分程式碼展示:

class Series extends Equipment{
    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||e instanceof Hswitch)
                continue;
            else if(e instanceof Series)
            {
                e.setVoltage(aver*e.getResistor());
                e.VoltageDivision();
            }
            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 if(e instanceof Hswitch&&e.IfHswitchPath(this.name))
            {
                resistor+=e.getResistor();
            }
            else
            {
                resistor += e.getResistor();
            }

        }
        return resistor;
    }

    public boolean IfPathway()
    {
        for(int i=0;i< list.size();i++)
        {
            Equipment e= list.get(i);
            if(e instanceof Switch)
            {
                if(e.switchstyle==0)
                {
                    return false;
                }
            }
            else if(e instanceof Parallel&&e.IfPathway()==false)
            {
                return false;
            }
            else if(e instanceof Hswitch&&e.IfHswitchPath(this.name)==false)
            {
                return false;
            }
            else if(e instanceof Series&&!e.IfPathway())
            {
                return false;
            }
        }
        return true;
    }

    public boolean Ifshort()//檢查是否短路 短路就是true 不短路就是false
    {
        boolean flag=true;
        for(int i=0;i<list.size();i++)
        {
            Equipment e= list.get(i);
            if(e instanceof Switch&&e.switchstyle==1)
            {
                continue;
            }
            else if(e instanceof Series&& e.Ifshort())
            {
                continue;
            }
            else
            {
                flag=false;
                break;
            }
        }
        return flag;
    }
    public ArrayList<Equipment> getList() {
        return list;
    }

    public void setList(ArrayList<Equipment> list) {
        this.list = list;
    }

    private ArrayList<Equipment> list;

    Series() {
        list = new ArrayList<>();
    }

    public void addEquipment(Equipment e) {
        list.add(e);
    }

    public Equipment getEquipment(int idex) {
        return list.get(idex - 1);
    }

}

在串聯電路類中對短路和斷路的判定方法改了,如果遇見了串聯電路,則再次呼叫該方法,然後就是setVoltage()方法這是負責分壓的方法,遇見串聯電路時,和遇見其他受控裝置一樣分壓,但是要多加一點,那就是再次分壓。

然後就是本次新迭代的受控裝置——互斥開關,這個在輸入時就需要十分注意它的方向,以下是部分程式碼:

class Hswitch extends Equipment implements Comparable<Hswitch>{

  
    Hswitch(String name)
    {
        this.name=name;
    }
    public void setHchoice(int hchoice)
    {
        if(hchoice%2==0)
        {
            Hchoice=2;
        }
        else
            Hchoice=3;
    }
    private int Hchoice;
     String H2;
    public void setH2(String tname)
    {
        H2=tname;
    }
     String H3;

    public void setH3(String tname)
    {
        H3=tname;
    }
    @Override
    double getResistor() {
        if(Hchoice==2)
        return 5;
        else
            return 10;
    }
    @Override
    public boolean IfHswitchPath(String tname)
    {
        if(Hchoice==2&&H2!=null&&H2.equals(tname))
        {
            return true;
        }
        else if(Hchoice==3&&H3!=null&&H3.equals(tname))
        {
            return true;
        }
        else
            return false;
    }

    @Override
    void display() {
        if(Hchoice==2)
        {
            System.out.println("@"+name+":closed");
        }
        else
            System.out.println("@"+name+":turned on");
    }

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

互斥開關中有一個特殊的布林變數Hchoice,專門判斷互斥開關的觸腳1與那個觸腳相連,

其中的IfHswitchPath()方法用於判斷某條路這個互斥開關是否接通,主要用於判斷某條串聯是否斷路。

互斥開關中也實現了Comparable這個介面,在輸出是需要進行排序。

最後是本次迭代的一個新的受控裝置——受控窗簾,它只受電壓和室內光線的控制,所以判斷窗簾的開啟程度需要最後判斷,在所有分壓完成後,以下是部分程式碼:

class Curtains extends Equipment implements Comparable<Curtains>{

    private int sumlux;

    Curtains(String name)
    {
        this.name=name;
    }
    @Override
    public void setVoltage(double c) {
        voltage=c;

    }

    public void setSumlux(int sumlux)
    {
        this.sumlux=sumlux;
    }
    @Override
    double getResistor() {
        return 15;
    }

    @Override
    void display() {
        if(voltage<50||sumlux<50)
        {
            System.out.println("@"+name+":100%");
        }
        else if(sumlux>=50&&sumlux<100)
        {
            System.out.println("@"+name+":80%");
        }
        else if(sumlux>=100&&sumlux<200)
        {
            System.out.println("@"+name+":60%");
        }
        else if(sumlux>=200&&sumlux<300)
        {
            System.out.println("@"+name+":40%");
        }
        else if(sumlux>=300&&sumlux<400)
        {
            System.out.println("@"+name+":20%");
        }
        else if(sumlux>=400)
        {
            System.out.println("@"+name+":0%");
        }
    }

    @Override
    void close() {
        voltage=0;
    }

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

其中的sumlux就是所以燈泡的光亮總和,然後和電壓一起判定就行了,這是一個比較簡單地受控裝置,

同時也同其他裝置一樣,需要實現Comparable介面,從而重寫compareTo方法,便於在最後輸出之前進行排序。

踩坑心得:

(1) 開關的閉合情況我沒有正確判斷,我之前是根據字串方法contain()方法進行判斷是否改變情況,而我對於輸入資訊中的調節開關的資訊沒有進行字串的抓取,就導致一些判斷錯誤。

例如:開關K11,遇見判斷資訊#K112也會進行調節,可是這兩個開關並不是一個,導致答案錯誤。

例如輸入:

#T1:[IN K12-1] [K12-2 D2-1] [D2-2 OUT]
#T2:[IN K123-1] [K123-2 D1-1] [D1-2 OUT]
#T3:[VCC K1-1] [K1-2 T1-IN] [T1-OUT T2-IN] [T2-OUT GND]
#K1
#K12
#K123
end

就會造成錯誤輸出:

@K1:turned on
@K12:closed
@K123:closed
@D1:0
@D2:0

因此在輸入開關的判斷資訊是就需要進行字串的抓取,

就像下面這樣:

 if(Line.contains("#K"))
           {
                String regstr="#(K[0-9]+)";
                Pattern pattern=Pattern.compile(regstr);
                Matcher matcher= pattern.matcher(Line);
                String name="";
                if(matcher.find())
                {
                    name=matcher.group(1);
                }
                changNumList.add(name);
            }

這樣在比較的時候用equal()方法就不會造成錯誤。

(2) 然後是一個非常非常非常非常不該出現的錯誤,可是我又錯了,我由於有一個雙層迴圈,內層迴圈數和外層的迴圈數寫反了,導致有時候會出現null的引用,我一直以為是正規表示式出現了錯誤,找了很久很久才發現,是我寫反了,浪費了大量時間。

這件事告訴我們寫程式碼一定要耐心和細心,不然一步錯,想要回頭找出來是很艱難的過程,也很痛苦。

改進建議:

(1)程式碼結構最佳化

  • 重構程式碼,確保程式碼遵循良好的程式設計實踐,如程式碼的封裝、繼承和多型。
  • 最佳化類的設計,確保每個類都有單一職責,並且職責之間保持清晰的界限。
  • 使用包和類層次結構來組織程式碼,保持清晰的命名規範,方便其他人理解。

(2)效能最佳化

  • 分析程式的效能瓶頸,並採取相應的最佳化措施。

  • 對於頻繁執行的計算,考慮使用更高效的演算法或資料結構。

第八次題集

設計與分析:

這次迭代新加入了二極體類,但是類與類之間的基本關係沒有變化,

基本類圖如下:

本次新增的二極體類,如果正向接入則相當於閉合開關,反向接入則相當於開關開啟,所以對於短路和斷路這些情況的特殊判定也要增加,以下是部分程式碼:

class Diode extends Equipment implements Comparable<Diode> {

    private int pre;

    Diode(String name, int pre) {
        this.name = name;
        this.pre = pre;
        doidepre = pre;
    }

    @Override
    int getRatedcurrent() {
        return 8;
    }

    @Override
    public void setVoltage(double c) {
        voltage = 0;
    }

    @Override
    double getResistor() {
        return 0;
    }

    @Override
    void display() {
        if (pre == 1) {
            System.out.print("@" + name + ":conduction" + " " + (int) contact1 + "-" + (int) contact2);
        } else {
            System.out.print("@" + name + ":cutoff" + " " + (int) contact1 + "-" + (int) contact2);
        }
        if (Ifexceed) {
            System.out.println(" exceeding current limit error");
        } else {
            System.out.println();
        }
    }
    @Override
    public int compareTo(Diode o) {
        if (this.name.compareTo(o.getName()) < 0)
            return -1;
        else
            return 1;
    }
}

其中變數pre就是前驅觸腳,根據輸入資訊確定前驅觸腳,然後在進行判斷是否斷開。

然後是電流的限制,就在電路裝置類中都加入了一個方法getRatedcurrent(),用來獲取額定電流,並透過方法Ifexceed分流,從而確定每一個電器是否超出限制。

以下是方法程式碼:

public void Ifexceed(double current) {
        for (int i = 0; i < list.size(); i++) {
            Equipment e = list.get(i);
            if (e instanceof Parallel) {
                e.Ifexceed(current);
            } else if (e instanceof Series) {
                e.Ifexceed(current);
            } else if (e instanceof Hswitch) {
                if (e.IfHswitchPath(this.name) && current > e.getRatedcurrent()) {
                    e.Ifexceed = true;
                }
            } else {
                if (current > e.getRatedcurrent()) {
                    e.Ifexceed = true;
                } else
                    e.Ifexceed = false;

                if(e instanceof Diode)
                {
                    if(current==0)
                    {
                        e.Ifdiode=false;
                    }
                    else
                    {
                        e.Ifdiode=true;
                    }
                }

            }
        }
    }

對每一條支路進行分流,確定它的電流,串聯電路電流相等。

然後是管腳電壓的計算,首先確定總路的電壓,然後確定·總路中所有裝置(當然也包括並聯和串聯等)的電壓,

然後依次減去,可以得到每個用電器兩端的電壓,如果是並聯或者串聯,則繼續呼叫相應方法。

主要判斷短路和斷路的情況,其中斷路是還要考慮斷開的裝置的觸腳電壓可能是0,這需要額外判斷。

同時注意這次題目將受控窗簾的預設情況改了,和上次不一樣。

以下是計算的部分程式碼:

public void footvoltage(double v, double v1) {
         ~~~
        double total = v;

        for (int i = 0; i < list.size(); i++) {
            Equipment e = list.get(i);
            if (e instanceof Governor || e instanceof ContinueGovernor)
                continue;
            else if (e instanceof Series) {
                e.footvoltage(total, total - e.getVoltage());
                total -= e.getVoltage();
            } else if (e instanceof Parallel) {
                e.footvoltage(total, total - e.getVoltage());
                total -= e.getVoltage();
            } else if (e instanceof Hswitch) {//有問題

                if (e.ifhswitch) {
                    e.contact1 = total;
                    if (e.IfHswitchPath2(this.name)) {
                        e.contact2 = total - e.getVoltage();
                    } else {
                        e.contact3 = total - e.getVoltage();
                    }
                    total -= e.getVoltage();
                } else {
                    if (e.IfHswitchPath2(this.name)) {
                        e.contact2 = total;
                        e.contact1 = total - e.getVoltage();
                    } else {
                        e.contact3 = total;
                        e.contact1 = total - e.getVoltage();
                    }
                    total -= e.getVoltage();
                }
            } else {

                if (e.Ifrigth) {
                    e.contact1 = total;
                    e.contact2 = (total - e.getVoltage());
                    total -= e.getVoltage();
                } else {
                    e.contact2 = total;
                    e.contact1 = (total - e.getVoltage());
                    total -= e.getVoltage();
                }

            }
        }
    }

這只是正常的情況,斷路和短路的程式碼過於繁瑣,就不展示了。

最後還有一個短路檢測,這個就很簡單了,只有直接判斷總路是不是短路,如果是直接輸出就行,然後return。

  if (mainPathway.Ifshort())//短路
        {
            System.out.println("short circuit error");
            return;
        }

踩坑心得:

這次題集難度很大,特殊情況也很多,所以真的踩了很多坑,以下是幾個重點的坑:

(1) 首先是讀題沒有仔細看,想當然的以為窗簾這些受控電器沒有變化和上次一樣,結果它改了,預設情況改成了全開。

導致錯了一個測試點,最後仔細看題終於發現了。

(2) 還有就是電路的電壓觸腳沒有弄清楚,高中電路沒有學好,如果並聯的一條電路斷路了,它的觸腳電壓可能為0,如果

他有多個斷開的裝置,則開頭和結尾的斷開裝置中間的所有電器的觸腳都是0。

例如輸入:

#T1:[IN K1-1] [K1-2 OUT]
#T2:[IN K2-1] [K2-2 K3-1] [K3-2 B2-1] [B2-2 OUT]
#M1:[T1 T2]
#T3:[VCC M1-IN] [M1-OUT B1-1] [B1-2 GND]
#K1
end

錯誤輸出:

@K1:closed 220-220 exceeding current limit error
@K2:turned on 220-0
@K3:turned on 0-220
@B1:200 220-0 exceeding current limit error
@B2:0 0-0

正確的輸出:

@K1:closed 220-220 exceeding current limit error
@K2:turned on 220-0
@K3:turned on 0-220
@B1:200 220-0 exceeding current limit error
@B2:0 220-220

(3) 如果並聯電路短路,則每條短路的電路均分電流,其他電路電流為0。

我下意識以為只會有一條短路電流,所有第一條短路會分全部電流,其他的則全部是0。

這就會導致電路裝置是否超出電路限制做出影響。

例如輸入:

#T1:[IN K1-1] [K1-2 OUT]
#T2:[IN K4-1] [K4-2 OUT]
#T4:[IN K3-1] [K3-2 OUT]
#M1:[T1 T2 T4]
#T3:[VCC K2-1] [K2-2 M1-IN] [M1-OUT B1-1] [B1-2 GND]
#K1
#K3
#K2
end

我的錯誤輸出:

@K1:closed 220-220 exceeding current limit error
@K2:closed 220-220 exceeding current limit error
@K3:closed 220-220 exceeding current limit error
@K4:turned on 220-220
@B1:200 220-0 exceeding current limit error

正確輸入為:

@K1:closed 220-220
@K2:closed 220-220 exceeding current limit error
@K3:closed 220-220 
@K4:turned on 220-220
@B1:200 220-0 exceeding current limit error

改進建議:

(1)程式碼結構最佳化

  • 重構程式碼,確保程式碼遵循良好的程式設計實踐,如程式碼的封裝、繼承和多型。
  • 最佳化類的設計,確保每個類都有單一職責,並且職責之間保持清晰的界限。
  • 使用包和類層次結構來組織程式碼,保持清晰的命名規範,方便其他人理解。

(2)效能最佳化

  • 分析程式的效能瓶頸,並採取相應的最佳化措施。

  • 對於頻繁執行的計算,考慮使用更高效的演算法或資料結構。

  • 在寫程式碼時,加入多一些錯誤異常處理,這可以很好的幫助除錯程式碼,發現錯誤。

總結

在本次家居強電電路模擬程式的迭代中,我主要關注了以下幾個方面的改進:

  1. 電路複雜度的提升:我們引入了更復雜的電路結構,如多個串聯的並聯電路和包含串聯電路的並聯電路。這樣的設計使得程式能夠更真實地模擬現實生活中的電路佈局。
  2. 新功能的加入:在第七次迭代中,我們增加了互斥開關和受控窗簾的功能。這些新功能的加入使得程式更加貼近實際應用,併為使用者提供了更多的操作可能性。
  3. 程式碼結構的最佳化:在程式碼設計方面,我們採取了模組化設計、使用設計模式、文件和註釋等措施,以提高程式碼的可讀性和可維護性。
  4. 效能最佳化和錯誤處理:在第八次迭代中,我們最佳化了程式的效能,並引入了電流限制和短路檢測等錯誤處理機制。這些改進使得程式更加健壯和可靠。

總的來說,這幾次迭代確實很有鍛鍊效果,我能明顯感覺到我的程式碼除錯能力增強了,遇到錯誤會不斷的測試,找到錯誤,然後修改。

我明白了持續學習的重要性。在未來專案開發過程中,我們可能遇到了許多新問題和挑戰。為了找到解決方案,我不得不查閱相關資料,學習新的知識和技能。這種持續學習的過程讓我不斷進步,也讓我意識到只有不斷學習,才能適應不斷變化的技術環境。

此外,我認識到程式設計不僅僅是一門技術,更是一種解決問題的能力。在專案開發過程中,我們需要面對各種各樣的技術難題。透過不斷嘗試和思考,我們找到了解決問題的方法。這種解決問題的能力讓我更加自信,也讓我明白了程式設計的價值所在。

最後,我感激這幾次PTA給我帶來的成長和收穫。在這個過程中,我提高了自己的程式設計技能,還收穫了豐富的經驗。這些經驗和技能將對我未來的學習和職業發展產生深遠的影響。