為 OVal 鋪好了路、為 AOP 過程做了引導(dǎo)之后,可以開始使用 Java 5 標(biāo)注來為代碼指定簡(jiǎn)單的約束條件了。
OVal 的可重用約束條件
用 OVal 為方法指定前置條件必須對(duì)方法參數(shù)進(jìn)行標(biāo)注。相應(yīng)地,當(dāng)調(diào)用一個(gè)用 OVal 約束條件標(biāo)注過的方法時(shí),OVal 會(huì)在該方法真正執(zhí)行前 驗(yàn)證該約束條件。
在我的例子中,我想要指定當(dāng) Class 參數(shù)的值為 null 時(shí),buildHierarchy 方法不能被調(diào)用。OVal 通過 @NotNull 標(biāo)注支持此約束條件,該標(biāo)注在方法所需的所有參數(shù)前指定。也要注意,任何想要使用 OVal 約束條件的類也必須在類層次上指定 @Guarded 標(biāo)注,像我在清單 7 中所做的那樣:
清單 7. OVal 約束條件
import net.sf.oval.annotations.Guarded;
import net.sf.oval.constraints.NotNull;
@Guarded
public class HierarchyBuilder {
public static Hierarchy buildHierarchy(@NotNull Class clzz){
Hierarchy hier = new Hierarchy();
hier.setBaseClass(clzz);
Class superclass = clzz.getSuperclass();
if(superclass != null && superclass.getName().equals("java.lang.Object")){
return hier;
}else{
while((clzz.getSuperclass() != null) &&
(!clzz.getSuperclass().getName().equals("java.lang.Object"))){
clzz = clzz.getSuperclass();
hier.addClass(clzz);
}
return hier;
}
}
}
通過標(biāo)注指定這個(gè)約束條件意味著我的代碼不再會(huì)被重復(fù)的條件弄得亂七八糟,這些條件檢查 null 值,并且一旦找到該值會(huì)拋出異!,F(xiàn)在這項(xiàng)邏輯由 OVal 處理,且處理的方法有些相似 —— 事實(shí)上,如果違反了約束條件,OVal 會(huì)拋出一個(gè) ConstraintsViolatedException,它是 RuntimeException 的子類。
當(dāng)然,我下一步要編譯 HierarchyBuilder 類和 清單 5 中相應(yīng)的 DefaultGuardAspect 類。我用 清單 6 中的 iajc 任務(wù)來實(shí)現(xiàn)這一目的,這樣我能把 OVal 的行為編入我的代碼中了。
接下來,我更新 清單 4 中的測(cè)試用例來驗(yàn)證是否拋出了一個(gè) ConstraintsViolatedException,如清單 8 所示:
清單 8. 驗(yàn)證是否拋出了 ConstraintsViolatedException
@Test(expectedExceptions={ConstraintsViolatedException.class})
public void verifyHierarchyNull() throws Exception{
Class clzz = null;
HierarchyBuilder.buildHierarchy(clzz);
}
指定后置條件
正如您所見,指定前置條件其實(shí)相當(dāng)容易,指定后置條件的過程也是一樣。例如,如果我想對(duì)所有調(diào)用 buildHierarchy 的程序保證它不會(huì)返回 null 值(這樣,這些調(diào)用程序不需要再檢查這個(gè)了),我可以在方法聲明之上放置一個(gè) @NotNull 標(biāo)注,如清單 9 所示:
清單 9. OVal 中的后置條件
@NotNull
public static Hierarchy buildHierarchy(@NotNull Class clzz){
//method body
}
當(dāng)然,@NotNull 絕不是 OVal 提供的惟一約束條件,但我發(fā)現(xiàn)它能非常有效地限制這些令人討厭的 NullPointerException,或至少能夠快速地暴露 它們。
更多的 OVal 約束條件
OVal 也支持在方法調(diào)用前或后對(duì)類成員進(jìn)行預(yù)先驗(yàn)證。這種機(jī)制具有限制針對(duì)特定約束條件的重復(fù)條件測(cè)試的好處,如集合大小或之前討論過的非 null 的情況。
例如,在清單 10 中,我使用 HierarchyBuilder 定義了一個(gè)為類層次構(gòu)建報(bào)告的 Ant 任務(wù)。請(qǐng)注意 execute() 方法是如何調(diào)用 validate 的,后者會(huì)依次驗(yàn)證 fileSet 類成員是否含值;如果不含,會(huì)拋出一個(gè)異常,因?yàn)闆]有了要評(píng)估的類,該報(bào)告不能運(yùn)行。
清單 10. 帶條件檢驗(yàn)的 HierarchyBuilderTask
public class HierarchyBuilderTask extends Task {
private Report report;
private List fileSet;
private void validate() throws BuildException{
if(!(this.fileSet.size() > 0)){
throw new BuildException("must supply classes to evaluate");
}
if(this.report == null){
this.log("no report defined, printing XML to System.out");
}
}
public void execute() throws BuildException {
validate();
String[] classes = this.getQualifiedClassNames(this.fileSet);
Hierarchy[] hclz = new Hierarchy[classes.length];
try{
for(int x = 0; x < classes.length; x++){
hclz[x] = HierarchyBuilder.buildHierarchy(classes[x]);
}
BatchHierarchyXMLReport xmler = new BatchHierarchyXMLReport(new Date(), hclz);
this.handleReportCreation(xmler);
}catch(ClassNotFoundException e){
throw new BuildException("Unable to load class check classpath! " + e.getMessage());
}
}
//more methods below....
}