Groovy: 比JavaBean好用的GroovyBean - @Bindable和@Vetoable

[size=x-large]Groovy: 比JavaBean好用的GroovyBean - Bindable和Vetoable[/size]

在[url=http://zjumty.iteye.com/blog/1885978]前面的文章[/url]中我們看到了GroovyBean的基本語法。除了能用少量的代碼實現JavaBean的功能以外,GroovyBean還提供了JavaBean標準裏的[url=http://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html]綁定屬性和限制屬性[/url]。綁定屬性是一個屬性,當它的值發生變化是會通知其他的Bean。限制屬性是一個屬性,當這個屬性變化會被其他Bean驗證。在這裏的如果有需要其他的Bean可以阻止變化。

在Groovy裏實現這兩個屬性是非常簡單的。通過@Bindable和@Vetoable標註就可以輕鬆實現。下面是一個例子:


import groovy.beans.*

class Car {
int numberOfDoors
@Vetoable String model
@Vetoable String brand
boolean automatic
@Bindable double price

String toString() {
"[Car details => brand: '${brand}', model: '${model}', #doors: '${numberOfDoors}', automatic: '${automatic}', price: '${price}']"
}
}


這就OK了。當我們編譯上面的類的時候Groovy會自動加上所有需要的addXXXListener方法。上面的類如果用Java來寫是這樣的:


import java.beans.*;

public class Car {
private int numberOfDoors;
private String model;
private String brand;
private boolean automatic;
private double price;

private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private final VetoableChangeSupport vcs = new VetoableChangeSupport(this);

public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}

public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}

public void addVetoableChangeListener(VetoableChangeListener listener) {
vcs.addVetoableChangeListener(listener);
}

public void removeVetoableChangeListener(VetoableChangeListener listener) {
vcs.removeVetoableChangeListener(listener);
}

public void setPrice(double price) {
double oldPrice = this.price;
this.price = price;
pcs.firePropertyChange("price", oldPrice, price);
}

public double getPrice() {
return this.price;
}

public void setModel(String model) throws PropertyVetoException {
String oldModel = this.model;
vcs.fireVetoableChange("model", oldModel, model);
this.model = model;
pcs.firePropertyChange("model", oldModel, model);
}

public String getModel() {
return this.model;
}

public void setBrand(String model) throws PropertyVetoException {
String oldBrand = this.brand;
vcs.fireVetoableChange("model", oldBrand, brand);
this.brand = brand;
pcs.firePropertyChange("model", oldBrand, brand);
}

public String getBrand() {
return this.brand;
}

public void setNumberOfDoors(int numberOfDoors) {
this.numberOfDoors = numberOfDoors;
}

public int getNumberOfDoors() {
return numberOfDoors;
}

public void setAutomatic(boolean automatic) {
this.automatic = automatic;
}

public boolean isAutomatic() {
return this.automatic;
}

public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("[Car details => brand: '");
builder.append(brand);
builder.append("', model: '");
builder.append(model);
builder.append("', #doors: '");
builder.append(numberOfDoors);
builder.append("', automatic: '");
builder.append(automatic);
builder.append("', price: '");
builder.append(price);
builder.append("']");
return builder.toString();
}
}


通過上面的代碼,好處顯而易見了

下面我們用一段Groovy腳本看一下怎麼監聽propertychange和vetoablechange事件。同樣用Groovy來實現的話會簡單的多。在Groovy中我們可以用閉包來實現Listener接口。下面的代碼監聽屬性變化並阻止值發生變化:


import groovy.beans.*
import java.beans.*

def toyota = new Car(brand: 'Toyota', model: 'Verso', price: 28919, numberOfDoors: 5)
toyota.propertyChange = {
if (it.propertyName == 'price') {
println "The price has changed. Inform sales the new price is '${it.newValue}'."
}
}
toyota.vetoableChange = { PropertyChangeEvent pce ->
if (pce.propertyName == "brand") {
if (!(pce.newValue in ['Toyota', 'Lexus'])) {
throw new PropertyVetoException('New value is not Toyota or Lexus', pce)
}
}
if (pce.propertyName == "model") {
if (pce.newValue ==~ /.*\d+.*/) {
throw new PropertyVetoException('No numbers in model names allowed.', pce)
}
}
}

toyota.price = 30995
assert 30995 == toyota.price

toyota.brand = 'Lexus'
assert 'Lexus' == toyota.brand

try {
toyota.brand = 'AUDI'
assert false: 'We should not be able to set this value.'
} catch (PropertyVetoException e) {
assert true
}

try {
toyota.model = 'A5'
assert false: 'We should not be able to set this value.'
} catch (PropertyVetoException e) {
assert true
}


Groovy的Car類是被編譯成Java的字節碼,所以我們可以在普通的Java程序中使用上面的Car類。下面是在Java中使用Car的監聽器接口。注意我們必須用groovyc來編譯上面的代碼,並且不能使用匿名內部類最後接口實現。


import java.beans.*;
import java.util.regex.*;

public class CarApp implements PropertyChangeListener, VetoableChangeListener {
public static void main(String[] args) {
Car toyota = new Car();
toyota.setModel("Verso");
toyota.setBrand("Toyota");
toyota.setNumberOfDoors(5);
toyota.setPrice(28919);

CarApp app = new CarApp();
toyota.addPropertyChangeListener(app);
toyota.addVetoableChangeListener(app);

toyota.setPrice(30995);
toyota.setBrand("Lexus");
try {
toyota.setBrand("AUDI");
} catch (PropertyVetoException e) {
System.out.println("Brand is not changed.");
}
try {
toyota.setModel("A5");
} catch (PropertyVetoException e) {
System.out.println("Model is not changed.");
}
}

public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("price")) {
System.out.println("The price has changed. Inform sales the new price is '" + evt.getNewValue() + "'.");
}
}

public void vetoableChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("brand")) {
if (!isValidBrand(evt.getNewValue())) {
throw new PropertyVetoException("New value is not Toyota or Lexus", evt)
}
}
if (evt.getPropertyName().equals("model")) {
if (!isValidModel(evt.getNewValue())) {
throw new PropertyVetoException("No numbers in model names allowed.", evt)
}
}
}

private boolean isValidBrand(String newValue) {
final String[] names = new String[2];
names[0] = "Toyota";
names[1] = "Lexus";
for (String name: names) {
if (newValue.equals(name)) {
return true;
}
}
return false;
}

private boolean isValidModel(String model) {
return !Pattern.matches(".*\\d+.*", model);
}
}


怎麼樣?使用@Bindable和@Vetoable一切都變得簡單多了吧!

http://mrhaki.blogspot.com/2009/08/groovy-goodness-bound-and-constrained.html
發佈了15 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章