首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

掌握 Eclipse JFace 數據綁定(三):高階功能實戰——轉換器、驗證器、主從表與可測試架構

     編輯:Java 相關知識

本教程基於 Eclipse 3.2 JFace 數據綁定 API,深入講解**轉換器**、**驗證器**、**表格主從綁定**及**Presentation Model 模式**構建可測試 UI 架構。前置需求:Java 經驗、SWT/JFace 基礎、已閱讀系列第一部分。

可測試代碼的關鍵:Presentation Model 模式

傳統桌面應用業務邏輯耦合 UI 控件,難以單元測試。**Presentation Model** 將狀態與業務邏輯從 UI 提取,測試無需實例化控件,UI 僅負責同步顯示。

重構前:耦合 UI 的啟用邏輯


class YearsMarriedEnablementListener implements ModifyListener {
    public void modifyText(ModifyEvent e) {
        boolean enable = nameTxt.getText().trim().length()>0 
                      && spouseTxt.getText().trim().length()>0;
        if (!enable) yearsMarriedTxt.setText("");
        yearsMarriedTxt.setEnabled(enable); // 直接操作 UI 控件
    }
}

測試需啟動完整 UI,脆弱且緩慢。

重構後:Presentation Model

  1. 領域模型:Contact (name, spouse, yearsMarried)
  2. Presentation Model:ContactPresentationModel 持有 Contact 與 enableYearsMarried 狀態,實現啟用邏輯,觸發 PropertyChangeEvent。
  3. UI 綁定:DataBindingContext 綁定 UI 控件屬性與 Presentation Model 屬性。

// Presentation Model 核心邏輯
private class EnablementPropertyChangeListener implements PropertyChangeListener {
    public void propertyChange(PropertyChangeEvent evt) {
        boolean enable = contact.getName()!=null && !contact.getName().trim().isEmpty()
                      && contact.getSpouse()!=null && !contact.getSpouse().trim().isEmpty();
        if (!enable) contact.setYearsMarried(null);
        setEnableYearsMarried(enable);
    }
}
// 構造子註冊監聽
contact.addPropertyChangeListener("name", listener);
contact.addPropertyChangeListener("spouse", listener);

測試純單元,無 UI 依賴:


assertFalse(pm.getEnableYearsMarried());
pm.getContact().setName("Name"); assertFalse(pm.getEnableYearsMarried());
pm.getContact().setSpouse("Spouse"); assertTrue(pm.getEnableYearsMarried());
pm.getContact().setYearsMarried("5");
pm.getContact().setSpouse(""); 
assertFalse(pm.getEnableYearsMarried());
assertNull(pm.getContact().getYearsMarried());

BindSpec:進階綁定配置

BindSpec 指定模型↔目標雙向轉換器、驗證器:

  • IConverter:getFromType()、getToType()、convert(Object)
  • IValidator:isPartiallyValid()(輸入中即時驗證)、isValid()(焦點離開完整驗證)

自定義轉換器:Boolean ↔ String

需求:將「Married」核取方塊改為下拉框(Yes/No),綁定 boolean enableYearsMarried。


class BooleanToStringConverter implements IConverter {
    Object getFromType() { return Boolean.TYPE; }
    Object getToType() { return String.class; }
    Object convert(Object v) { return ((Boolean)v) ? "Yes" : "No"; }
}
class StringToBooleanConverter implements IConverter { ... } // 反向

// 綁定下拉框
ctx.bind(new Property(combo, SWTProperties.SELECTION),
         new Property(pm, "enableYearsMarried"),
         new BindSpec(new BooleanToStringConverter(), new StringToBooleanConverter(), null, null));

自定義驗證器:YearsMarried 僅限數字且不可為 5


class YearsMarriedValidator implements IValidator {
    ValidationError isPartiallyValid(Object v) {
        try { Integer.valueOf((String)v); return null; }
        catch (NumberFormatException e) { return new ValidationError(ERROR, "Not A Number"); }
    }
    ValidationError isValid(Object v) {
        return "5".equals(v) ? ValidationError.error("5 Is Not Allowed") : null;
    }
}

// 綁定驗證器
ctx.bind(yearsMarriedTxt, new Property(contact, "yearsMarried"), 
         new BindSpec(null, null, new YearsMarriedValidator(), null));

觀察驗證錯誤:即時反饋 UI

Binding 物件暴露 getPartialValidationError()、getValidationError() 可觀測對象,綁定至 Label 顯示錯誤訊息:


ctx.bind(partialErrorLabel, binding.getPartialValidationError(),
         new BindSpec(new ValidationErrorToStringConverter(), new ReadOnlyConverter(...), null, null));
ctx.bind(fullErrorLabel, binding.getValidationError(), ...);

主從表綁定:TableViewer + 詳情表單

  1. TablePresentationModel:持有 List<Contact> contacts 與 WritableValue selectedContact(選中項佔位符)。
  2. TableForm:TableViewer 綁定內容與單選:

ctx.bind(new Property(viewer, ViewersProperties.CONTENT),
         new TableModelDescription(new Property(pm, "contacts", Contact.class, true), 
                                   new String[]{"name","spouse"}), null);
ctx.bind(new Property(viewer, ViewersProperties.SINGLE_SELECTION),
         pm.getSelectedContactObservable(), null);

需註冊 ViewersObservableFactory、ViewersBindingFactory 至 DataBindingContext。

間接綁定:連接主從模型

將 ContactForm 綁定至 IObservableValue(WritableValue)而非直接綁定 Contact:


// ContactForm 構造子接收 IObservableValue
ctx.bind(nameTxt, new Property(contactObs, "name", String.class, false), ...);

// 運行時注入選中項
ContactPresentationModel pm = new ContactPresentationModel(new WritableValue(Contact.class));
pm.getContactObservable().setValue(selectedContactFromTable);

表格選中項變更 → WritableValue 更新 → 詳情表單自動同步,雙向綁定無縫聯動。

結語

JFace 數據綁定高階功能——轉換器、驗證器、主從表、間接綁定——配合 Presentation Model 模式,將開發者從繁瑣同步代碼中解脫,構建結構清晰、高度可測試、易維護的豐富客戶端應用。核心 API 涵蓋 JavaBean/SWT/JFace 屬性通用引用,強大擴展點支撐複雜業務場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved