Data Binding
Introducción
El término data binding se refiere al concepto de procesar las entradas de datos desde la interfaz de usuario a los modelos del dominio de la aplicación de forma dinámica y más o menos automática. Anteriormente hemos visto como utilizar el sistema de data binding a mediante AbstractView. En este capítulo veremos los detalles de la implementación.
Control Accessor
Escribir código genérico para los componentes Swing es una tarea compleja debido a que es necesario el conocimiento específico del tipo de control incluso para operaciones simples. Por ejemplo, para obtener el valor contenido en un control Swing se deben utilizar métodos diferentes si se trata de un JTexField o de un JCheckBox. Nos encontramos el mismo problema si queremos ser notificados de cambios en los valores del componente. Debemos añadir un Listener distinto en función del tipo.
La interfaz ControlAccessor trata de resolver este problema, permitiendo escribir código que es independiente del tipo del componente Swing. Básicamente la idea es la misma que está detrás de la interfaz PropertyEditor de la especificación JavaBeans o en el formidable BeanWrapper de Spring Framework.
ControlAccessor define seis métodos para permitir un acceso básico a cualquier tipo de componente Swing:
- setControlValue(Object value) actualiza el valor contenido en el control.
- getControlValue() obtiene el valor contenido en el control.
- addControlChangeListener(ControlChangeListener l) añade un ControlChangeListener que será notificado en los cambios de valor del control.
- removeControlChangeListener(ControlChangeListener l) elimina un ControlChangeListener añadido anteriormente.
- setEnabled(boolean enabled) activa/desactiva el control.
- isEnabled() devuelve true si el control está activo.
Control Accessor Factory
Debido a que queremos operar sobre componentes Swing del que en principio, no conocemos el tipo, necesitamos un medio para asociar el componente con su ControlAccessor específico. Este es la fábrica de ControlAcessors. La implementación por defecto que proporciona JDAL, ConfigurableControlAccessorFactory utiliza un mapa para asociar los ControlAccessor a la clase del componente.
... ControlAccessor controlAccesor = controlAccessorFactory.getControlAccessor(control); if (controlAccessor != null) { controlAccessor.addControlChangeListener(this); controlAccessor.setControlValue(someValue); } ...
Binder
La interfaz Binder añade métodos a ModelHolder que permiten mover los datos entre el control de la interfaz de usuario y el modelo del dominio:
- update(): Actualiza el modelo a partir de la información del control.
- refresh(): Actualiza el control a partir de la información del modelo.
- getBindingResult() Devuelve el resultado de la operación update().
JDAL delega la obtención del Binder apropiado a la fábrica BinderFactory que contiene un FactoryMethod que asocia un Binder con la clase Java del componente.
La interfaz PropertyBinder es una especialización de Binder que permite asociar un control a una propiedad de un modelo.
Finalmente la clase CompositeBinder representa un agregado de PropertyBinders que comparten el mismo modelo.
Composite Binder
Todo el trabajo de data binding lo realiza la clase CompositeBinder. Consiste en un agregado de Binders que, generalmente operan sobre el mismo modelo.
CompositeBinder proporciona siguientes métodos relacionados con el binding de datos.
- bind(String propertyPath, Object component, T model, boolean readOnly) Crea un binding entre la propiedad referenciada por propertyPath en el modelo y el componente.
- getBinder(StringPropertyPath Obtiene el binder asociado al propertyPath
- getBindingResult() Obtiene el resultado de la operación de binding (update()) como un BindingResult de Spring Framework.
- getPropertyBinders() Obtiene una colección con todos los binders que contiene.
- autobind() Realiza el binding automático entre el modelo y la vista siguiendo la convención del mismo nombre.
- update() Actualiza los datos del modelo.
- refresh() Actualiza los datos de la vista.
Nota: Aquí, por vista, nos referimos a la clase que contiene los componentes Swing. No es necesario que implemente la interfaz View. Tampoco es necesario crear getters y setters para los componentes.
Puede usar CompositeBinder de forma independiente con sus propios componentes y sin necesidad de utilizar el contenedor IoC de spring:
CompositeBinder<Object> binder = new CompositeBinder<Object>(model); binder.setBinderFactory(new ControlAccessorBinderFactory( ConfigurableControlAccessorFactory.getDefaultFactory()))); binder.autobind(view); binder.update(); BindingResult result = binder.getBindingResult();