用:Arianna Blasi, Alberto Goffi, Konstantin Kuznetsov, Alessandra Gorla, Michael D. Ernst, Mauro Pezzè, and Sergio Delgado Castellanos. 2018. Translating Code Comments to Procedure Specifications. In Proceedings of 27th ACM SIG- SOFT International Symposium on Software Testing and Analysis (ISSTA’18). ACM, New York, NY, USA, 12 pages. https://doi.org/10.1145/3213846.3213872.
過(guò)程規(guī)范在許多軟件開(kāi)發(fā)任務(wù)中很有用。例如,在自動(dòng)生成測(cè)試用例的過(guò)程中,它們可以指導(dǎo)測(cè)試,充當(dāng)能夠發(fā)現(xiàn)錯(cuò)誤并識(shí)別非法輸入的測(cè)試先知。盡管實(shí)際上很少有正式的規(guī)范,但是對(duì)于開(kāi)發(fā)人員來(lái)說(shuō),使用半結(jié)構(gòu)化注釋來(lái)記錄其代碼是標(biāo)準(zhǔn)做法。這些注釋通過(guò)預(yù)定義標(biāo)簽和自然語(yǔ)言的組合來(lái)表達(dá)過(guò)程規(guī)范。本文介紹了 Jdoctor,該方法結(jié)合了模式,詞法和語(yǔ)義匹配,將 Javadoc 注釋轉(zhuǎn)換為以 Java 表達(dá)式編寫的可執(zhí)行過(guò)程規(guī)范。在實(shí)證評(píng)估中,將 Javadoc 轉(zhuǎn)換為過(guò)程規(guī)范時(shí),Jdoctor 的精度達(dá)到 92%,召回率達(dá)到 83%。
我們還將 Jdoctor 衍生的規(guī)范提供給自動(dòng)測(cè)試用例生成工具 Randoop。規(guī)范使 Randoop 能夠生成測(cè)試案例,從而減少誤報(bào)并揭示更多缺陷。
程序規(guī)范表達(dá)了預(yù)期的程序行為,因此可以啟用或自動(dòng)化許多軟件工程任務(wù)。在軟件測(cè)試中,它用作確定哪些輸入合法和哪些輸出正確的預(yù)言 [4, 17, 31, 37]。在調(diào)試中,它標(biāo)識(shí)錯(cuò)誤的語(yǔ)句 [29, 54]。在代碼合成中,這是合成器致力于的目標(biāo) [28, 47]。在重構(gòu)中,它確保轉(zhuǎn)換是一致的 [23]。在形式驗(yàn)證中,它區(qū)分正確和錯(cuò)誤的實(shí)現(xiàn) [53]。在運(yùn)行時(shí)監(jiān)視中,它可以識(shí)別異常行為 [15]。自動(dòng)化(部分)任務(wù)需要工具可以操縱的機(jī)器可讀格式。正式的規(guī)范很好地達(dá)到了這個(gè)目的。但是,很少有正式的規(guī)范,因?yàn)榫帉懸?guī)范不是常見(jiàn)的軟件開(kāi)發(fā)實(shí)踐。
? 相比之下,非正式規(guī)范很容易以自然語(yǔ)言編寫的半結(jié)構(gòu)化和非結(jié)構(gòu)化文檔的形式提供。程序員在過(guò)程文檔中指定前提條件、后置條件和特殊行為是標(biāo)準(zhǔn)做法。 Javadoc 標(biāo)記語(yǔ)言和工具于 1995 年在 Java 的第一個(gè)版本中出現(xiàn),而類似但跨語(yǔ)言的工具 Doxygen 在 2 年后出現(xiàn)了。 Java IDE 會(huì)自動(dòng)插入 Javadoc 注釋的模板。最重要的是,程序員已經(jīng)習(xí)慣于編寫這些注釋。結(jié)果,大量代碼包含了非正式的 Javadoc 規(guī)范。但是,軟件工程工具很少使用這些非正式規(guī)范。
? 本文介紹了 Jdoctor,它是一種從程序員已經(jīng)創(chuàng)建的工件(即 Javadoc 代碼注釋)自動(dòng)構(gòu)建可執(zhí)行過(guò)程規(guī)范的技術(shù),而無(wú)需程序員更改開(kāi)發(fā)實(shí)踐或做額外的工作。可執(zhí)行規(guī)范是可以執(zhí)行的規(guī)范,例如,因?yàn)樗怯镁幊陶Z(yǔ)言而不是其他某種邏輯編寫的。它也需要在過(guò)程上表達(dá),而不是(例如)聲明性地要求某些值的存在而不指示如何計(jì)算它。由 Jdoctor 生成的過(guò)程規(guī)范可用于多種軟件工程任務(wù)中,例如自動(dòng)生成測(cè)試用例,如本文所示。
自動(dòng)生成測(cè)試用例可以降低開(kāi)發(fā)成本和軟件故障的社會(huì)成本 [5, 27, 44]。要自動(dòng)生成測(cè)試用例,測(cè)試生成器必須生成一個(gè)輸入,該輸入使被測(cè)程序執(zhí)行某些操作,并生成一個(gè) oracle 以確定程序是否正常運(yùn)行。
? 創(chuàng)建準(zhǔn)確的測(cè)試 Oracle 仍然是一個(gè)未解決的問(wèn)題。正如我們現(xiàn)在所解釋的,這將導(dǎo)致測(cè)試生成器同時(shí)遭受錯(cuò)誤警報(bào)(當(dāng)被測(cè)程序正確時(shí)測(cè)試失敗)和錯(cuò)過(guò)警報(bào)(當(dāng)被測(cè)程序有錯(cuò)誤時(shí)測(cè)試通過(guò))。例如,假設(shè)自動(dòng)生成的測(cè)試中的方法調(diào)用引發(fā)了異常。拋出此異常并不一定意味著所測(cè)試的方法有錯(cuò)誤。存在以下可能性:
(1)引發(fā)的異常實(shí)際上揭示了實(shí)現(xiàn)缺陷。一個(gè)示例是在主題程序中觸發(fā)斷言,或者以其他方式無(wú)法完成所請(qǐng)求的操作。
(2)拋出異常是預(yù)期的、期望的行為。一個(gè)示例是在不可變對(duì)象上調(diào)用變量時(shí)發(fā)生的IllegalOperationException。
.注釋標(biāo)記(<!--…-->)
語(yǔ)法: <!--注釋內(nèi)容-->
2.文件標(biāo)記(<html>…</html>)
語(yǔ)法: <html>…</html>
說(shuō)明:<html>這對(duì)標(biāo)記是HTML文件的標(biāo)記。<html>處于文件的最前面,表示HTML文件的開(kāi)始。即瀏覽器從<html>標(biāo)記開(kāi)始解釋,直到遇到</html>標(biāo)記為止。
3.文件頭標(biāo)記(<head>…</head>)
語(yǔ)法: <head>…</head>
說(shuō)明:
①由head這組標(biāo)記定義的元素中,并不放置網(wǎng)頁(yè)的任何內(nèi)容,是放置關(guān)于這份HTML文件的信息。就是說(shuō),它并不屬于HTML文件的主體,它包含文件的標(biāo)題、編碼方式和URL等。這些信息大部分用于提供索引、辨認(rèn)或其他應(yīng)用。
②這對(duì)標(biāo)記在HTML文件中并不是必需的。如果某個(gè)HTML文件并不需要提供相關(guān)信息,則可省略<head>標(biāo)記。
4.文件標(biāo)題標(biāo)記(<title>…</title>)
語(yǔ)法: <title>文件標(biāo)題文字</title>
說(shuō)明:此標(biāo)記用于設(shè)定HTML文件的標(biāo)題名稱,它將顯示在瀏覽器的標(biāo)題欄中。
注意:<title>標(biāo)記用于<head>標(biāo)記中。但如果HTML文件中沒(méi)有使用<head>標(biāo)記,則<title>標(biāo)記仍然起作用。
5.文件主體標(biāo)記(<body>…</body>)
語(yǔ)法: <body>…</body>
說(shuō)明:
①由<body>標(biāo)記所建立的元素是HTML文件的內(nèi)容主體,也是HTML文件的重點(diǎn)所在。HTML文件中要顯示在網(wǎng)頁(yè)上的所有內(nèi)容,也都放置在這個(gè)元素中。
②<body>具有一些特殊的屬性,可以設(shè)定背景及字符顏色等。
【注釋】從技術(shù)上來(lái)說(shuō)沒(méi)有對(duì)錯(cuò),理論上,你想怎么寫就怎么寫,你愛(ài)怎么寫就怎么寫!
但它確實(shí)也會(huì)對(duì)我們?cè)斐捎绊懀绕涫窃诙嗳藚f(xié)同開(kāi)發(fā)的系統(tǒng)中。雜亂的注釋也會(huì)讓你或你的隊(duì)友頭疼~
所以,我們需要規(guī)范一下注釋。那什么才是好的注釋呢?我們先來(lái)看看什么是不好的注釋!
我們往往會(huì)寫一段注釋來(lái)說(shuō)明“這是什么”。比如:
// Find all the users
let users=userHelper.findAll();
// Add score to each user
users.forEach((user)=> {
user.score++;
}
// Return the users
return users;
但是這段代碼本身的意思就很清楚了,再附上注釋就有點(diǎn)多余了。
所以我們應(yīng)該將其剔除。
let users=userHelper.findAll();
users.forEach((user)=> {
user.score++;
}
return users;
那么,這段代碼是不是就方便閱讀了呢?其實(shí),咱們還能更進(jìn)一步:
let users=userHelper.findAll();
userHelper.incrementScoreForAll(users);
return users;
這樣你感覺(jué)如何?相比于最開(kāi)始的那段代碼,這段是不是就看得舒舒服服?
所以,可讀的代碼比可讀的注釋更重要。優(yōu)先考慮讓你的代碼說(shuō)話,實(shí)在不行,再附上簡(jiǎn)短、清晰的注釋。
// Find all users
let users=userHelper.findBanned();
// Give each user 100 extra score
users.forEach((user)=> {
user.score=0;
}
return users;
我們有時(shí)候會(huì)發(fā)現(xiàn)注釋和代碼并不匹配,比如這里為用戶設(shè)置分?jǐn)?shù)的操作。代碼中是 0 分,注釋卻是 100 分。
導(dǎo)致出現(xiàn)這種情況有多種可能:
怎么辦?讓我們來(lái)看看優(yōu)解:
// userHelper.js
function updateScoreForUsers(score, users) {
users.forEach((user)=> {
user.score +=score;
}
}
// Code 1: punish banned users
let users=userHelper.findBanned();
userHelper.updateScoreForUsers(users, -100);
return users;
// Code 2: give everybody 1 extra score
let users=userHelper.findAll();
userHelper.updateScoreForUsers(users, 1);
return users;
這樣寫將設(shè)置分?jǐn)?shù)的邏輯封裝成函數(shù)進(jìn)行了抽離,功能更強(qiáng)了,也更易于閱讀了。
現(xiàn)在,我們可以在多種情況下重復(fù)調(diào)用它,且不必?fù)?dān)心注釋未及時(shí)更新的問(wèn)題了。
請(qǐng)問(wèn)如果是這樣的注釋,你還有信心整個(gè)完整讀下來(lái)嗎?即使你是一個(gè)勇敢堅(jiān)強(qiáng)的技術(shù)人,讀下來(lái)也會(huì)消耗很多時(shí)間吧?這樣低效率的事肯定不是我們想要的。
// userHelper.js
/**
* Mass updates the user score for the given a list of user
* The score will be updated by the amount given in the parameter score
* For example, if the parameter score is 5, the users will all receive 5 extra score
* But if the score is negative, it can also be used. In that case, the score will be decreased by 5.
* If you set score as 0, then this method will be useless as nothing will be updated.
* If you set the score as a non number value, the function will not do anything and return false
* just to ensure the score is not messed up after updating it with a non-number value
* Try to avoid using non-number values in the score parameter as this should not be used like that
* If you do however choose to use Infinity in the score argument, it will work, because Infinity is considered as a number in JavaScript
* If you set the score to Infinity, all the users score will become Inifinity, because n + Infinity where n is a number, will always result in Infinity
* Regarding the users, make sure this is an array! If it is not an array, we will not process the users score,
* then our method will simply return false instead and stop processing
* Also make sure the users array is a list of actual user objects, we do not check this, but make sure the object has all the required fields as expected
* especially the score object is important in this case.
* @returns {boolean} Returns true if successful, false if not processed.
*/
function updateScoreForUsers(score, users) {
if (isNaN(score) || typeof users !=='array') { return false; }
users.forEach((user)=> {
user.score +=score;
}
return true;
}
所以,請(qǐng)確保你的注釋不要太長(zhǎng)。有那么多想說(shuō)的,可以寫文檔、可以寫文章,不要寫注釋~
簡(jiǎn)單直接是最迷人的!
這是另一個(gè)極端的例子,但是它確實(shí)源自于現(xiàn)實(shí)的開(kāi)發(fā)項(xiàng)目中。
// userHelper.js
/**
* Update score
* @returns {boolean} result
*/
function updateScoreForUsers(score, users) {
if (isNaN(score) || typeof users !=='array') { return false; }
users.forEach((user)=> {
user.score +=score;
}
return true;
}
此段代碼注釋只是說(shuō)明了返回值,但是更為關(guān)鍵的傳參并未作出釋義。顯得有些尷尬~
如果你決定注釋,那就不要只寫一半。請(qǐng)盡量準(zhǔn)確、完整、干凈的將其寫出。從長(zhǎng)期來(lái)看,你一定會(huì)從中受益。
好的注釋就是告訴大家你為什么要進(jìn)行注釋!
比如:
// userHelper.js
function updateScoreForUsers(score, users) {
users.forEach((user)=> {
user.score +=score;
// VIPs are promised 500 extra score on top
if (user.type=='VIP') {
user.score +=500;
}
}
return true;
}
此例中我們可以明白 VIP 用戶是被產(chǎn)品要求多加 500 分值的。這樣其它開(kāi)發(fā)就不會(huì)對(duì)此產(chǎn)生疑惑。
如果代碼由多個(gè)團(tuán)隊(duì)維護(hù),簡(jiǎn)單直接的小標(biāo)注就更為必要了!
注釋在代碼中扮演很重要的角色。本瓜還記得大學(xué)老師說(shuō):注釋應(yīng)該占代碼的三分之一。
我們都有不同的注釋習(xí)慣,但是也應(yīng)該有一個(gè)基本的指導(dǎo):
代碼可讀就是最好的注釋。
我是掘金安東尼: 一名人氣前端技術(shù)博主(文章 100w+ 閱讀量)
終身寫作者(INFP 寫作人格)
堅(jiān)持與熱愛(ài)(簡(jiǎn)書打卡 1000 日)
我能陪你一起度過(guò)漫長(zhǎng)技術(shù)歲月嗎(以夢(mèng)為馬)
覺(jué)得不錯(cuò),給個(gè)三連吧(這是我最大的動(dòng)力 )
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。