You are on page 1of 5

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 interface ObservadorTablero. 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 modeloAjedrez podra implementar (heredar) de una interfaceModeloAjedrez. Las clases de controlador e interface grfica veran a esta interfaceModeloAjedrez, en vez de a modeloAjedrez. 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: cuando Ordenador 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 de JFrame, 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.