- [1] 前言
- [2] 設(shè)計(jì)與分析
- [3] 采坑心得
- [4] 改進(jìn)建議
- [5] 總結(jié)

  • (1)前言

本篇文章總體分析PTA中題目集5-題目集7:
<1> 題目集5總共5道題目,前四道均為簡(jiǎn)單題,主要考察對(duì)正則表達(dá)式基礎(chǔ)的應(yīng)用,而第五道為算法復(fù)雜的電梯調(diào)度程序;
<2> 題目集6總共3道題,前兩道為算法簡(jiǎn)單的面向?qū)ο箢愒O(shè)計(jì)題,第三道為題目集5電梯調(diào)度程序的重構(gòu)和模塊化設(shè)計(jì),必須符合單一職責(zé)原則(SRP);
<3> 題目集7總共3道題,前兩道為算法簡(jiǎn)單的類設(shè)計(jì)題,第三道為題目集6電梯調(diào)度程序的二次迭代。

  • (2)設(shè)計(jì)與分析

1.題目集5電梯單部調(diào)度問題:

SouceMonitor分析圖:

解釋:
根據(jù)左邊的Kiviat圖和上面準(zhǔn)確的Value值可知:每個(gè)方法的平均語句數(shù)Avg Stmts/Method(23.56)遠(yuǎn)超綠色范圍的正常值,說明存在代碼堆積,過度嵌套多重if-else語句。實(shí)際上確實(shí)如此,第一次的電梯算法中還停留在面向過程的解題思維中,源碼中總共也就兩個(gè)類(elevator和Main),許多方法全部堆砌在elevator類中,缺乏模塊化思維
心得:
合理分析不同算法邏輯的實(shí)現(xiàn)過程,盡量把類里的每個(gè)方法各自獨(dú)立實(shí)現(xiàn)自己的特有功能,把不同的功能放在不同的方法中代碼可讀性也強(qiáng),對(duì)代碼改進(jìn)和優(yōu)化時(shí)也輕松。

2. 題目集6電梯單部調(diào)度問題:

類圖:

SouceMonitor分析圖:

解釋:
根據(jù)左邊的Kiviat圖和上面準(zhǔn)確的Value值可知:對(duì)比上次的題目集5的電梯算法,這次的每個(gè)方法的平均語句數(shù)Avg Stmts/Method(8.35)處于綠色范圍的正常值,說明不存在代碼過度堆積問題。但最大圈復(fù)雜度Max Complexity(11)超過正常值,存在過度嵌套多重if-else語句。分析源代碼中,確實(shí)有兩個(gè)方法getMaxFloor()和getMinFloor(),在分析電梯上行和下行過程中,使用多個(gè)if-else語句判斷樓層和方向。此外我的最終源代碼500行遠(yuǎn)超過PTA的代碼限度,無法正常提交代碼,說明存在許多冗余無效的代碼量,或者類的設(shè)計(jì)不規(guī)范,不高效,后續(xù)需要進(jìn)一步改進(jìn)與優(yōu)化。
心得:
面對(duì)過于復(fù)雜的算法和邏輯,不宜過多使用if-else語句,后續(xù)需要進(jìn)一步學(xué)習(xí),用面向?qū)ο?/strong>的思維重構(gòu)代碼。

3. 題目集7電梯單部調(diào)度問題:

SouceMonitor分析圖:

解釋
題目集7目前的電梯程序只實(shí)現(xiàn)了部分測(cè)試數(shù)據(jù)和算法邏輯,對(duì)于當(dāng)前的源碼進(jìn)行分析可知:
最大圈復(fù)雜度Max Complexity遠(yuǎn)超過正常值,后續(xù)仍需要對(duì)源碼進(jìn)行合理優(yōu)化重構(gòu)

  • (3)采坑心得

1.前部分題目心得

提交的錯(cuò)誤源碼:

點(diǎn)擊查看代碼
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();

        if(input.length() <= 4 || input.length() >= 16) {
            System.out.print("你輸入的QQ號(hào)驗(yàn)證失敗");
            return;
        }
        if(input.matches("[1-9][\\d]")) {
            System.out.print("你輸入的QQ號(hào)驗(yàn)證成功");
        }else {
            System.out.print("你輸入的QQ號(hào)驗(yàn)證失敗");
        }
    }
}
(1) 不熟練正則表達(dá)式的使用,開始的正則表達(dá)式匹配不完整,[1-9][\\d] 僅匹配前兩位(首字符非0,第二位為數(shù)字),但未校驗(yàn)后續(xù)字符;若輸入12aaaaa會(huì)被錯(cuò)判為合法。

(2)不熟練正則表達(dá)式的使用,長(zhǎng)度校驗(yàn)和數(shù)字校驗(yàn)均可隱藏在正則中,

首字符非0 ?[1-9]

全數(shù)字校驗(yàn)和長(zhǎng)度校驗(yàn) ?\d{4,1}

(3)隨機(jī)計(jì)數(shù)的點(diǎn)分布在內(nèi)接圓中為整形(int hits),返回ruturn時(shí)準(zhǔn)確值不夠精確,丟失了部分小數(shù)點(diǎn)后的精確值,導(dǎo)致與π的差值較大。因此需要強(qiáng)制轉(zhuǎn)換會(huì)double類型。

強(qiáng)制轉(zhuǎn)換類型的代碼:

點(diǎn)擊查看代碼
	public double simulation(int n) {
		int hits = 0;
		for(int i = 0;i < n;i++) {
			double x = rectangle.coordinate.getAbscissa() + Math.random() * rectangle.getLength();
			double y = rectangle.coordinate.getOrdinate() - Math.random() * rectangle.getWidth();
			
			Coordinate randomcoordinate = new Coordinate(x,y);
			
			if(getDistance(circle.getCoordinate(),randomcoordinate) <= circle.getRadius()) {
				hits++;
			}
		}
		return (double)(4 * hits) / n;//強(qiáng)制int轉(zhuǎn)換為double
	}

2. 單部電梯調(diào)度問題:

提交的問題:

采坑心得體會(huì):
在不斷改進(jìn)和提交源碼的過程中,前部分源碼提交過程大部分出現(xiàn)的問題為運(yùn)行超時(shí),說明算法實(shí)現(xiàn)的邏輯不完整,有漏洞導(dǎo)致死循環(huán),需要改正和調(diào)整正確邏輯和算法;
后部分源碼提交過程出現(xiàn)的問題幾乎全是答案錯(cuò)誤,說明邏輯實(shí)現(xiàn)了閉環(huán),但源碼算法實(shí)現(xiàn)的過程與測(cè)試點(diǎn)實(shí)現(xiàn)的需求不符合,需要更改錯(cuò)誤實(shí)現(xiàn)的邏輯和算法。

  • (4)改進(jìn)建議
  1. 電梯多類設(shè)計(jì)仍然存在不合理不規(guī)范的情況:代碼集中在Controlle類
    (包含addIntenalRequest,addExternalRequest,processRequests,getMaxUpFloor,getMinDownFloor,handleRequestsAtCurrentFloor等方法)中;
    其中的addIntenalRequest和addExternalRequest方法應(yīng)該放在RequestsQueue類中才合理,后續(xù)應(yīng)該對(duì)相應(yīng)代碼改進(jìn)與優(yōu)化。

  2. 電梯類算法設(shè)計(jì)冗長(zhǎng),有一些邏輯可以合并一起處理;

例如如下getMaxUpFloor()方法:

點(diǎn)擊查看代碼
private TargetFloor getMaxUpFloor() {
        // 定義候選變量
        Integer insideCandidate = null;
        Integer outsideCandidate = null;
        int currentFloor = elevator.getCurrentFloor();
        LinkedList<Integer> outsideUpRequests = queue.getExternalUpRequests();
        LinkedList<Integer> outsideDownRequests = queue.getExternalDownRequests();

        if (!queue.getInternalRequests().isEmpty() && queue.getInternalRequests().peek() >= currentFloor) {
            insideCandidate = queue.getInternalRequests().peek();
        }
        if (!queue.getExternalRequests().isEmpty() && queue.getExternalRequests().peek() >= currentFloor) {
            outsideCandidate = queue.getExternalRequests().peek();
        }

        if (queue.getInternalRequests().isEmpty()) {
            if (queue.getExternalRequests().isEmpty()) {
                return null;
            } 
            else if(!queue.getExternalRequests().isEmpty()) {
            	if(queue.getExternalRequests().peek() == outsideDownRequests.peek()) {
            		return new TargetFloor(queue.getExternalRequests().peek(), "DOWN");
            	}
            	else if(queue.getExternalRequests().peek() == outsideUpRequests.peek()) {
            		return new TargetFloor(queue.getExternalRequests().peek(), "UP");
            	}
            } 	
        } 
        else if(!queue.getInternalRequests().isEmpty() && insideCandidate == null) {//內(nèi)部無效
        	if(queue.getExternalRequests().isEmpty()) {
        		return new TargetFloor(queue.getInternalRequests().peek(), "DOWN");
        	}
        	else if(outsideCandidate != null) {
        		if(queue.getExternalRequests().peek() == outsideUpRequests.peek()) {
        			return new TargetFloor(queue.getExternalRequests().peek(), "UP");
        		}
        		else if(queue.getExternalRequests().peek() == outsideDownRequests.peek() && queue.getExternalRequests().peek() < outsideDownRequests.peek()) {
        			return new TargetFloor(queue.getExternalRequests().peek(), "DOWN");
        		}
        		else if(queue.getExternalRequests().peek() == outsideDownRequests.peek() && queue.getExternalRequests().peek() > outsideDownRequests.peek()) {
        			return new TargetFloor(queue.getExternalRequests().peek(), "UP");
        		}
        	}
        	else if(outsideCandidate == null) {//外部無效
        		if(queue.getInternalRequests().peek() > queue.getExternalRequests().peek()) {
        			return new TargetFloor(queue.getInternalRequests().peek(), "DOWN");
        		}
        		else if(queue.getInternalRequests().peek() == queue.getExternalRequests().peek()) {
        			if(queue.getExternalRequests().peek() == outsideDownRequests.peek()) {
                		return new TargetFloor(queue.getExternalRequests().peek(), "DOWN");
                	}
                	else if(queue.getExternalRequests().peek() == outsideUpRequests.peek()) {
                		return new TargetFloor(queue.getExternalRequests().peek(), "DOWN");
                	}
        		}
        		else if(queue.getInternalRequests().peek() < queue.getExternalRequests().peek()) {
        			if(queue.getExternalRequests().peek() == outsideUpRequests.peek()) {
        				return new TargetFloor(queue.getInternalRequests().peek(), "DOWN");
        			}
        			else if(queue.getExternalRequests().peek() == outsideDownRequests.peek()) {
        				return new TargetFloor(queue.getExternalRequests().peek(), "DOWN");
        			}
        		}
        	}
        }
        else if(insideCandidate != null) {
        	if(queue.getExternalRequests().peek() == null) {
        		return new TargetFloor(queue.getInternalRequests().peek(), "UP");
        	}
        	else if(outsideCandidate == null) {//外部無效
        		return new TargetFloor(queue.getInternalRequests().peek(), "UP");
        	}
        	else if(queue.getInternalRequests().peek() < queue.getExternalRequests().peek()) {
        		return new TargetFloor(queue.getInternalRequests().peek(), "UP");
        	}
        	else if(queue.getInternalRequests().peek() == queue.getExternalRequests().peek()) {
        		if(queue.getExternalRequests().peek() == outsideUpRequests.peek()) {
        			return new TargetFloor(queue.getExternalRequests().peek(), "UP");
        		}
        		else if(queue.getExternalRequests().peek() == outsideDownRequests.peek()) {
        			return new TargetFloor(queue.getExternalRequests().peek(), "UP");
        		}
        	}
        	else if(queue.getInternalRequests().peek() > queue.getExternalRequests().peek()) {
        		if(queue.getExternalRequests().peek() == outsideUpRequests.peek()) {
        			return new TargetFloor(queue.getExternalRequests().peek(), "UP");
        		}
        		else if(queue.getExternalRequests().peek() == outsideDownRequests.peek()) {
        			return new TargetFloor(queue.getInternalRequests().peek(), "UP");
        		}
        	}
        }
        return null;
    }

其它的像getMinDownFloor()方法與上部分方法類似,均可以優(yōu)化處理不必要和無效的代碼量。

  • (5)總結(jié)
  1. 通過3周的Java題目集訓(xùn)練,我基本上能理解和實(shí)現(xiàn)簡(jiǎn)單的Java面向?qū)ο缶幊蹋斫饬祟惻c對(duì)象的封裝特性,初步掌握了基本的模塊化設(shè)計(jì)的實(shí)現(xiàn)方法。
    例如,通過電梯調(diào)度系統(tǒng)的開發(fā),實(shí)踐了將復(fù)雜功能拆解為獨(dú)立對(duì)象(如電梯本體、請(qǐng)求隊(duì)列、調(diào)度器)的工程化思維,初步了解了代碼結(jié)構(gòu)化設(shè)計(jì)模式。

  2. 在面對(duì)復(fù)雜的算法邏輯題時(shí),沒有認(rèn)真分析題目,找到最佳的算法思路,導(dǎo)致浪費(fèi)很多時(shí)間,效率低
    例如:在寫題目集5電梯類時(shí),一開始就直接分析三個(gè)隊(duì)列的不同組合情況(內(nèi)部需求,外部向上需求,外部向下需求),不但草稿演算浪費(fèi)5,6張紙,最后運(yùn)行調(diào)試后發(fā)現(xiàn)電梯算法(與后續(xù)新增的測(cè)試樣例)部分電梯運(yùn)行過程是錯(cuò)的;最后發(fā)現(xiàn)分析兩個(gè)隊(duì)列(內(nèi)部需求,外部需求)僅可,而(向上和向下)只需要判斷一下方向即可。

  3. 目前的有關(guān)類設(shè)計(jì)的題目,課程組老師均給出了類圖,對(duì)應(yīng)著類圖編程相對(duì)比較輕松。只需要思考每個(gè)類里的方法算法邏輯如何實(shí)現(xiàn)的即可。如果不看對(duì)應(yīng)的類圖直接寫代碼,我目前對(duì)面向?qū)ο缶幊痰乃悸窌?huì)不清晰沒有邏輯,又會(huì)回到面向過程的編程中去;因此,在后續(xù)的學(xué)習(xí)中,需要進(jìn)一步培養(yǎng)分析對(duì)象和類設(shè)計(jì)的能力,更好的適應(yīng)面向?qū)ο缶幊獭?/p>