2009年5月12日 星期二

V09 自動補齊註解(4)──Method使用關聯與判斷內容

在處理Method流程的同時,我們可以拿到Method裡每一行敍述所操作的方法,收集每一個方法內所呼叫的全部方法(不管是元件內部或是其它的Component Interface Method),就能夠再作進一步的分析使用。

對一個Method而言,解析過裡頭的程式碼同時收集所有呼叫方法意義是:該Method所使用的全部Method有精確的範圍。再進一步地分析,方法有無論如何都會執到、提供條件敍述判斷用、滿足特定條件判斷才會執行到的三種類型,第一種表示的是百分之百都會用到的動作(include)、第二種表示的是造成處理流程分歧的判斷(decision)、第三種表示的是前述判斷條件成立後才會執行到的動作(exclude)。

方法使用關聯的應用意義在於追溯,從Use Case Method往下看是完成所必須具備的全部Method,從指定Method往上看則是找出每一層直接或間接使用它的關係;關聯的層級也可以從Method可以推展到Class。以此為基礎產生出來的就會是各個層級裡所有Class(或Method)的水平追溯與垂直追溯,在Class層級等於Use Case層級時,拿到的是Use Case的水平追溯與垂直追溯。

在條件敍述裡判斷的方法可以被另外區分出來,涵蓋所有條件分歧路線與所有條件值的測試才會是完整的。經過這一層的處理,應該還可以產出所有影響分歧條件的Method清單並同時收集每個條件Method本身全部的可能傳回值,撰寫Test Case時可以快速參考所有可能的變化加以設計測試。

使用與判斷的資訊我認為放在Method的Java Doc比較符合其存在的位置意義,經由處理程式自動產生,並在日後每次執行的時候自動修改這個部分的內容。

2009年5月11日 星期一

V08 自動補齊註解(3)──補齊Method內的程式碼註解

元件六個部分裡Properties、Model與Exception的程式碼是簡單到不需要用註解說明的,自動補齊程式碼註解的對象是Implementation、Flow與Action。依照我的設計準則,每一層被上一層呼叫到的入口Method都應該只用程式流程的敍述來呼叫實際動作的方法。

流程相關的方法有以下五種,標示種類為流程者表示內部應只放流程敍述與動作方法呼叫。
●Implementation Method(入口方法)
 beforeInvoke()與afterInvoke()內放的是流程,細部的動作只能是Flow Method或是其它的Component Interface。
●Flow Method且被Implementation Method直接呼叫(被入口方法呼叫的流程方法)
●Flow Method但只被Flow Method呼叫(內部流程方法)
 以上兩種都屬於流程,細部動作只能是Flow Method、Action Method或是其它的Component Interface。
●Action Method且被Flow Method直接呼叫(被流程方法呼叫的動作方法)
 這屬於實作流程,細部動作只能是Action Method或是其它的Component Interface。
●Action Method但只被Action Method呼叫(內部動作方法)
 這屬於實作流程,細部動作只能是Action Method或是其它的Component Interface。不過到這層應該大多都是呼叫很底層的API,產生的流程內容已經不易讀懂。

除了最後一種Method之外,其他種類的Method只能放置流程控制的指令。為這幾種指令定義好註解的範本,再從實際執行的方法註解裡取得第一行的功能簡述嵌入到註解範本裡,產成註解結果後放置到程式碼前即可。依序執行每個Class的每個Method裡的每行程式,就可以用同樣的方法產生全部的程式碼註解,並可以隨時依真實的註解同步最新的內容。

2009年5月8日 星期五

V07 自動補齊註解(2)──Method的傳入參數與傳回值

版權宣告與Class註解僅是靜態的說明,解析的重點還是在於Method。Java Doc的最基本是說明Method的功能,使用param記錄傳入參數名稱與類型、return記錄傳回類型、throws記錄拋出的例外類型,都與Method的宣告行有關。

設計者在改變方法宣告的同時,不一定會去更正Java Doc的內容,因此仍然需要加上自動補齊的功能令它們同步。註解需要的內容都可以從Method宣告行拿到,列出的資訊與Java Doc轉成的Data Model比較後,刪除多出的屬性、自動補上缺少的欄位並調整順序以符合Method的宣告。自動產生的部分應加上標示(例如[AUTO])以方便事後搜尋出來作人工的調整。

開發團隊都會定義自己所用的Naming Rules,在處理Class與Method的同時也可以依照規則檢查各個屬性的名稱;同時也可以檢查屬性名稱後是否有加上說明。以上應有而未有的註解可以在應加上的地方註明[TODO]表示少了些東西,以便快速定位後補加上去。

到現在為止處理的是Class與Method宣告的部分,在詳細描述的區塊務必在先用第一句簡短地描述其功用,後面再跟著註記詳細的說明;這是因為未來需要Class與Method的註解時會取得第一個句子作為使用內容(避免內容過多)。

註:如果Method是實作Interface或是覆寫父類別的Method,就不要在這層產生宣告註解而應該產生指向宣告的註解內容。

2009年5月7日 星期四

V06 自動補齊註解(1)──格式正確性與基本資訊

註解是人為按照格式輸入的,編輯器會自動檢查註解是否有造成程式編譯不正確的錯誤,但是註解內容的格式就需要另外用工具來判別。除了檢查格式之外,應該同時從預先建立的基本資訊檔裡取得對應的內容自動地放入註解並儲存。

package宣告之前通常應有版權宣告段落,class註解內通常會有since、author、version;Method註解雖然有param、return、throws但是打算也加上since、author等資訊,這是因為Method可能會因重構移動到其它Class裡而造成資訊的錯失。

雖然Eclipse可以匯入標準的註解格式,但是格式可能因不同的需求而修改存放資訊,在需要變動的時候絕對不會有人一一將成千上萬的Class取出來重新調整;同樣地如果有人忘了匯入而造成缺少註解內容,若那人不動聲色地放入儲存庫也不見得有人發現或是會更正。用自動的功能消弭掉人為的誤差讓所有的產出一致無論在什麼領域都是非常重要的事。

整合以上的需要註記的欄位就是應該放在基本資訊檔裡的資訊,這些資訊區分為公司通用(版權宣告)、版本使用(version)、個人專用(author)與時間相關(since、版權宣告裡的年份),設計時依不同用途放置在不同的資料夾中備取。處理時若有缺少的欄位就從資訊檔取得,存在的欄位再定義檢查的規則看是否有誤。

2009年5月6日 星期三

V05 註解的存取(3)──取得註解的內容

為了確認Java程式碼與註解的關聯,實際在workspace裡測試了Eclipse JDT的功能。JDT裡共有兩個系列可以處理Java程式,分別是CompilationUnit與AST。

AST採用DOM的結構方式解析程式碼,從Class、Field、Method甚至是方法裡的Block、Statement都能找到對應的物件,不管是解析的深度(CompilationUnit只能拿到Class、Field與Method)或是API的便利都比CompilationUnit好用。但是AST從Eclipse 2.1起就因為註解的複雜而取消在Block與Statement上附帶Java Comment的功能,只在Type(等同於Class)層級集合該程式的全部註解;無法建立註解對應程式碼的關聯,這對我後續想做到的事是沒有幫助的。CompilationUnit的解析沒有進入Method內,也沒有提供直接取得Java Doc的方法,但是各物件都提供了getSource()的方法可快速拿到包含註解與宣告的程式碼。

程式碼的註解的確會造成判讀的困擾,像是因修改而註解掉的程式碼就很可能被視為下一行程式的註解。為了讓程式明白地知道該註解是資訊還是修改記錄,必須定義不同用途的註解方式:像是註解的程式碼一律在行首使用 // 、設計的記錄與屬性都用多行的 /* */ 包覆該程式碼前一行、修改的簡要資訊則先用單行的 /* */ 放在程式碼後讓處理程式整合到設計記錄。

用程式來處理程式碼是我想做的作法中最關鍵的技術。尋常的程式設計的自由度會令程式區塊內的意義不夠精確,所以要先定義每一個細部單位的意義;再來是解析後儲存的集合與每一個單位彼此之間關聯的建立。根據後面想要達成的功能,將會陸續明白需要在哪些地方建立什麼樣的關聯,這也必須加以明確的記錄。

至於要用哪一種技術來解析程式碼,現在也還不能決定。目前比較偏好CompilatoinUnit加上自己寫程式分析更細部的source,不過解析方法純粹自己撰寫也是能夠接受的選擇。後面的文章會暫時假設所有程式碼與註解的解析都百分之百正確,以此為基本前提來討論可以自動產出些什麼。

2009年5月5日 星期二

V04 註解的存取(2)──處理註解的順序

要開始撰寫處理註解內容的工具的準備工作有三個:首先要定義好註解的格式與對應的Data Model,再來是用什麼樣的流程來通過全部有註解的地方,最後則是用什麼樣的技術來取得該地方的註解內容。

現在有定義的程式註解全部都放在Java檔案裡。這裡需要用兩個方向來看待之:在Eclipse的workspace結構裡Java檔案的放置方式,與取得Java檔案後如果解析內部的結構。

要在workspace裡找到Java檔案,結構的層次依序為workspace、java project、source folder
、package四層(雖然package真實的資料夾結構是多層的,但是在Eclipse裡已經壓扁為一層),這意味著每一個層都得準備一層迴圈來重覆處理,同時在每一層的迴圈裡可能需要有自訂處理範圍的功能(初期先全部處理亦可)。經過這四層迴圈就可以取得全部的Java檔案作後續處理。

Java檔案的內部結構是Class(Interface)、Data或Method、Method內部的程式碼,其中Data與Method之間沒有關聯但是在一個Java檔案裡都是零到無數個的。所以處理一個Java檔案時要依序取得Class(Interface)註解、所有Data註解、所有Method註解與所有Method裡所有程式Comment,在取得的同時也要將關聯資訊放入註解Data Model。

在處理程式裡取得的註解Data Model都要依照結構放入對應的cache,目的是希望隨時都可藉由package、class、data與method等等的關鍵字找到對應的註解Data Model,處理完後全部的註解Data Model還必須匯出到現有的Java檔案裡一同儲存才算是完整的處理。

2009年5月4日 星期一

V03 如何寫好作文

作文的基本結構其實很接近Use Case-Scenario-Activity,可以用top-down的設計方式來鋪陳文章的內容。

先將整篇作文視為SRS(System Requirement Specification),作文的題目就像是Use Case Name,用很少的字數來表達全部文章的功用;SRS的第一個章節是描述Use Case目標的詳細說明,那麼第一段就可以先用較多的字數闡述主題(這適用於論文,記述文不要用)。

再來就要先在腦海裡製作文章的scenario,進入前的準備、起點、詳細流程、流程中各種可能分歧與最後的狀況,這些scenario相關的部分都必須先構築完整才能進行下一階段。如果流程只有一半就開始寫作,很有可能會跟現今的專案一樣寫到一半才發現流程有不足之處而難以收尾,屆時不是改寫出問題的那一段就是草草結束,這些都不會有好結果的。

如果需要其他看法的scenario佐證自己的論點,記得也要一併思考。能夠整合為一個流暢的scenario的話就一起表述,否則就依序分開寫作,但是要小心不要有前後的衝突而自打嘴巴。各個scenario都陳述的最後用一段結尾來說明這個Use Case採用我們上面作法的優點或是延伸的感想。這樣一來作文的全部架構就大致成形。

接著進入細部設計。scenario裡的每個activity是句子的集合,要決定用哪幾句話的句意來達到activity的目標,同時再設計每個句子裡要用什麼樣的字詞組合。心裡的模型如何拆解為對的句子順序關係到流暢度,記憶了多少字詞關係到可以選用的API多寡,這無法投機而要看各人事前所作的修行。組合之後再運用重構的想法調整用字遣詞與前後關係,確認後再落筆撰寫。

慢慢寫出心裡的結構與內容完成作文後,記得再閱讀一次測試來找出不理想的地方修正。通過測試後就可以無怨無悔地交卷了。

註:我的高中聯考作文分數為50.5/70、大學聯考作文得分也超過60%,這個論點應該很具有參考價值!

2009年5月2日 星期六

V02 註解本體的內容與註解的放置

註解要怎麼寫才是理想的?在網路上能找到一些不同的說法。比較普遍接受的說法是不要記載程式碼怎麼寫的,而要記錄為了什麼目的而寫這些程式碼;因為註解是為了讓其他人快速地看懂自己為什麼要這樣寫。對於這個看法我沒有異議,方法有自己必須達成的目標,方法內的一連串註解是達成目標的分解動作,一組詳細記錄的註解其作用有如該方法的SOP般讓所有人都能很快地看懂怎麼樣來完成。

註解以精簡為目標,但是在較艱深的場合還是附上簡短的範例說明較為理想。如果把人腦視作一部電腦,撰寫某段程式碼後寫出的註解就像是把當時的想法匯出為文字敍述,驗證註解內容是否恰當的一種方式,就是經過較長一段時間忘卻後讓原撰寫者看自己原來所寫的註解,倘使能夠讓自己完全回憶起當時的情景就是滿足所需的註解。

目前Class、Method與Data的Java Doc註解,以及程式上的Comment都跟著程式碼走。前面的Java Doc只存在於靜態說明的地方,即使屬性加多後行數增多,並不至於影響閱讀;但是Comment一般都要求越簡短越好,用我的方法製作Comment後勢必使得程式碼間的間隔拉大造成不易閱讀的現象,這並不符合普遍的需要。

龐大的Comment或許可以脫離程式另外存在?這是讓程式碼變回較易讀的方法之一,但是Comment與程式分離之後必須有自己的管理機制,同時還要建立Comment與程式碼的關聯。程式碼是非常易變的東西,建立在易變的基礎而且必須另外同步更新Comment勢必浪費非常多的時間。顯而易見地,這是一個蠢解法。

既然目的是在擁有詳細的註解時維持程式碼的可讀性,那麼可以學習Eclipse作法,在Java編輯器上把Comment區塊縮減為一行。開啟一個Class後其實我們可以發現有很多地方可以收合,像是最前面的版權宣告區塊、所有的Java Doc註解與Method的程式本體都是如此,但是對於Comment就沒有這樣的功能。改寫Java編輯器使之對Comment也有收合與展開的功能,就可以達成目的。

2009年5月1日 星期五

V01 註解的存取(1)──定義註解的格式

以下的各種註解範例取自於JDK 5.0裡頭的java.lang.StringTokenizer。

/**
* Class Description....
*
* @author Lee Boynton
* @author Arthur van Hoff
* @version 1.189, 10/21/05
* @see java.lang.Object#toString()
* @see java.lang.StringBuffer
* @see java.lang.StringBuilder
* @see java.nio.charset.Charset
* @since JDK1.0
*/
這是Java Doc對Class的註解範例。很明顯地具有描述的本體與下面用@符號開頭的屬性,屬性可能具有多個值。

/** Field Description.... */
private final char value[];
這是Java Doc對Data的註解範例。只擁有描述本體而沒有任何屬性的結構,很明顯地是Class註解的子集合。

/**
* Method Description....
*
* @param codePoints array that is the source of Unicode code points.
* @param offset the initial offset.
* @param count the length.
* @exception IllegalArgumentException if any invalid Unicode code point
* is found in codePoints
* @exception IndexOutOfBoundsException if the offset
* and count arguments index characters outside
* the bounds of the codePoints array.
* @since 1.5
*/
public String(int[] codePoints, int offset, int count) {
}
這是Java Doc對Method的註解範例。註解的屬性名稱雖與Class註解有所出入,但是基本的結構是一致的。

/* Reset these anyway */
delimsChanged = false;
// The array representing the String is the same
// size as the String, so no point in making a copy.
v = originalValue;
從String與StringTokenizer裡發現兩種不同的註解類型,都是合法的Comment。它們都只具有描述的本體而沒有任何屬性,與Data的註解結構相同。

2000年的時候曾經維護過某大公司應用在銀行的大型產品程式碼,註解裡鉅細靡遺地記錄著對應程式碼的修改歷程,每筆記錄存放了修改日期、修改人員與修改原因的資訊,同時Class上也收集了Class內所有的修改記錄,看起來非常地清楚。因此在註解的設計裡同樣選擇採用這個制度,讓修改歷程放在註解裡跟著程式碼走,額外再使用Java Doc的標籤讓這些記錄可以出現在產出文件裡。(以前的產品只能用文字搜尋)

U22提到了收集所有註解另外處理的功能,經過收集後的註解已經與程式碼脫離,所以需要額外的欄位來定義與其相關的程式部位:Class、Data註解需要記載屬於哪一個Class,Method、Comment註解需要記載屬於哪一個Class的Method,將這些資訊定義在一個關聯用的Attribute(id)裡。

以上是註解Data Model結構(U23)的基本設計依據。