造Lab1完全指南:從代碼規(guī)范到工程思維)
1. 從一份實(shí)驗(yàn)報(bào)告說起軟件構(gòu)造Lab1到底在練什么如果你正在讀軟件工程或者計(jì)算機(jī)相關(guān)專業(yè)大概率會(huì)在某個(gè)學(xué)期遇到一門叫“軟件構(gòu)造”的課。這門課和數(shù)據(jù)結(jié)構(gòu)、算法設(shè)計(jì)不太一樣它不追求讓你寫出多復(fù)雜的邏輯而是盯著一個(gè)容易被忽視的問題你寫的代碼別人能不能看懂、能不能改、能不能放心地用。Lab1作為整門課的第一次實(shí)驗(yàn)往往承擔(dān)著“把野路子代碼習(xí)慣掰過來”的任務(wù)。我當(dāng)年第一次拿到Lab1的實(shí)驗(yàn)要求時(shí)心里想的是“不就是寫幾個(gè)Java類嗎”。真正動(dòng)手之后才發(fā)現(xiàn)實(shí)驗(yàn)的評(píng)分點(diǎn)根本不在于功能跑通沒有而在于你的代碼結(jié)構(gòu)是否清晰、注釋是否到位、異常處理是否合理、測(cè)試是否覆蓋了邊界情況。換句話說Lab1是在訓(xùn)練一種工程化的思維方式而不是單純的編程能力。這篇文章適合兩類人看一類是正在做或者即將做這個(gè)實(shí)驗(yàn)的同學(xué)想提前知道里面有哪些坑另一類是對(duì)軟件構(gòu)造感興趣、想了解工程化編碼到底意味著什么的自學(xué)者。我會(huì)從實(shí)驗(yàn)的核心目標(biāo)、環(huán)境搭建、代碼設(shè)計(jì)、測(cè)試策略、報(bào)告撰寫這幾個(gè)維度把Lab1完整拆解一遍盡量把每一步“為什么這么做”講清楚。提示不同年份、不同教學(xué)班的Lab1具體要求可能有差異本文基于常見的實(shí)驗(yàn)框架展開具體以你拿到的實(shí)驗(yàn)指導(dǎo)書為準(zhǔn)。2. 實(shí)驗(yàn)的核心目標(biāo)不是寫代碼是寫“能活很久”的代碼2.1 軟件構(gòu)造視角下的“好代碼”標(biāo)準(zhǔn)很多同學(xué)寫代碼的習(xí)慣是能跑就行。變量名用a、b、c函數(shù)寫到幾百行異常直接catch住什么都不做注釋只在老師要求的時(shí)候補(bǔ)兩句。這種代碼在單人一次性任務(wù)里確實(shí)沒問題但一旦進(jìn)入團(tuán)隊(duì)協(xié)作或者需要長期維護(hù)的場(chǎng)景就會(huì)變成災(zāi)難。Lab1的第一個(gè)核心目標(biāo)就是讓你建立一套“可維護(hù)性”的判斷標(biāo)準(zhǔn)。具體來說包括以下幾個(gè)維度可讀性變量名、函數(shù)名是否能自解釋代碼縮進(jìn)和空行是否合理注釋是否解釋了“為什么”而不是“是什么”。可擴(kuò)展性新增一個(gè)功能時(shí)是需要改動(dòng)大量已有代碼還是只需要增加一個(gè)新的實(shí)現(xiàn)類。健壯性面對(duì)非法輸入、邊界條件、異常情況時(shí)程序是直接崩潰還是給出合理的錯(cuò)誤提示。可測(cè)試性代碼是否容易被單元測(cè)試覆蓋依賴關(guān)系是否清晰。這四個(gè)維度聽起來很抽象但在Lab1里會(huì)通過具體的任務(wù)讓你反復(fù)體會(huì)。比如實(shí)驗(yàn)通常會(huì)要求你實(shí)現(xiàn)一個(gè)有一定規(guī)模的功能模塊然后寫測(cè)試用例去驗(yàn)證它。如果你在設(shè)計(jì)階段沒有考慮可測(cè)試性后面寫測(cè)試的時(shí)候就會(huì)發(fā)現(xiàn)根本無從下手。2.2 從“能跑”到“好改”的思維轉(zhuǎn)變我觀察過很多同學(xué)做Lab1的過程最大的障礙不是技術(shù)難度而是思維慣性。大家習(xí)慣了“面向過程”地堆代碼突然要求用面向?qū)ο蟮姆绞饺ピO(shè)計(jì)會(huì)很不適應(yīng)。舉個(gè)例子實(shí)驗(yàn)里可能要求你處理一組具有不同屬性的對(duì)象。面向過程的寫法是寫一個(gè)大的函數(shù)里面用if-else判斷類型然后分別處理。這種寫法在對(duì)象類型少的時(shí)候沒問題但一旦類型增多函數(shù)會(huì)急劇膨脹而且每次新增類型都要修改原有代碼違反了開閉原則。面向?qū)ο蟮膶懛ㄊ窍瘸橄蟪鲆粋€(gè)基類或者接口把公共行為定義好然后每種類型寫一個(gè)子類去實(shí)現(xiàn)。新增類型時(shí)只需要加一個(gè)子類不需要?jiǎng)右延写a。這個(gè)轉(zhuǎn)變說起來簡(jiǎn)單但真正動(dòng)手的時(shí)候很多人還是會(huì)不自覺地寫回if-else。注意Lab1的評(píng)分中代碼結(jié)構(gòu)往往比功能完整性占更高的權(quán)重。一個(gè)功能全部實(shí)現(xiàn)但結(jié)構(gòu)混亂的提交得分可能遠(yuǎn)低于功能只完成80%但結(jié)構(gòu)清晰的提交。2.3 實(shí)驗(yàn)任務(wù)與工程能力的映射關(guān)系Lab1的每個(gè)任務(wù)點(diǎn)其實(shí)都對(duì)應(yīng)著一項(xiàng)具體的工程能力。我整理了一個(gè)對(duì)照表方便你理解實(shí)驗(yàn)設(shè)計(jì)的意圖實(shí)驗(yàn)任務(wù)對(duì)應(yīng)的工程能力常見失分點(diǎn)功能模塊實(shí)現(xiàn)面向?qū)ο笤O(shè)計(jì)、接口抽象類職責(zé)不清、耦合過重異常處理防御性編程、錯(cuò)誤恢復(fù)吞異常、拋異常不寫信息單元測(cè)試測(cè)試驅(qū)動(dòng)開發(fā)、邊界分析只測(cè)正常路徑、斷言不充分代碼注釋文檔能力、溝通能力注釋重復(fù)代碼、缺少設(shè)計(jì)說明實(shí)驗(yàn)報(bào)告技術(shù)寫作、總結(jié)能力只貼代碼、缺少設(shè)計(jì)決策記錄這張表建議你在動(dòng)手之前先看一遍做的時(shí)候?qū)φ罩鴻z查能幫你少走很多彎路。3. 環(huán)境搭建那些實(shí)驗(yàn)指導(dǎo)書不會(huì)細(xì)說的事3.1 JDK版本選擇與IDE配置Lab1通常要求用Java完成所以第一步是配好JDK和IDE。這里有幾個(gè)細(xì)節(jié)值得注意。JDK版本方面實(shí)驗(yàn)指導(dǎo)書一般會(huì)指定一個(gè)版本比如JDK 8或者JDK 11。我的建議是嚴(yán)格按照要求的版本來不要圖新鮮裝最新的。原因很簡(jiǎn)單不同JDK版本在API和編譯行為上有差異你用JDK 17寫的代碼可能在評(píng)分環(huán)境里跑不起來。特別是涉及到一些集合類的默認(rèn)行為、字符串處理、日期時(shí)間API的時(shí)候版本差異會(huì)直接導(dǎo)致編譯錯(cuò)誤或運(yùn)行結(jié)果不一致。IDE的選擇上Eclipse和IntelliJ IDEA都可以。Eclipse的優(yōu)勢(shì)是輕量、啟動(dòng)快很多學(xué)校的實(shí)驗(yàn)環(huán)境默認(rèn)就是Eclipse。IDEA的優(yōu)勢(shì)是智能提示更強(qiáng)、重構(gòu)功能更完善寫代碼效率更高。如果你之前沒用過IDEA建議花半小時(shí)熟悉一下基本操作后面寫代碼會(huì)省很多時(shí)間。配置的時(shí)候有一個(gè)容易忽略的點(diǎn)編碼格式。Java源文件默認(rèn)使用平臺(tái)編碼Windows中文環(huán)境下可能是GBK而實(shí)驗(yàn)要求通常是UTF-8。如果編碼不一致中文注釋會(huì)變成亂碼嚴(yán)重的話會(huì)導(dǎo)致編譯失敗。在IDE的設(shè)置里把工作空間編碼和文件編碼都改成UTF-8能避免很多莫名其妙的問題。3.2 構(gòu)建工具與依賴管理Lab1一般不會(huì)涉及太復(fù)雜的依賴但有些實(shí)驗(yàn)框架會(huì)要求你用Maven或者Gradle來管理項(xiàng)目。如果你的實(shí)驗(yàn)要求里提到了構(gòu)建工具那就老老實(shí)實(shí)按它的來。Maven的核心是pom.xml文件你需要關(guān)注幾個(gè)關(guān)鍵配置properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties這三行分別指定了Java編譯版本和源碼編碼。很多同學(xué)編譯報(bào)錯(cuò)就是因?yàn)橥伺渚幋a導(dǎo)致中文字符無法識(shí)別。如果你用的是Gradle對(duì)應(yīng)的配置在build.gradle里java { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } tasks.withType(JavaCompile) { options.encoding UTF-8 }提示如果你不確定實(shí)驗(yàn)是否需要構(gòu)建工具可以先不用直接用IDE自帶的編譯功能。但如果你打算把代碼提交到代碼托管平臺(tái)建議還是配上方便別人拉取后直接構(gòu)建。3.3 版本控制的基本操作雖然Lab1不一定強(qiáng)制要求用Git但我強(qiáng)烈建議你從第一次實(shí)驗(yàn)開始就養(yǎng)成用版本控制的習(xí)慣。原因很實(shí)際實(shí)驗(yàn)過程中你會(huì)不斷修改代碼有時(shí)候改著改著發(fā)現(xiàn)改錯(cuò)了想回退如果沒有版本控制就只能靠記憶手動(dòng)改回來。最基本的操作流程是在項(xiàng)目根目錄執(zhí)行g(shù)it init初始化倉庫。創(chuàng)建.gitignore文件把編譯產(chǎn)物如bin/、out/、target/和IDE配置文件排除掉。每完成一個(gè)功能點(diǎn)就git add和git commit提交信息寫清楚做了什么。如果實(shí)驗(yàn)允許可以把代碼推送到遠(yuǎn)程倉庫做備份。.gitignore的一個(gè)參考內(nèi)容bin/ out/ target/ *.class .idea/ *.iml .settings/ .classpath .project這個(gè)習(xí)慣在后續(xù)的Lab里會(huì)越來越重要因?yàn)轫?xiàng)目規(guī)模會(huì)變大沒有版本控制幾乎沒法管理。4. 代碼設(shè)計(jì)從需求到實(shí)現(xiàn)的完整思路4.1 需求拆解與職責(zé)劃分拿到實(shí)驗(yàn)任務(wù)后不要急著打開IDE寫代碼。先花時(shí)間把需求讀透搞清楚要做什么、輸入是什么、輸出是什么、有哪些約束條件。以常見的Lab1任務(wù)為例假設(shè)要求實(shí)現(xiàn)一個(gè)“圖形管理系統(tǒng)”支持添加不同形狀、計(jì)算總面積、按條件篩選。你可以按以下步驟拆解第一步識(shí)別核心概念。這里的概念有形狀、圓形、矩形、三角形、圖形管理器。每個(gè)概念對(duì)應(yīng)一個(gè)類。第二步確定類之間的關(guān)系。圓形、矩形、三角形都是形狀所以可以抽象出一個(gè)Shape基類或接口。圖形管理器負(fù)責(zé)管理一組形狀它和形狀之間是聚合關(guān)系。第三步定義每個(gè)類的職責(zé)。Shape定義公共行為如計(jì)算面積、獲取名稱子類實(shí)現(xiàn)具體計(jì)算邏輯。圖形管理器負(fù)責(zé)增刪查改和統(tǒng)計(jì)。第四步考慮擴(kuò)展性。如果以后要加新的形狀類型現(xiàn)有代碼需要改動(dòng)嗎如果不需要說明設(shè)計(jì)是合理的。這個(gè)拆解過程看起來簡(jiǎn)單但很多同學(xué)跳過這一步直接寫代碼結(jié)果寫到一半發(fā)現(xiàn)結(jié)構(gòu)不對(duì)又回頭重構(gòu)反而更費(fèi)時(shí)間。4.2 接口與抽象類的選擇邏輯Java里接口和抽象類都可以用來定義公共行為但它們的適用場(chǎng)景不同。Lab1里經(jīng)常需要做這個(gè)選擇我分享一下我的判斷標(biāo)準(zhǔn)。用接口的情況需要定義一組行為規(guī)范但不關(guān)心具體實(shí)現(xiàn)。一個(gè)類需要實(shí)現(xiàn)多個(gè)不相關(guān)的行為。希望保持類之間的松耦合。用抽象類的情況多個(gè)類有共同的屬性和方法實(shí)現(xiàn)可以復(fù)用。需要在基類里定義一些默認(rèn)行為子類可以選擇性覆蓋。類之間有明確的繼承關(guān)系。在圖形管理系統(tǒng)的例子里Shape更適合用抽象類因?yàn)樗行螤疃加小懊Q”這個(gè)屬性而且“打印信息”這個(gè)方法可以有默認(rèn)實(shí)現(xiàn)。但如果實(shí)驗(yàn)要求里形狀之間沒有共享狀態(tài)用接口也完全可以。注意不要為了用設(shè)計(jì)模式而用設(shè)計(jì)模式。Lab1的評(píng)分看重的是設(shè)計(jì)是否合理而不是用了多少種模式。一個(gè)簡(jiǎn)單的接口加幾個(gè)實(shí)現(xiàn)類只要職責(zé)清晰就是好設(shè)計(jì)。4.3 異常處理的設(shè)計(jì)原則異常處理是Lab1里最容易失分的地方之一。很多同學(xué)的寫法是這樣的try { // 一些操作 } catch (Exception e) { // 什么都不做 }這種寫法的問題在于一旦出錯(cuò)程序不會(huì)崩潰但也不會(huì)給出任何提示問題被隱藏了調(diào)試的時(shí)候根本不知道哪里出了錯(cuò)。正確的做法是能處理的異常才捕獲如果你不知道該怎么處理就讓它往上拋。捕獲后要有動(dòng)作至少打印日志或者包裝成更友好的異常再拋出。不要捕獲過于寬泛的異常盡量捕獲具體的異常類型如NumberFormatException而不是Exception。異常信息要有意義包含出錯(cuò)的值、期望的格式等上下文信息。一個(gè)合理的異常處理示例public int parseAge(String input) { try { int age Integer.parseInt(input); if (age 0 || age 150) { throw new IllegalArgumentException(年齡超出合理范圍: age); } return age; } catch (NumberFormatException e) { throw new IllegalArgumentException(年齡格式不正確: input, e); } }這個(gè)例子里異常被轉(zhuǎn)換成了更具體的類型并且保留了原始異常作為cause方便追蹤問題。4.4 代碼注釋的寫法與尺度注釋不是越多越好關(guān)鍵是寫在該寫的地方。Lab1里常見的注釋問題有兩種一種是不寫注釋另一種是寫廢話注釋。廢話注釋的例子// 設(shè)置name public void setName(String name) { this.name name; }這種注釋沒有任何信息量因?yàn)榇a本身已經(jīng)說清楚了。好的注釋應(yīng)該解釋“為什么”// 使用防御性拷貝避免外部修改影響內(nèi)部狀態(tài) public void setTags(ListString tags) { this.tags new ArrayList(tags); }這個(gè)注釋解釋了為什么要多此一舉地新建一個(gè)ArrayList而不是直接賦值。這種信息才是讀者需要的。在Lab1里建議在以下幾個(gè)位置寫注釋類的頭部說明這個(gè)類的職責(zé)和設(shè)計(jì)思路。復(fù)雜方法的頭部說明輸入、輸出、可能拋出的異常。關(guān)鍵算法或邏輯分支解釋為什么這樣處理。不明顯的邊界條件說明為什么取這個(gè)值。5. 測(cè)試策略怎么證明你的代碼是對(duì)的5.1 單元測(cè)試的基本框架Lab1通常要求用JUnit寫單元測(cè)試。JUnit 4和JUnit 5的寫法略有不同但核心思想一致為每個(gè)公開方法寫測(cè)試用例驗(yàn)證正常情況和異常情況下的行為。一個(gè)典型的JUnit 4測(cè)試類結(jié)構(gòu)import org.junit.Test; import org.junit.Before; import static org.junit.Assert.*; public class ShapeTest { private Circle circle; Before public void setUp() { circle new Circle(5.0); } Test public void testArea() { assertEquals(78.54, circle.getArea(), 0.01); } Test(expected IllegalArgumentException.class) public void testNegativeRadius() { new Circle(-1.0); } }Before注解的方法會(huì)在每個(gè)測(cè)試方法執(zhí)行前運(yùn)行用來準(zhǔn)備測(cè)試數(shù)據(jù)。assertEquals的第三個(gè)參數(shù)是浮點(diǎn)數(shù)比較的誤差范圍因?yàn)楦↑c(diǎn)運(yùn)算有精度問題不能直接用等號(hào)比較。5.2 邊界條件與異常路徑的覆蓋很多同學(xué)寫測(cè)試只測(cè)正常情況比如給圓形傳一個(gè)正數(shù)半徑驗(yàn)證面積計(jì)算正確。但真正能發(fā)現(xiàn)bug的往往是邊界條件和異常路徑。以圓形為例需要測(cè)試的邊界包括半徑為0面積應(yīng)該是0。半徑為負(fù)數(shù)應(yīng)該拋出異常。半徑非常大驗(yàn)證是否會(huì)出現(xiàn)溢出。半徑為小數(shù)驗(yàn)證精度是否可接受。異常路徑的測(cè)試同樣重要。如果你的方法在參數(shù)非法時(shí)拋出異常就要寫測(cè)試驗(yàn)證它確實(shí)拋出了正確的異常類型。JUnit 4用Test(expected XxxException.class)JUnit 5用assertThrows。提示測(cè)試方法的命名要能說明測(cè)試意圖比如testAreaWithZeroRadius比testArea2好得多。評(píng)分的時(shí)候老師會(huì)看你的測(cè)試是否覆蓋了關(guān)鍵場(chǎng)景。5.3 測(cè)試代碼的質(zhì)量標(biāo)準(zhǔn)測(cè)試代碼也是代碼同樣需要保持整潔。幾個(gè)基本要求一個(gè)測(cè)試方法只測(cè)一件事不要把多個(gè)斷言混在一個(gè)方法里失敗了不好定位。測(cè)試之間相互獨(dú)立一個(gè)測(cè)試的失敗不應(yīng)該影響另一個(gè)測(cè)試。避免在測(cè)試?yán)飳憦?fù)雜邏輯測(cè)試應(yīng)該直白如果需要復(fù)雜計(jì)算才能驗(yàn)證結(jié)果說明設(shè)計(jì)可能有問題。使用有意義的斷言消息assertEquals(面積計(jì)算錯(cuò)誤, expected, actual, 0.01)比沒有消息的斷言更容易排查問題。5.4 測(cè)試覆蓋率的合理追求有些實(shí)驗(yàn)會(huì)要求測(cè)試覆蓋率達(dá)到某個(gè)百分比。我的建議是不要為了覆蓋率而寫測(cè)試。覆蓋率只是一個(gè)參考指標(biāo)100%的覆蓋率不代表沒有bug80%的覆蓋率也可能已經(jīng)覆蓋了所有關(guān)鍵路徑。優(yōu)先覆蓋以下內(nèi)容所有公開方法的正常路徑。所有參數(shù)校驗(yàn)的異常路徑。所有條件分支的兩個(gè)方向。循環(huán)的零次、一次、多次情況。把精力花在這些地方比盲目追求行覆蓋率更有價(jià)值。6. 實(shí)驗(yàn)報(bào)告撰寫把思考過程留下來6.1 報(bào)告結(jié)構(gòu)的設(shè)計(jì)實(shí)驗(yàn)報(bào)告不是代碼的堆砌而是你設(shè)計(jì)思路的記錄。一份好的Lab1報(bào)告通常包含以下部分實(shí)驗(yàn)?zāi)繕?biāo)用自己的話復(fù)述實(shí)驗(yàn)要解決的問題不要照抄指導(dǎo)書。設(shè)計(jì)思路說明你如何拆解問題、如何劃分模塊、為什么這樣設(shè)計(jì)。核心代碼說明挑選關(guān)鍵代碼片段解釋其作用和設(shè)計(jì)意圖。測(cè)試設(shè)計(jì)與結(jié)果說明測(cè)試了哪些場(chǎng)景結(jié)果如何發(fā)現(xiàn)了什么問題。問題與解決記錄實(shí)驗(yàn)過程中遇到的困難和解決過程??偨Y(jié)與反思你從這次實(shí)驗(yàn)中學(xué)到了什么還有哪些不足。其中第2部分和第5部分是拉開差距的地方。很多同學(xué)的報(bào)告只有代碼和截圖缺少設(shè)計(jì)決策的記錄得分自然不高。6.2 設(shè)計(jì)決策的記錄方式記錄設(shè)計(jì)決策時(shí)可以采用“問題-方案-理由”的結(jié)構(gòu)。比如問題形狀的面積計(jì)算方式各不相同如何統(tǒng)一處理方案定義Shape抽象類聲明抽象方法getArea()各子類實(shí)現(xiàn)具體計(jì)算。理由這樣新增形狀時(shí)只需添加子類不需要修改管理器代碼符合開閉原則。這種寫法能讓老師看到你的思考過程而不是只看到最終結(jié)果。6.3 常見報(bào)告失分點(diǎn)根據(jù)我的觀察Lab1報(bào)告常見的失分點(diǎn)包括只貼代碼不解釋報(bào)告變成代碼倉庫看不出設(shè)計(jì)思路。截圖不清晰運(yùn)行結(jié)果截圖模糊或者只截了一部分。缺少測(cè)試說明沒有說明測(cè)試了哪些場(chǎng)景測(cè)試結(jié)果如何。格式混亂標(biāo)題層級(jí)不清、代碼沒有高亮、段落沒有分段。抄襲痕跡明顯和別人的報(bào)告結(jié)構(gòu)、措辭高度相似。避免這些問題的方法很簡(jiǎn)單用自己的話寫把自己的思考過程寫出來。哪怕設(shè)計(jì)不完美真實(shí)的思考也比完美的抄襲有價(jià)值。7. 那些年我踩過的坑與總結(jié)的經(jīng)驗(yàn)做Lab1的過程中我踩過不少坑有些是技術(shù)上的有些是習(xí)慣上的。這里挑幾個(gè)有代表性的分享一下。第一個(gè)坑是忽視編譯警告。Java編譯器給出的警告比如未使用的變量、未檢查的類型轉(zhuǎn)換很多人直接忽略。但有些警告背后隱藏著真正的bug比如資源未關(guān)閉、空指針風(fēng)險(xiǎn)。我的建議是把IDE的警告級(jí)別調(diào)高盡量讓代碼零警告。第二個(gè)坑是測(cè)試寫得太晚。我一開始的習(xí)慣是先寫完所有功能代碼再補(bǔ)測(cè)試。結(jié)果發(fā)現(xiàn)代碼結(jié)構(gòu)對(duì)測(cè)試不友好很多方法依賴外部狀態(tài)根本沒法單獨(dú)測(cè)試。后來改成先寫接口和測(cè)試再寫實(shí)現(xiàn)代碼質(zhì)量明顯提升。這就是測(cè)試驅(qū)動(dòng)開發(fā)的價(jià)值。第三個(gè)坑是注釋和代碼不同步。改代碼的時(shí)候忘了改注釋導(dǎo)致注釋誤導(dǎo)讀者。后來我養(yǎng)成了一個(gè)習(xí)慣每次修改方法簽名或行為時(shí)先改注釋再改代碼確保兩者一致。第四個(gè)坑是提交前不檢查。有一次提交后發(fā)現(xiàn)有個(gè)文件忘了保存編譯都通不過。從那以后我每次提交前都會(huì)執(zhí)行一次完整的編譯和測(cè)試確認(rèn)沒問題再提交。關(guān)于Lab1我最大的體會(huì)是它訓(xùn)練的不是編程能力而是工程習(xí)慣。這些習(xí)慣在后續(xù)的課程和工作中會(huì)反復(fù)被驗(yàn)證其價(jià)值。變量命名規(guī)范、異常處理合理、測(cè)試覆蓋充分、注釋清晰準(zhǔn)確這些看起來是小事但積累起來就是專業(yè)和業(yè)余的分界線。如果你正在做這個(gè)實(shí)驗(yàn)我的建議是不要把它當(dāng)成負(fù)擔(dān)而是當(dāng)成一次刻意練習(xí)。每寫一個(gè)類問自己“這個(gè)類的職責(zé)是什么”每寫一個(gè)方法問自己“邊界條件處理了嗎”每寫一行注釋問自己“這行注釋解釋了為什么嗎”。把這些問句變成習(xí)慣Lab1的目標(biāo)就達(dá)到了。最后分享一個(gè)實(shí)用技巧如果你在設(shè)計(jì)中糾結(jié)用接口還是抽象類、用繼承還是組合可以先把兩種方案都寫出來比較一下哪種改動(dòng)更少、哪種更容易測(cè)試。實(shí)踐出真知寫兩遍比想半天更有效。