在 J2SE 5.0 之前,為現有類別階層新增操作(如匯出)常用 Visitor 模式:
// Employee 階層需新增 accept 方法
public abstract class Employee {
public abstract void accept(Exporter visitor);
}
// 每個具體類別實作雙重分派
public class Regular extends Employee {
public void accept(Exporter v) { v.visit(this); }
}
// Exporter 介面需針對每個具體類別定義 visit 多載
public interface Exporter {
void visit(Regular e);
void visit(Vendor e);
void visit(Contractor e);
// 新增 Employee 子類別 → 必須修改 Exporter 介面及所有實作!
}
核心缺陷:違反開放封閉原則——新增匯出格式需修改 Exporter 介面,新增 Employee 子類別需修改 Exporter 及所有實作,雙向耦合。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Exportable { }
@Target(FIELD):僅允許標註欄位。@Retention(RUNTIME):執行期可透過反射讀取。@Inherited:子類別繼承父類別欄位的註解(針對類別註解有效,欄位註解需配合掃描邏輯)。
public abstract class Employee {
@Exportable protected String name;
@Exportable protected String empNo;
// 子類別繼承欄位自動擁有註解
}
public class Regular extends Employee {
@Exportable private String title;
@Exportable private String phone;
@Exportable private String location;
@Exportable private Date hireDate;
@Exportable private ArrayList members; // 集合欄位特殊處理
}
public class Vendor extends Employee {
@Exportable private String company;
@Exportable private String product;
@Exportable private String id;
}
核心邏輯:掃描類別所有宣告欄位 → 過濾 @Exportable → 依型別分派輸出(簡單欄位/集合遞迴)。詳見前文 ExportableGenerator.genDoc() 實作。
// 文字格式
public class TXTExportableGenerator extends ExportableGenerator {
@Override protected void outputSimpleField(PrintWriter out, Field f, Object obj) {
out.print(f.getName() + "=" + value(f, obj) + ";\n");
}
@Override protected void outputFieldHeader(PrintWriter out, Object e) {}
@Override protected void outputFieldFooter(PrintWriter out, Object e) {}
}
// XML 格式(新增格式零修改現有代碼)
public class XMLExportableGenerator extends ExportableGenerator { /* ... */ }
| 維度 | Visitor 模式 | 註解驅動 |
|---|---|---|
| 新增匯出格式 | 修改 Exporter 介面 + 所有實作 | 新增 Generator 子類別(零修改) |
| 新增 Employee 子類別 | 修改 Exporter 介面 + 所有實作 | 無需修改框架,自動掃描新欄位 |
| 新增/移除欄位 | 修改對應 visit 方法 | 僅增減欄位上的 @Exportable |
| 程式碼侵入性 | 高(需 accept/visit 雙重分派) | 低(單一欄位註解) |
| 編譯期型別安全 | 高(介面強制實作) | 中(執行期反射,可配合測試保證) |
| 效能 | 極高(直接方法呼叫) | 中(反射開銷,可快取 Field 陣列優化) |
New → Annotation 自動生成 @interface 骨架,游標定位於元註解區。@Exportable 加在方法上 → 即時紅線「Annotation type not applicable to method」。isAnnotationPresent 觸發內容輔助,自動補全泛型參數。Ctrl+1 一鍵生成三個 output... 方法存根。註解類型配合反射,提供了一種宣告式、零侵入、格式解耦的替代方案,在「資料驅動操作」場景下完美替代 Visitor 模式,符合開放封閉原則,大幅降低維護成本。Eclipse 3.1 的工具鏈讓這種模式的開發門檻極低,值得成為現代 Java 開發者的標準工具箱之一。