Observador y Observable

Aquí está el problema: está diseñando un programa que renderizará datos que describen una escena tridimensional en dos dimensiones. El programa debe ser modular y debe permitir múltiples vistas simultáneas de la misma escena. Cada vista debe poder mostrar la escena desde un punto de vista diferente, bajo diferentes condiciones de iluminación. Más importante aún, si alguna parte de la escena subyacente cambia, las vistas deben actualizarse.

Ninguno de estos requisitos presenta un desafío de programación insuperable. Sin embargo, si el código que maneja cada requisito tuviera que escribirse de novo , agregaría un trabajo significativo al esfuerzo general. Afortunadamente, la biblioteca de clases de Java ya proporciona soporte para estas tareas en forma de interfaz Observery clase, Observableambas inspiradas, en parte, por los requisitos de la arquitectura MVC.

La arquitectura Modelo / Vista / Controlador (MVC)

La arquitectura Modelo / Vista / Controlador se introdujo como parte de Smalltalk, un popular lenguaje de programación orientado a objetos inventado por Alan Kay. MVC fue diseñado para reducir el esfuerzo de programación requerido para construir sistemas haciendo uso de múltiples presentaciones sincronizadas de los mismos datos. Sus características centrales son que el modelo, los controladores y las vistas se tratan como entidades separadas, y que los cambios realizados en el modelo deben reflejarse automáticamente en cada una de las vistas.

Además del ejemplo de programa descrito en el párrafo inicial anterior, la arquitectura Modelo / Vista / Controlador puede usarse para proyectos como los siguientes:

  • Un paquete de gráficos que contiene vistas simultáneas de gráfico de barras, gráfico de líneas y gráfico circular de los mismos datos.
  • Un sistema CAD, en el que partes del diseño se pueden ver con diferentes aumentos, en diferentes ventanas y en diferentes escalas.

La Figura 1 ilustra la arquitectura MVC en su forma más general. Hay un modelo. Varios controladores manipulan el modelo; múltiples vistas muestran los datos en el modelo y cambian a medida que cambia el estado del modelo.

Figura 1. Arquitectura modelo / vista / controlador

Beneficios de MVC

La arquitectura Modelo / Vista / Controlador tiene varios beneficios:

  • Existe una separación claramente definida entre los componentes de un programa: los problemas en cada dominio se pueden resolver de forma independiente.
  • Hay una API bien definida: cualquier cosa que use la API correctamente puede reemplazar el modelo, la vista o el controlador.
  • El enlace entre el modelo y la vista es dinámico: se produce en tiempo de ejecución, en lugar de en tiempo de compilación.

Al incorporar la arquitectura MVC en un diseño, las partes de un programa pueden diseñarse por separado (y diseñarse para hacer bien su trabajo) y luego unirse en tiempo de ejecución. Si posteriormente se considera que un componente no es adecuado, se puede reemplazar sin afectar a las otras piezas. Compare ese escenario con el enfoque monolítico típico de muchos programas Java rápidos y sucios. A menudo, un marco contiene todo el estado, maneja todos los eventos, hace todos los cálculos y muestra el resultado. Por lo tanto, en todos los sistemas, excepto en el más simple, realizar cambios después de los hechos no es trivial.

Definiendo las partes

El modelo es el objeto que representa los datos en el programa. Gestiona los datos y realiza todas las transformaciones en esos datos. El modelo no tiene conocimiento específico ni de sus controladores ni de sus puntos de vista; no contiene referencias internas a ninguno de los dos. Más bien, el sistema mismo asume la responsabilidad de mantener los vínculos entre el modelo y sus vistas y de notificar a las vistas cuando cambia el modelo.

La vista es el objeto que gestiona la visualización de los datos representados por el modelo. Produce la representación visual del objeto modelo y muestra los datos al usuario. Interactúa con el modelo a través de una referencia al propio objeto del modelo.

El controlador es el objeto que proporciona los medios para la interacción del usuario con los datos representados por el modelo. Proporciona los medios por los que se realizan los cambios, ya sea en la información del modelo o en la apariencia de la vista. Interactúa con el modelo a través de una referencia al propio objeto del modelo.

En este punto, un ejemplo concreto podría resultar útil. Considere como ejemplo el sistema descrito en la introducción.

Figura 2. Sistema de visualización tridimensional

La pieza central del sistema es el modelo de la escena tridimensional. El modelo es una descripción matemática de los vértices y las caras que componen la escena. Los datos que describen cada vértice o cara se pueden modificar (quizás como resultado de la entrada del usuario o una distorsión de la escena o un algoritmo de transformación). Sin embargo, no existe una noción de punto de vista, método de visualización (estructura alámbrica o sólida), perspectiva o fuente de luz. El modelo es una pura representación de los elementos que componen la escena.

La parte del programa que transforma los datos del modelo en una pantalla gráfica es la vista. La vista encarna la visualización real de la escena. Es la representación gráfica de la escena desde un punto de vista particular, bajo condiciones de iluminación particulares.

El controlador sabe lo que se puede hacer con el modelo e implementa la interfaz de usuario que permite que se inicie esa acción. En este ejemplo, un panel de control de entrada de datos podría permitir al usuario agregar, modificar o eliminar vértices y caras.

Observador y Observable

El lenguaje Java admite la arquitectura MVC con dos clases:

  • Observer: Cualquier objeto que desee ser notificado cuando cambia el estado de otro objeto.
  • Observable: Cualquier objeto cuyo estado pueda ser de interés y en el que otro objeto pueda registrar un interés.

Estas dos clases se pueden utilizar para implementar mucho más que la arquitectura MVC. Son adecuados para cualquier sistema en el que los objetos deban ser notificados automáticamente de los cambios que se producen en otros objetos.

Normalmente, el modelo es un subtipo de Observabley la vista es un subtipo de Observer. Estas dos clases manejan la función de notificación automática de MVC. Proporcionan el mecanismo mediante el cual las vistas pueden ser notificadas automáticamente de cambios en el modelo. Las referencias de objetos al modelo tanto en el controlador como en la vista permiten el acceso a los datos del modelo.

Funciones de observador y observable

Los siguientes son listados de código para el observador y las funciones observables:

Observador

  • public void update(Observable obs, Object obj)

    Se llama cuando se ha producido un cambio en el estado de lo observable.

Observable

  • public void addObserver(Observer obs)

    Agrega un observador a la lista interna de observadores.

  • public void deleteObserver(Observer obs)

    Elimina un observador de la lista interna de observadores.

  • public void deleteObservers()

    Elimina todos los observadores de la lista interna de observadores.

  • public int countObservers()

    Devuelve el número de observadores en la lista interna de observadores.

  • protected void setChanged()

    Sets the internal flag that indicates this observable has changed state.

  • protected void clearChanged()

    Clears the internal flag that indicates this observable has changed state.

  • public boolean hasChanged()

    Returns the boolean value true if this observable has changed state.

  • public void notifyObservers()

    Checks the internal flag to see if the observable has changed state and notifies all observers.

  • public void notifyObservers(Object obj)

    Checks the internal flag to see if the observable has changed state and notifies all observers. Passes the object specified in the parameter list to the notify() method of the observer.

Next we'll take a look at how to create a new Observable and Observer class, and how to tie the two together.

Extend an observable

A new class of observable objects is created by extending class Observable. Because class Observable already implements all of the methods necessary to provide the desired behavior, the derived class need only provide some mechanism for adjusting and accessing the internal state of the observable object.

In the ObservableValue listing below, the internal state of the model is captured by the integer n. This value is accessed (and, more importantly, modified) only through public accessors. If the value is changed, the observable object invokes its own setChanged() method to indicate that the state of the model has changed. It then invokes its own notifyObservers() method in order to update all of the registered observers.

Listing 1. ObservableValue

 import java.util.Observable; public class ObservableValue extends Observable { private int n = 0; public ObservableValue(int n) { this.n = n; } public void setValue(int n) { this.n = n; setChanged(); notifyObservers(); } public int getValue() { return n; } } 

Implement an observer

A new class of objects that observe the changes in state of another object is created by implementing the Observer interface. The Observer interface requires that an update() method be provided in the new class. The update() method is called whenever the observable changes state and announces this fact by calling its notifyObservers() method. The observer should then interrogate the observable object to determine its new state, and, in the case of the MVC architecture, adjust its view appropriately.

In the following TextObserver listing, the notify() method first checks to ensure that the observable that has announced an update is the observable that this observer is observing. If it is, it then reads the observable's state, and prints the new value.

Listing 2. TextObserver

 import java.util.Observer; import java.util.Observable; public class TextObserver implements Observer { private ObservableValue ov = null; public TextObserver(ObservableValue ov) { this.ov = ov; } public void update(Observable obs, Object obj) { if (obs == ov) { System.out.println(ov.getValue()); } } } 

Tie the two together

A program notifies an observable object that an observer wishes to be notified about changes in its state by calling the observable object's addObserver() method. The addObserver() method adds the observer to the internal list of observers that should be notified if the state of the observable changes.

The example below, showing class Main, demonstrates how to use the addObserver() method to add an instance of the TextObserver class (Listing 2) to the observable list maintained by the ObservableValue class (Listing 1).

Listing 3. addObserver()

 public class Main { public Main() { ObservableValue ov = new ObservableValue(0); TextObserver to = new TextObserver(ov); ov.addObserver(to); } public static void main(String [] args) { Main m = new Main(); } } 

How it all works together

The following sequence of events describes how the interaction between an observable and an observer typically occurs within a program.

  1. First the user manipulates a user interface component representing a controller. The controller makes a change to the model via a public accessor method -- which is setValue() in the example above.
  2. The public accessor method modifies the private data, adjusts the internal state of the model, and calls its setChanged() method to indicate that its state has changed. It then calls notifyObservers() to notify the observers that it has changed. The call to notifyObservers() could also be performed elsewhere, such as in an update loop running in another thread.
  3. The update() methods on each of the observers are called, indicating that a change in state has occurred. The observers access the model's data via the model's public accessor methods and update their respective views.

Observer/Observable in an MVC architecture

Now let's consider an example demonstrating how observables and observers typically work together in an MVC architecture. Like the model in the ObservableValue (Listing 1) the model in this example is very simple. Its internal state consists of a single integer value. The state is manipulated exclusively via accessor methods like those in ObservableValue. The code for the model is found here.

Initially, a simple text view/controller class was written. The class combines the features of both a view (it textually displays the value of the current state of the model) and a controller (it allows the user to enter a new value for the state of the model). The code is found here.

By designing the system using the MVC architecture (rather than embedding the code for the model, the view, and the text controller in one monolithic class), the system is easily redesigned to handle another view and another controller. In this case, a slider view/controller class was written. The position of the slider represents the value of the current state of the model and can be adjusted by the user to set a new value for the state of the model. The code is found here.

About the author

Todd Sundsted has been writing programs since computers became available in desktop models. Though originally interested in building distributed object applications in C++, Todd moved to the Java programming language when Java became the obvious choice for that sort of thing.

This story, "Observer and Observable" was originally published by JavaWorld .