Вы находитесь на странице: 1из 7

Separacin modelo vista controlador

Vamos a explicar en qu consiste la separacin modelo-vista-controlador a la hora de


hacer un programa. Aunque en la explicacin se utiliza como ejemplo un juego de ajedrez,
al final se presenta un cdigo en java para un puzzle (ms sencillo que un juego de
ajedrez).
Objetivo
Un problema muy comn para los programadores es la reutilizacin del cdigo que ya
tienen hecho. A veces hay que resolver un problema parecido a algo que ya tenemos
hecho, mejorar el aspecto de un programa, mejorar su algoritmo, etc. Esta tarea se facilita
mucho si a la hora de programar tenemos la precaucin de separar el cdigo en varias
partes que sean susceptibles de ser reutilizadas sin modificaciones.
Qu es el modelo, la vista y el controlador
En casi cualquier programa que hagamos podemos encontrar tres partes bien
diferenciadas
Por un lado tenemos el problema que tratamos de resolver. Este problema suele
ser independiente de cmo queramos que nuestro programa recoga los resultados
o cmo queremos que los presente. Por ejemplo, si queremos hacer un juego de
ajedrez, todas las reglas del ajedrez son totalmente independientes de si vamos a
dibujar el tablero en 3D o plano, con figuras blancas y negras tradicionales o
figuras modernas de robots plateados y monstruos peludos. Este cdigo
constituira el modelo. En el juego del ajedrez el modelo podra ser una clase (o
conjunto de clases) que mantengan un array de 8x8 con las piezas, que permita
mover dichas piezas verificando que los movimientos son legales, que detecte los
jaques, jaque mate, tablas, etc. De hecho, las metodologas orientadas a
objeto nos introducen en este tipo de clases, a las que llaman clases del negocio.
Otra parte clara es la presentacin visual que queramos hacer del juego. En el
ejemplo del ajedrez seran las posibles interfaces grficas mencionados en el
punto anterior. Esta parte del cdigo es la vista. La llamar interface grfica por
ser lo ms comn, pero podra ser de texto, de comunicaciones con otro programa
externo, con la impresora, etc. Aqu tendramos, por ejemplo, la ventana que
dibuja el tablero con las figuras de las piezas, que permiten arrastrar con el ratn
una pieza para moverla, botones, lista visual con los movimientos realizados, etc.
La tercera parte de cdigo es aquel cdigo que toma decisiones, algoritmos, etc.
Es cdigo que no tiene que ver con las ventanas visuales ni con las reglas de
nuestro modelo. Esta parte del cdigo es el controlador. En nuestro cdigo de
ajedrez formaran parte de esto el algoritmo para pensar las jugadas (el ms
complejo de todo el juego).
Dependencias entre modelo, vista y controlador
Si ordenamos estos tres grupos por probabilidad de ser reutilizable, tenemos un resultado
como el siguiente:
Lo ms reutilizable y que es menos susceptible de cambio, es el modelo. Las
reglas del juego de ajedrez no cambian de un da para otro. Si tenemos un
conjunto de clases que mantengan en memoria el tablero y las reglas de
movimiento de las piezas, es posible que esas clases (o funciones y estructuras de
datos) nos sirvan durante mucho tiempo sin necesidad de tocarlas. En un punto
intermedio est el controlador. Es posible que mejoremos con cierta frecuencia
nuestro algoritmo de juego del ajedrez, posiblemente cada vez que saquemos una
nueva versin de nuestro juego.
Finalmente, lo que ms cambia, es la vista. De hecho, un mismo programa de
ajedrez suele darnos posibilidad de varias presentaciones. El modelo y el
controlador seran los mismos, pero habra varias vistas distintas.
Tras este tipo de ordenacin, si queremos reaprovechar cosas en futuros programas de
ajedrez, est claro que el modelo debe ser independiente. Las clases (o funciones y
estructuras) del modelo no deben ver a ninguna clase de las otros grupos. De esta forma
podremos compilar el modelo en una librera independiente que podremos utilizar en
cualquier programa de ajedrez que hagamos. Es ms, suponiendo que hagamos el
programa en C y queramos cambiarnos de plataforma (de linux a windows, por ejemplo),
tenemos bastantes posibilidades de que el cdigo utilizado sea C standard y compile casi
directamente en cualquier plataforma. No tenemos librerias grficas, de sockets ni otras
libreras avanzadas que suelen ser muy distintas, incluso dentro de una misma plataforma
si utilizamos distintos entornos de desarrollo (comparemos por ejemplo, los grficos de
visual c++ con los de borland c++, ambos en PC/windows).
Siguiendo con el orden de posibilidad de reutilizacin, el controlador podra (y suele) ver
clases del modelo, pero no de la vista. Si en el juego del ajedrez el controlador es el que
analiza el tablero y hace los movimientos del ordenador, est claro que el controlador
debe ver el modelo que tiene las piezas y hacer en l los movimientos. Sin embargo, no
debe ver nada de la vista. De esta forma, el cambio de interface grfica no implicar
retocar el algoritmo y recompilarlo, con los consiguientes riesgos de estropearlo adems
del trabajo del retoque.
La vista es lo ms cambiante, as que podemos hacer que vea clases del modelo y del
controlador. Si cambiamos algo del controlador o del modelo, es bastante seguro que
tendremos como mnimo que recompilar la interface grfica.
Tras esto vemos claramente que cuando el jugador mueve una pieza en pantalla, la
interface grfica se entera y hace al modelo que mueva la pieza. El modelo, como retorno
de la funcin o mtodo llamado, puede devolver si el movimiento es vlido o no. Si el
movimiento es vlido, la interface grfica puede decirle al controlador que mueva, para
simular el movimiento del ordenador. Incluso, como veremos ms adelante, el controlador
puede enterarse de que se ha movido una pieza y de que es su turno, sin necesidad de
que le avise la vista.
El siguiente diagrama de secuencia muestra esto ms o menos. Hay que fijarse que las
flechas slo van en sentido de vista a modelo y controlador, y del controlador al modelo.

Comunicacin en sentido inverso
Ahora surge una pregunta. Si el controlador decide hacer un movimiento en el modelo del
ajedrez, cmo se entera la interface grfica para visualizar dicho movimiento en
pantalla?. Debemos tener en cuenta que ni el modelo ni el controlador ven a la vista, por
lo que no pueden llamar a ninguna clase ni mtodo de ella para que se actualize.
Para este tipo de problemas, tenemos otros patrones de diseo, por ejemplo,
el patrn observador. Debemos hacer una interface (en java sera un interface, en C++
sera una clase con todos los mtodos virtuales puros) que tenga mtodos del
estilo tomaMovimiento (Movimiento), tomaJaque (), tomaTablas(), ganan
(ColorGanador), y en general, para cualquier cosa que pueda pasar en el tablero que
pueda tener inters para alguien. Llamemos a esta interfaceObservadorTablero.
Esta interface formara parte del modelo, de forma que las clases del modelo s pueden
verla. La clase del modelo que mantiene el tablero, debe tener una lista de objetos que
implementen esta interface (en C++, clases que hereden de esta clase con mtodos
virtuales). La clase del modelo debe tener adems un par de mtodos del
estilo anhadeObservador
(ObservadorTablero) y eliminaObservador(ObservadorTablero). Estos mtodos
aadiran o borraran el parmetro que se les pasa de la lista y que es un objeto que
implementa la interface.
Tanto el controlador como la vista, deben implementar esta interface (heredar de ella en
C++) y deben llamar al mtodo anhadeObservador(this) del modelo. A partir de este
momento, cada vez que el modelo mueva una pieza, detecte un jaque, etc, debe llamar al
mtodo tomaMovimiento(Movimiento),tomaJaque(), etc de todos
los ObservadorTablero que tenga en su lista.
De esta forma el modelo est avisando a las clases de la vista y del controlador sin
necesidad de verlas (slo ve a la interface). De hecho, el modelo y la interface pueden
estar compiladas en una misma librera y el hacer nuevas clases que implementen la
interface o modificar las que ya la implementan, no es necesario recompilar la librera.
Si hay alguna cosa que pase en el controlador y deba enterarse la interface grfica,
habra que implementar un mecanismo similar. Por ejemplo, el ordenador decide rendirse
y la interface grfica debera mostrar un aviso indicndolo.
Tambin, para aislar an ms las clases, suele ser habitual que el modelo (o incluso el
controlador) implementen (o hereden de) una interface del modelo con los mtodos para
mover piezas y dems, de forma que ni la interface grfica ni el controlador dependen de
un modelo concreto. Por ejemplo, la clase modeloAjedrezpodra implementar (heredar)
de una interfaceModeloAjedrez. Las clases de controlador e interface grfica veran a
esta interfaceModeloAjedrez, en vez de amodeloAjedrez. Las clases de la interface
grfica y del controlador deberan tener mtodos del estilo tomaModelo
(InterfaceModeloAjedrez), con el que se le pasa el modelo concreto que deben tratar.
Juntarlo todo
Para que todo esto funcione, es necesario que haya un programa principal a parte de todo
esto. El programa principal se debe encargar de instanciar las clases concretas del
modelo, controlador y vista que se van a usar y encargarse de llamar a todos los mtodos
del estilo tomaModelo() y anhadeObservador(), es decir, hacer que se vean unas a
otras de la forma adecuada.
Si queremos hacer una interface grfica totalmente nueva, bastar con hacerla de forma
que admita el mismo modelo y controlador que ya tenemos. Luego en el main tocaremos
el new de la interface grfica para que lo haga de la nueva y ya est. Todo debera
funcionar sin tener necesidad siquiera de recompilar el modelo ni el controlador (el
algoritmo para jugar al ajedrez).
El ejemplo
Aqu tienes un ejemplo de un puzzle en java en el que se ha seguido (ms o menos) esta
filosofa de programacin. Puedes verlo funcionando como applet, ver los fuentes e
incluso bajrtelos.
Como modelo est las clase Puzzle, que tiene mtodos para mover las piezas y para
suscribirse a movimientos de piezas y a que el tablero est ordenado. Avisar a clases
que implementen ObservadorMovimiento. He hecho tambin una clase Casilla, pero es
simplemente por hacer una estructura con los campos fila, columna y la comparacin
entre dos estructuras para saber si corresponden a la misma casilla.
Como controlador, est Ordenador, que nicamente saber ordenar y desordenar el
puzzle. Por no complicarme la vida, el algoritmo de ordenacin consiste en apuntar todos
los movimientos que se hacen en el puzzle y realizarlos en orden inverso. Ordenador, por
tanto, se suscribe en Puzzle a los movimientos, para apuntarlos y poder hacerlos luego al
revs. Se suscribe tambin a que el puzzle est ordenado para borrar la lista de
movimientos. Otro detalle ms: cuandoOrdenador est ordenando el puzzle, se
desuscribe de los movimientos, para no ser avisado de sus mismos movimientos de
ordenacin y montar un lio.Cuando termina de ordenar, se vuelve a suscribir.
Como vista, la clase GuiTablero es un lienzo (Canvas) de dibujo en el que se pintan las
piezas (unas imgenes .gif que hay por ah). Los clicks de ratn se interpretan y llaman al
mtodo mueve(fila,columna) de Puzzle. GuiTablero tambin se suscribe a los
movimientos del puzzle, de forma que cuando se realize un movimiento, se entera y
repinta la pantalla. La clase GuiTableroBotones contiene un GuiTablero y dos botones,
uno para ordenar y otro para desordenar. La pulsacin de estos botones llamar a los
mtodos ordena() y desordena() de Ordenador.
Finalmente he hecho dos posibles clases principales. AppletPuzzle hereda de JApplet,
para poder meter el puzzle en una pgina web y mainPuzzle hereda deJFrame, para
poder utilizarlo como aplicacin independiente. Estas clases instancian algunas de las
anteriores y son adems las encargadas de leer los ficheros .gif que hacen de piezas del
puzzle. Pasan las clases Image correspondientes a la vista.
Una observacin
Si te fijas un poco en la API de java, vers que utilizan esta filosofa con frecuencia. Por
ejemplo JList es la vista de una lista, ListModel es la interface del modelo de
lista, DefaultListModel es una posible implementacin de este modelo. JList tiene un
mtodo setModel(ListModel) para pasarle el modelo. ListModel tiene mtodos
de addListDataListener(ListDataListener) y removeListDataListener(ListDataListener
), con lo que se le est pasando a quin tiene que avisar cuando haya cambios en el
modelo de lista. JList tiene una clase interna que implementa ListDataListener, con lo
que a travs de esa clase se enterar de los cambios en el modelo y los refrescar en
pantalla.
Otra observacin ms
Todava no llevo demasiado utilizando el patrn este y lo que he escrito aqu es la primera
idea que me he hecho sobre el tema. Es posible que algunas cosas no sean totalmente
correctas. De hecho, he leido hace un par de das otra explicacin de este patrn, en el
que el controlador controla tanto al modelo como a la vista, de forma que el controlador es
capaz, en un momento dado, de llamar a mtodos de la vista. Por supuesto, tanto modelo,
como controlador y vista implementan interfaces determinadas y slo se ven entre ellos a
travs de esas interfaces.
CLASES

Este ejemplo se utiliza para explicar el patrn modelo-vista-controlador.
Los fuentes son los siguientes:
Modelo Casilla.java, ObservadorMovimiento.java y Puzzle.java
Controlador Ordenador.java
Vista GuiTablero.java y GuiTableroBotones.java
Principal
Como applet para poner en pgina web es AppletPuzzle.java.
Como aplicacin independiente es mainPuzzle.java.
Si quieres descargarlos, en puzzle.zip estn todos, fuentes y compilados, adems de las
imgenes para las piezas y el hueco. Basta que los descomprimas en un directorio y
ejecutes la clase mainPuzzle.class.
Unos cuantos detalles del puzzle:
Lo he hecho para imgenes de 32x32. Se podra arreglar fcilmente el cdigo para
que admitiera imgenes de otras dimensiones, eso s, todas las piezas del mismo
tamao para que encajen.
Est hecho para n filas y m columnas. Basta con instanciar la clase Puzzle con el
tamao deseado y hacer ms imgenes.
No se avisa cuando el puzzle est ordenado (aunque la clase Puzzle si avisa
cuando ocurre esto). No costara mucho hacer un aviso cuando el puzzle est
ordenado.
El algoritmo de ordenar es un pequeo fraude. nicamente anota todos los
movimientos para luego poder hacerlos en sentido inverso.

Вам также может понравиться