May 04

Aprendiendo Orientación a Objetos observando (I).

Como he comentado varias veces, una cosa es hacer programas orientados a objetos haciendo algunas clases, algunas herencias, algo de polimorfismo, encapsulación y demás … y otra muy distinta es hacer programas realmente orientados a objetos aprovechando al 99% la potencia y flexibilidad de la orientación a objetos. Puedes pasarte años programando con clases y no darte cuenta de su verdadera potencia si alguien no te muestra el camino.

Un buen maestro es la API de Java. De hecho, a mí me dio un buen empujón en su día. Basta con fijarse, cuando usas clases estándar de java, cómo están organizadas estas clases y cómo colaboran entre ellas.

Tomemos por ejemplo el JTable. Si nos ponemos a usarlo un poco en serio y miramos las clases java implicadas, vemos que entre ellas hay estas cinco: JTable, TableModel, TableCellRenderer, TableCellEditor y ListSelectionModel. Hay que fijarse en qué hace cada una de ellas y para qué sirve

  • JTable es la parte visual de la tabla. El componente que vemos.
  • TableModel contiene los datos que se visualizan en la tabla. Es lo que se llama un modelo de datos. Tiene métodos addTableModelListener() con los que cualquiera puede "suscribirse" para enterarse automáticamente de cuando cambian esos datos. El JTable es uno de los suscriptores, aunque repito, cualquiera puede suscribirse.
  • TableCellRenderer. Esta es la clase que sabe cómo se pinta un dato. Cuando el JTable recoge un dato del TableModel, le pregunta al TableCellRenderer cómo pintarlo en la celda correspondiente.
  • TableCellEditor. Esta es la clase que sabe cómo se edita y modifica un dato. Cuando el usuario quiere editar una celda -por ejemplo, haciendo doble click- el JTable le pregunta al TableCellEditor qué debe pintar en esa celda para editar el dato -puede ser un JTextField, un JComboBox o cualquier cosa que queramos-.
  • ListSelectionModel. Esta es la clase que sabe qué filas están seleccionadas en el JTable. Tiene métodos addListSelectionListener() para que cualquiera pueda "suscribirse" y enterarse de cambios en esta selección.

Bien, ¿qué podemos aprender de todo esto?. Pues a poco que nos fijemos, un montón de cosas.

Por ejemplo, si nosotros supiéramos un lenguaje orientado a objetos y quisiéramos hacer un componente similar al JTable, posiblemente no pensaramos en hacer tantas clases. Quizás haríamos una sola -el JTable- o si somos espabilados, dos -el JTable y el TableModel-. ¿Por qué hace java tantas?

Una primera razón es que en un buen diseño orientado a objetos, cada clase debe tener una única responsabilidad clara. Saber dibujar la tabla, contener los datos, saber dibujar un dato, saber editar un dato y saber qué filas están seleccionadas, son responsabilidades distintas y pueden, por tanto, ponerse en clases distintas.

Separar tanto permite reutilizar mejor. Por ejemplo, el ListSelectionModel es el mismo para un JList y para un JTable. El ListCellRenderer es común para un JList y un JComboBox, etc. Ese código no tendríamos que repetirlo varias veces, dentro del JList y del JTable, dentro del JList y del JComboBox.

Otro razón es la previsión de cosas que pueden querer cambiarse. No sabemos qué tipos de datos puede querer pintar un programador en un JTable, ni cómo quiere pintarlos, así que le damos la opción de poder decidir cómo se dibujan esos datos, cómo editarlos e incluso cómo almacenarlos. Si te fijas, las clases que mencioné anteriormente son casi todas interfaces -excepto el JTable-. Java proporciona clases por defecto que implementan esas interfaces, como DefaultTableModel, DefaultTableCellRenderer, DefaultListSelectionModel, .. pero nosotros podemos hacer nuestras propias clases implementando las interfaces correspondientes y cambiarlas en el JTable. En definitiva, todo lo que se prevé que puede ser modificable en el JTable, no lo implementa el JTable, sino que el JTable le pregunta a una interface que el programador pueda implementar. Esto -conocido como patrón estrategia- hace que el JTable sea muy configurable.

Otra cosa más que podemos aprender es el motivo de los métodos addTableModelListener() y addListSelectionListener() que mencionamos antes. Al hacerlo así, el TableModel no tiene que avisar directamente al JTable cuando cambien los datos, sino que es el JTable el que debe suscribirse a los cambios. De esta manera, TableModel no sabe ni conoce ni tiene por qué conocer ni depender de un JTable. Este mecanismo -conocido como patrón observador- hace que el TableModel podamos llevarlo y utilizarlo donde queramos, sin necesidad de llevar a rastras un JTable. Es más, podemos, por ejemplo, usar un mismo TableModel en dos JTable distintos y como modelo de datos de un gráfico que nos hagamos. Cualquier cambio en los datos se reflejará automáticamente en los dos JTable y en el gráfico.

Yendo un poco más allá, vemos aquí los principios del patrón modelo-vista-controlador. Aunque no está el controlador, vemos claramente separados el modelo -TableModel- de la vista -JTable-.

Si observamos un poco más la API de java, vemos que este patrón de clases se repite en casi todos los componentes java. Así, el JList tiene un ListModel, un  ListCellRenderer … y el mismo ListSelectionModel que el JTable. Con los JTree pasa lo mismo. En los JTextField, JTextArea, … el modelo es un Document. El JComboBox tiene un ComboBoxModel …. y el mismo ListCellRenderer que los JList.

continuará…

Entradas relacionadas:

  • No hay entradas relacionadas.

9 Responses to “Aprendiendo Orientación a Objetos observando (I).”

  1. Jorge Says:

    Muy interesante y valida tu observación, yo recien estoy en esto de los patrones y pues aún ando un poco confundido pero leyendote pues se me van despejando dudas.

    Ahora la dude que me surge es en la ultima parte donde mencionas el patron MVC pero no indicas cual es la clase o interfaz que representa al controlador ¿existe o esta fusionada con la vista?

  2. Jorge Says:

    Perdon me olvide de comentar algo mas!! (se me vino a la cabeza luego del ultimo posteo).

    Me tiene confundido los conceptos que lei respecto a las funciones de la clase(s) controlador, pues indican de que esta o estas son las encargadas de comunicar a la vista y al modelo y bueno en consecuencia gestionan los eventos de la vista.

  3. Chuidiang Says:

    Hola:

    El controlador es el que actúa sobre el modelo y a veces sobre la vista. Me explico.

    Cuando sobre la vista ocurre un evento de ratón o teclado -un click en un botón, por ejemplo-, la vista debería avisar al controlador de este evento y el controlador es el que tiene que hacer lo que sea -mostrar otra ventana, cambiar datos en el modelo, etc-. Un ejemplo, en el caso de java, los ActionListener que añades al botón serían los controladores.

    En este caso de la tabla no los menciono porque entre las clases mencionadas no hay ningún controlador. Sin embargo, internamente si debería haberlos, aunque no se ven. Por ejemplo, si haces doble click en una celda para editar, un controlador interno que tenga la tabla sería el encargado de hacer que la celda “cambie” para mostrar un editor. Cuando termines de editar -pulsando por ejemplo- un controlador sería el encargado de recoger ese dato del editor y meterlo en el modelo. De todas formas, ya te digo, esos supuestos controladores son internos al JTable y ni siquiera se si realmente están ahí.

    En resumen, el modelo son los datos y no deben ver a nadie, únicamente implementar un patrón observador.

    La vista es la ventana y debería ser tonta. Unicamente pedirle datos al modelo para visualizarlos, suscribirse a cambios en el modelo para visualizarlos e implementar algún tipo de observador para eventos de teclado y ratón.

    El controlador es el que se suscribe a los eventos de teclado y ratón en la vista y el que actúa tanto sobre la vista -para mostrar u ocultar ventanas, habilitar botones, etc- y sobre el modelo -cambiar datos-.

    Se bueno.

  4. Jorge Says:

    Hola:

    Muchas gracias por la aclaración. Ahora una pregunta mas si no molestarte mucho pero me surge una duda en definir ¿Cúal es la diferencia entre 3 capas y MVC? ¿Cuándo aplicamos cada uno?.

    Y otra duda en un ejemplo sobre el patrón MVC(el juego del ajedrez) tu dices de que el controlador es el encargado del algoritmo del juego(Inteligencia Artificial) pero en la respuesta que me diste acá me dices de que en realidad es el encargado de gestionar los eventos ¿Pueden existir variaciones?

  5. Chuidiang Says:

    Hola de nuevo:

    Cuando escribí lo del ajedrez no tenía muy claro el patrón MVC -tengo que corregirlo un día-. De todas formas, esto de los patrones tampoco debería -y es sólo mi opinión- tomarse a rajatabla. Un patrón es una solución que ha demostrado ser eficiente al resolver un problema de diseño, pero quizás no se ajuste exactamente a nuestro problema, por lo que podemos tomarnos la libertad de modificar el patrón un poco según nuestras necesidades.

    El algoritmo de ajedrez sí podría entrar como parte del controlador, puesto que no forma parte del modelo de datos -el tablero- ni de las reglas del ajedrez. Tampoco forma parte de la vista, pero actúa sobre el modelo. Sin embargo, también podríamos suponerlo como algo externo a nuestro sistema, igual que el jugador, y no formaría para nada parte del MVC. Lo importante sería conseguir un juego de ajedrez en el que podamos reaprovechar lo máximo posible para otro futuro juego que hagamos. El modelo del tablero y las reglas de ajedrez debería quedar hechas de una vez para siempre, ya que el tablero siempre es de 8×8, las reglas son fijas y el número de piezas también, no cambian de un día para otro. La interface de usuario debería poder cambiarse con facilidad y el algoritmo también.

    En cuanto a tres capas, depende de en que nivel apliques el patrón. Puedes aplicarlo a tres clases tontas -un JButton, un ActionListener y una clase a la que se modifique un atributo cuando se apriete el botón- o puedes aplicarlo a la totalidad de la aplicación en conjunto. En este segundo caso iría más lo de las tres capas. La primera capa sería el modelo de datos y serían muchas clases que conforman el modelo de datos. Idem para la vista y para el controlador.

    En J2EE, por ejemplo, siempre se ha hablado de que está organizado según la estructura de 3 capas o MVC. ¿Por qué?. Porque J2EE contempla la base de datos y los EJBs de entidad -unas clases- como parte del modelo. Sólo contienen datos. Los Servlets serían el controlador -clases que atienden las peticiones y actúan sobre el modelo-. Mientras que las páginas JSP harían de vista. Está organizado en tres capas porque te proporciona tres tipos de “herramientas” -EJBs, Servlet y JSP- que puedes usar y cada una está muy orientada a una de estas funciones MVC.

    Aunque es del otro artículo -el del patrón decorador, el II- simplemente comentarte que lo que hay en java sobre los InputStream no es estrictamente un patrón decorador. En este patrón, todas las capas de cebolla deberían implementar exactamente el mismo método. Cada una llamaría al método de la capa interior, haría algo adicional con los resultados y lo devolvería hacia arriba. Pero, como te comento, es mejor ser un poco flexible y aprovechar la filosofía del patrón decorador -hacer capas de cebolla- aunque hagas “la vista gorda” con redefinir exactamente el mismo método en todas las capas.

    Se bueno.

  6. Aprender Programación Orientada a Objetos Says:

    [...] aprender Programación Orientada a Objetos y aplicar patrones de diseño mirando el API de Java(1, 2 y [...]

  7. Guillermo Says:

    podrian publicar un ejemplo de programacion en 3 capas para analizarlo?… muchas personas me han dicho cosas distintas sobre la estructura de la programacion a 3 capas, pero necesito ver un ejemplo funcionando para aclarar mis dudas… un ejemplo pequeño no mas.

    se los agradeceria.

    adios

  8. Chuidiang Says:

    Hola:

    No es correcto al 100%, pero aquí tienes un ejemplo que quizás te permita aclarar dudas.

    http://www.chuidiang.com/ood/patrones/modelo_vista_controlador.php

    Se bueno.

  9. Javier Says:

    Hola.

    Solamente quería felicitarte por este magnífico blog, en el q creo somos muchos los q hemos resuelto bastantes dudas y aprendido bastante en un lenguaje muy claro.

    Te animo a seguir así, salu2!

Leave a Reply