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

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

前面的文章中我们看到了GroovyBean的基本语法。除了能用少量的代码实现JavaBean的功能以外,GroovyBean还提供了JavaBean标准里的 绑定属性和限制属性。绑定属性是一个属性,当它的值发生变化是会通知其他的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

你可能感兴趣的:(groovy)