為 OVal 鋪好了路、為 AOP 過程做了引導之后,可以開始使用 Java 5 標注來為代碼指定簡單的約束條件了。
OVal 的可重用約束條件
用 OVal 為方法指定前置條件必須對方法參數(shù)進行標注。相應地,當調用一個用 OVal 約束條件標注過的方法時,OVal 會在該方法真正執(zhí)行前 驗證該約束條件。
在我的例子中,我想要指定當 Class 參數(shù)的值為 null 時,buildHierarchy 方法不能被調用。OVal 通過 @NotNull 標注支持此約束條件,該標注在方法所需的所有參數(shù)前指定。也要注意,任何想要使用 OVal 約束條件的類也必須在類層次上指定 @Guarded 標注,像我在清單 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;
}
}
}
通過標注指定這個約束條件意味著我的代碼不再會被重復的條件弄得亂七八糟,這些條件檢查 null 值,并且一旦找到該值會拋出異常,F(xiàn)在這項邏輯由 OVal 處理,且處理的方法有些相似 —— 事實上,如果違反了約束條件,OVal 會拋出一個 ConstraintsViolatedException,它是 RuntimeException 的子類。
當然,我下一步要編譯 HierarchyBuilder 類和 清單 5 中相應的 DefaultGuardAspect 類。我用 清單 6 中的 iajc 任務來實現(xiàn)這一目的,這樣我能把 OVal 的行為編入我的代碼中了。
接下來,我更新 清單 4 中的測試用例來驗證是否拋出了一個 ConstraintsViolatedException,如清單 8 所示:
清單 8. 驗證是否拋出了 ConstraintsViolatedException
@Test(expectedExceptions={ConstraintsViolatedException.class})
public void verifyHierarchyNull() throws Exception{
Class clzz = null;
HierarchyBuilder.buildHierarchy(clzz);
}
指定后置條件
正如您所見,指定前置條件其實相當容易,指定后置條件的過程也是一樣。例如,如果我想對所有調用 buildHierarchy 的程序保證它不會返回 null 值(這樣,這些調用程序不需要再檢查這個了),我可以在方法聲明之上放置一個 @NotNull 標注,如清單 9 所示:
清單 9. OVal 中的后置條件
@NotNull
public static Hierarchy buildHierarchy(@NotNull Class clzz){
//method body
}
當然,@NotNull 絕不是 OVal 提供的惟一約束條件,但我發(fā)現(xiàn)它能非常有效地限制這些令人討厭的 NullPointerException,或至少能夠快速地暴露 它們。
更多的 OVal 約束條件
OVal 也支持在方法調用前或后對類成員進行預先驗證。這種機制具有限制針對特定約束條件的重復條件測試的好處,如集合大小或之前討論過的非 null 的情況。
例如,在清單 10 中,我使用 HierarchyBuilder 定義了一個為類層次構建報告的 Ant 任務。請注意 execute() 方法是如何調用 validate 的,后者會依次驗證 fileSet 類成員是否含值;如果不含,會拋出一個異常,因為沒有了要評估的類,該報告不能運行。
清單 10. 帶條件檢驗的 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....
}