MODEL/VIEW

Estas dos clases implementan el patrón de diseño "Observer". El objetivo de este patrón es establecer un canal entre un objeto (el "model"), y un grupo de objetos (las "views") de forma tal que cuando cambia el estado del model, todas las views son notificadas y actualizadas.

Un uso frecuente de este patrón (aunque no el único), es para tener representaciones gráficas (posiblemente más de una) de un objeto con datos. Cuando los datos cambian, todas las representaciones son informadas automáticamente. Esto tiene como ventaja:

Primary model

En el caso más simple, existe una relación uno a muchos entre el modelo y sus views (es decir, cada view es view de solo un modelo, pero un modelo puede tener muchos views). En este caso, se llamará modelo primario de una view a su único módelo (Se le pone ese nombre para distinguir de las situaciones con una relación muchos a muchos, descripta más adelante).

Supongamos que se desea usar como modelo instancias de la clase CONCRETE_MODEL. Como vistas se usaran instancias de VIEW_A y VIEW_B (puede haber views distintas). Entonces:

Una view puede cambiar de modelo repetidas veces llamando a `set_model' con distintos modelos. También se puede usar Void como parámetro, en cuyo caso la vista queda desligada de un modelo primario.

Secondary Models

En algunos casos no se da una relación uno a uno, sino uno a muchos. (No confundir esto con el caso de que el modelo es compuesto, que se explica más abajo).

En este caso, también pueden usarse las clases MODEL/VIEW, con las siguientes desventajas:

El modelo accesible a través de model (y fijado con set_model, en la forma explicada anteriormente) se sigue denominando "modelo primario". Los otros modelos se llaman "modelos secundarios".

Por ejemplo, si existen modelos de tipo MODEL_A, MODEL_B, y se desea una view CONCRETE_VIEW con un MODEL_A como modelo primario, otro MODEL_A como modelo secundario, y un MODEL_B como otro modelo secundario. Las diferencias con el caso anterior son:

Nótese que todos estos son cambios en la vista, los modelos se siguen diseñando igual.

Una view puede desligarse de un modelo secundario x, llamando a x.remove_view (Current)

Una view puede tener modelo primario, modelos secundarios, ambos, o ninguno.

Cadenas de model/view

A veces la clase que se usa como modelo es un contenedor con referencias a otros objetos, y tiene que ser capaz de notificar sobre cambios no solo en su estado sino en el estado de los objetos que contiene. Por ejemplo podemos tener:

class MODEL_A

inherit
    MODEL
...

feature

    items: ARRAY [MODEL_B]

...

Y queremos que las instancias de MODEL_A notifiquen a sus views cuando los elementos de items cambian su estado. Para ello, el esquema que se usa puede ser el mismo MODEL/VIEW, pero entre MODEL_A y MODEL_B. Es decir, definimos MODEL_A de la siguiente forma.

class MODEL_A

inherit
    MODEL
    VIEW [MODEL_B]

feature

    items: ARRAY [MODEL_B]

    add_item (new_item: MODEL_B) is
    do
        items.add_last (new_item)
        new_item.add_view (Current)
        notify_views
    end

feature

    on_model_change is
    do
        notify_views
    end

...

Puede verse que ahora los elementos de items son modelos secundarios de MODEL_A. Para un contenedor múltiple esto es más directo, en caso de un contenedor de un elemento podría usarse un modelo primario, o ambas formas.

on_model_change es llamado cuando algun elemento dentro de items cambia su estado, y MODEL_A propaga el cambio llamando a notify_views. notify_views También se sigue usando en los features que cambian el estado de un MODEL_A (por ejemplo, add_item).

Estas cadenas de VIEW - MODEL+VIEW - MODEL+VIEW - ... - MODEL pueden tener uno o muchos pasos, siguiendo el mismo esquema.

Protocolos de notificación

Un problema de usar una clase general para el patrón MODEL/VIEW es que el protocolo de notificación es muy elemental, solo se indica que el modelo cambio, aunque a veces sería util saber que aspecto o en que forma.

Para resolver esto, pueden darsele al modelo queries que permitan saber que fue modificado. Esto usualmente es un conjunto de flags booleanos con nombre changed_xxx (uno por cada aspecto), o algo más complejo para adaptarse a casos menos convencionales.

Cuando se usan estos flags, usualmente se inicializan a False (su valor por defecto) al crear el modelo, y las features que modifican aspectos del modelo los cambian a True antes de llamar a notify_view. Conviene redefinir notify_views al heredar MODEL de esta forma:

notify_views is
do
    Precursor
    changed_aspect1 := False
    changed_aspect2 := False
    -- ... repeat for all aspects
    -- May be more complicated for other protocols
    changed_aspectn := False
end