BUENAS PRÁCTICAS EN J2EE. SEGUNDA PARTE. VERSION PARA IMPRIMIR (Primera parte en http://espanol.aurumsol.com/articulos/art6/art6-1.html Versión original en español en http://espanol.aurumsol.com/articulos/art7/art7-1.html Enlace a la versión original en inglés) Informe de The Middleware Company Traducido y abreviado con permiso por Dr. Vicent-Ramon Palasí Lallana. Gerente General de Aurum Solutions. http://www.aurumsol.com Diciembre 2003 5. DESPLIEGUE El despliegue es una de las etapas finales del desarrollo de software. En ella, se copian todos los archivos que se necesitan para ejecutar una aplicación del entorno de desarrollo al de producción. El resultado de la etapa de despliegue es una aplicación ejecutable ubicada en un entorno de producción. 5.1. Buena práctica número 12. Siga la especificación estándar J2EE para empaquetado La especificación J2EE describe los artefactos necesarios para el proceso de despliegue y su ubicación en el servidor, permitiendo así la portabilidad de las aplicaciones y sus componentes. A continuación, se muestra una lista de módulos que incluye la especificación: Una aplicación J2EE se empaqueta en un archivo EAR (Enterprise Archive), que es un archivo JAR estándar con una extensión .ear. Para crear un archivo EAR, se combinan módulos EJB, Web y de aplicación con los descriptores de despliegue adecuados para cada uno de ellos. Un módulo EJB es una colección de Enterprise Javabeans, empaquetados conjuntamente en un archivo .jar. Este archivo contiene todas las clases necesarias para los Enterprise Javabeans, incluyendo clases de soporte y un descriptor de despliegue EJB (llamado “ejb-jar.xml”). Un módulo Web se empaqueta en un archivo JAR que tiene una extensión .war. Este archivo está compuesto de servlets, JSPs, applets, clases de soporte, documentos estáticos (HTML, imágenes, sonidos, etc) y un descriptor de despliegue Web (llamado “web.xml”). Un módulo de aplicación es un archivo JAR estándar que contiene tanto clases Java como un descriptor de despliegue de aplicación cliente (llamado application-client.xml). Los descriptores de despliegue son archivos de configuración XML que contienen toda la información necesaria para desplegar los componentes de un módulo así como las instrucciones para combinar los diversos componentes dentro de una aplicación. Dada la complejidad del empaquetado, se debería aplicar una herramienta de verificación J2EE, como la que proporciona el SDK J2EE, sobre el archivo EAR resultante antes del despliegue. Una herramienta de verificación comprueba que el contenido del despliegue esté bien formado y que sea consistente con las especificaciones de EJB, servlets y J2EE. 5.2. Buena práctica número 13. Utilice herramientas que ayuden en el despliegue A pesar de la excelente documentación que explica el proceso de despliegue, éste puede llegar a ser agotador, complejo y difícil de depurar. Afortunadamente, hay una variedad de herramientas que lo facilitan. Aunque la mayoría de entornos de desarrollo (IDEs) proporcionan un soporte adecuado para el despliegue, no se debería depender de estas tecnologías propietarias. Por el contrario, es preferible crear un guión que permita el despliegue sobre cualquier plataforma, aunque ésta no soporte el IDE. Para ello, Ant es la herramienta usada por la mayoría de los mejores programadores, ya que ofrece una serie de ventajas para desplegar aplicaciones J2EE. Usando Ant, se pueden desplegar aplicaciones de forma tan sencilla como ejecutar un comando. Ant tiene tareas incorporadas para crear archivos JAR, WAR y EAR. Además, puede automatizar también la copia de los archivos a su ubicación correcta en el servidor J2EE. La diferencia más importante entre un IDE y un guión (como los de Ant) para despliegues de gran tamaño es que el último se puede repetir fácilmente. Además, como un guión no requiere intervención manual, es una opción mucho más sólida para desplegar en un entorno de producción. 5.3. Buena práctica número 14. Respalde sus datos y su entorno de producción Debería tener precaución extrema cuando trabaje con máquinas de producción. Hay pocas cosas que causen un daño mayor que hacer cambios a un entorno de producción. Se puede evitar mucho sufrimiento si se hacen copias de respaldo. Planifique respaldar sus entornos de producción y de desarrollo. Es fácil de hacer y es lo que se debe hacer. Asegúrese de que sus copias de respaldo incluyan archivos de aplicación, de configuración y de datos. Respalde con regularidad y también antes de cada despliegue. Así, en caso de problemas, podrá recuperar fácilmente el último estado bueno. 6. AFINACIÓN (TUNING) Quizás la etapa más descuidada en la mayoría de los proyectos de desarrollo es la de afinación. Esto es sorprendente, pues una de las causas más comunes de fracaso en grandes proyectos de software es un pobre rendimiento. Aunque muchos programadores llevan a cabo algo de afinación no estructurada, muy pocos construyen y ejecutan un plan de rendimiento que funcione. Aunque parte del problema se debe a la falta de buenas herramientas de afinación, esta situación está cambiando. Algunos vendedores están comenzando a incluir buenos servicios de perfilado en Java así como ayudas para la resolución de problemas. 6.1. Buena práctica número 15. Construya un plan de rendimiento Es necesario comenzar con un plan sólido de rendimiento. A continuación se detallan los componentes clave de dicho plan: Criterios. Se debe asegurar que se capturan todos los criterios importantes de rendimiento. Se debería ser lo más específico posible sobre cargas, tiempos y configuraciones. Interesados. Se debería determinar las personas que deben aprobar el plan y las que deben aprobar el producto acabado. Se debería nombrar a un director del plan para que alguien sea responsable del rendimiento de la aplicación. Mitigación de riesgos. El plan de rendimiento es un buen lugar para comenzar a planificar cómo se mitigan los riesgos cuando algo sale mal. Cronograma. Se necesita reservar tiempo suficiente para implementar el plan de rendimiento. Se debería reservar un poco de tiempo durante cada iteración de diseño para solucionar los cuellos de botella asociados a cada caso de uso. También se debería reservar un periodo de tiempo al final del ciclo de desarrollo, normalmente combinándolo con las pruebas. Herramientas. Se debería definir qué herramientas se usarán para analizar y afinar el rendimiento. Estas herramientas ayudarán a simular cargas, perfilar el rendimiento de la aplicación, analizar los resultados y afinar servidores de software individuales como, por ejemplo, motores de bases de datos. Entornos. Si se necesitan simular cargas grandes, se necesitará una red extensa para hacerlo y un software para ejecutarlo. Otra opción es alquilar a consultores tiempo en este tipo de entornos. Personal. Necesita asegurarse de que cuenta con los recursos humanos necesarios para el plan, desde personal de programación a administradores de bases de datos y especialistas en hardware. Casos claves de prueba. Se deberían construir los casos críticos de prueba por adelantado. Después de que se ha construido el plan de rendimiento, éste debe ser aprobado por los interesados, se debe asegurar que se contará con los recursos y se debe ejecutar el plan. Normalmente las metodologías iterativas incluyen etapas que enfatizan el diseño de una solución, su construcción, pruebas y despliegue. Un análisis de rendimiento sólido se extiende por todas estas etapas. Resumiendo, los problemas tardíos de rendimiento han hecho fracasar muchos proyectos, pero si se cuenta con un plan sólido, se tendrá más probabilidades de salir ileso. La planificación de rendimiento es costosa pero las alternativas pueden ser catastróficas. 6.2. Buena práctica número 16. Gestione las fugas de memoria En este documento, una fuga de memoria (“memory leak”) es aquella memoria que la aplicación ya no necesita, pero que el recolector de basura (“garbage collector”) no libera. En sentido estricto, esto no es realmente una fuga: es simplemente que el programador no da la información suficiente al recolector de basura para que haga su trabajo. La recolección de basura en Java funciona identificando objetos que son alcanzables por la máquina virtual de Java, como se muestra en la figura 2. De forma periódica (por ejemplo, cuando la cantidad usada de memoria sobrepasa un cierto límite), la máquina virtual invoca al recolector de basura (GC). En la ejecución del programa, algunos objetos referencian a otros, creando un grafo dirigido. El recolector de basura examina todos los objetos en memoria y ve si son alcanzables desde el objeto raíz, es decir, si hay un camino en el grafo desde ese objeto raíz a cada objeto. Con esta información, el recolector de basura marca los objetos como alcanzables o no y libera todos los objetos que no son alcanzables. Figura 2. El recolector de basura de Java funciona determinando los objetos que son alcanzables. Una flecha indica una referencia de un objeto a otro. Consecuentemente, si se ha dejado una referencia a un objeto en algún lugar, el objeto no puede ser liberado, pues puede ser alcanzado de alguna forma. Las fugas de memoria pueden ocurrir de muchas formas diferentes, pero sus causas son solamente unas pocas: Limpieza descuidada de referencias. Puede ocurrir una fuga de memoria cuando un objeto primario crea otro objeto y, entonces, varios objetos adicionales crean referencias a este último. Si no se “limpian” estas referencias cuando se libera el objeto primario, entonces se tendrá una fuga de memoria. Diferentes ciclos de vida. Cuando un objeto con un ciclo de vida largo gestiona otro con un ciclo de vida corto, hay potencial para una fuga de memoria. Mal manejo de excepciones. Algunas veces, las excepciones pueden evitar el código de limpieza. Por esta razón, debería colocar este código en los bloques `finally`. La mayoría de fugas de memoria en Java están comprendidas en una de las tres categorías anteriores. Estudiemos algunos casos especiales de fugas: La colección con fuga. Este tipo de fuga de memoria sucede cuando se coloca un objeto en una colección y nunca se elimina de la misma. Con una caché, esto sucede cuando nunca se eliminan datos viejos de la misma. El patrón de diseño “Publish-Subscribe” permite que un objeto que declara un evento publique una interfaz y los suscriptores reciban notificación cuando el evento ocurre. Cada vez que un objeto se suscribe a un evento, es colocado en una colección. Si desea que el recolector de basura libere el objeto suscriptor, deberá cancelar la suscripción cuando haya acabado con ella. Singleton. Este tipo de fuga de memoria ocurre cuando se tiene un singleton con un ciclo de vida largo que referencia un objeto con un ciclo de vida corto. Si no se pone la referencia a `null`, entonces el recolector de basura nunca liberará el objeto de vida corta. La función costosa que apenas se usa. Muchas aplicaciones tienen funciones, como Imprimir, que pueden ser extremadamente costosas pero que son usadas en pocas ocasiones. Si se referencia estas funciones desde objetos con ciclos de vida largos, es mejor limpiar explícitamente las referencias entre los usos. El estado de sesión. Cuando se maneja estado de sesión, se está sujeto a fugas de memoria, porque es difícil determinar cuando finaliza la sesión con un determinado usuario. Estas son las fugas típicas. Seguramente, usted encontrará otras. Como las fugas de memoria son muy difíciles de encontrar, vale la pena comprender los patrones asociados con las mismas y aplicar a esas áreas un mayor escrutinio. Cuando se descubra una fuga, se debería usar una plataforma que ayude a identificarla. Resolución de fugas Para resolver los problemas de memoria, se deberían seguir los siguientes pasos: Reproducir el problema. El primer paso (y el más importante) es crear un caso de prueba que reproduzca el problema. Para ello, puede comenzarse activando la recolección de basura después de ejecutar los casos de prueba. Cuando se examine el uso total de memoria respecto al tiempo, se formará un gráfico con picos y valles. Los picos ocurren antes de que el sistema active la recolección de basura. Los valles aparecen después de la recolección de basura y representan el uso verdadero de memoria en el sistema. Si los mínimos en los valles aumentan a un ritmo constante, como en la figura 3, se ha encontrado una fuga de memoria. Figura 3. El eje vertical representa el uso de memoria y el horizontal, el tiempo. El aumento en los valles entre recolecciones de basura (GC) indica una fuga. Aislar el problema. Después de reproducir el problema, se debe intentar aislar el problema acotando el caso de prueba. Con frecuencia, se encuentra la fuga de esta forma, sin necesidad de más investigación. Encontrar los objetos que molestan. El siguiente paso es usar un perfilador de memoria para localizar los objetos en memoria en los que se encuentra la fuga. Así, se comienza identificando bloques de memoria anormalmente grandes. Entonces, se comprueba el número de instancias de estos bloques y, a continuación, se activa la recolección de basura para los mismos, volviendo a contar el número de instancias de nuevo. Así se puede determinar si el sistema está liberando los objetos como debería. Encontrar y reparar referencias a esos objetos. Cuando se ha identificado el objeto causante de la fuga, se pueden encontrar referencias al mismo y repararlas, lo que implica asignarlas a `null` o bien realizar otros pasos de limpieza (cerrar un archivo, eliminar una referencia de una colección, etc.). La limpieza debería realizarse en un bloque `finally` y no en el cuerpo principal de un método, para asegurarse de que se ejecuta incluso cuando las excepciones interrumpen la ejecución del programa. Prevenir futuros casos del problema. Cuando sea posible, se deberían eliminar las causas de los problemas que causaron las fugas para evitar que se repitan en el futuro. 6.3. Buena práctica número 17. Concéntrese en las prioridades Una forma de la regla 80/20 dice que, típicamente, 80% de la ejecución de un programa ocurrirá en un 20% del código. Por ello, es importante concentrar los recursos de desarrollo en las áreas que probablemente tengan un mayor impacto sobre el rendimiento. Para ello, debemos considerar: El análisis de puntos calientes (Hot spot analysis) determina las secciones de código más activas para que los programadores concentren en ellas sus esfuerzos de optimización. Las medidas de rendimiento pueden localizar casos de prueba que son críticamente lentos de forma que pueda variar la ejecución para que pase por las secciones de código con mayor importancia en el rendimiento, con el fin de poderlo optimizar. La automatización de las pruebas de rendimiento permite establecer criterios sólidos de rendimiento para los casos de prueba y resolver problemas cuando se violan esos criterios. Para ello, se puede usar JUnitPerf (http://www.clarkware.com/software/JUnitPerf.html) o cualquier herramienta de automatización de pruebas de rendimiento. La clave de una afinación efectiva es una asignación eficiente de recursos. Es decir, se debería invertir la mayor parte del tiempo en afinar las áreas de la aplicación que producirán el mayor beneficio. Lo contrario también es cierto: no se debería invertir tiempo intentando optimizar cada elemento de código que se escribe, excepto si se sabe que es crítico para el rendimiento. Esto no quiere decir que se deba escribir código descuidado, sino que debe usarse el código más simple que funcione, como mínimo hasta que las pruebas manuales o automáticas de rendimiento confirmen que es necesario optimizarlo. 7. ENTORNOS Aunque este documento de buenas prácticas se concentra en el desarrollo de software, en este apartado se estudiarán buenas prácticas para los entornos de desarrollo y producción. 7.1. Buena práctica número 18. Elija adecuadamente su entorno de producción Para despliegues de gran tamaño, se necesita diseñar el entorno de producción tan cuidadosamente como la aplicación. Las alternativas de despliegue que tienen mayor impacto sobre el desarrollo de la aplicación son los siguientes: Clustering contra standalone. Si la implementación de la aplicación usa la técnica de clustering, cada parte de la aplicación J2EE deberá soportar esta técnica. Distribución de capas en las diferentes máquinas. Las diferentes arquitecturas J2EE se corresponden con diferentes arquitecturas en n-capas. A continuación, se dan unas indicaciones para elegir la arquitectura más adecuada. Capas de una aplicación Las capas ayudan a organizar la lógica de la aplicación. Pueden facilitar el mantenimiento, aumentar la flexibilidad y resolver problemas de despliegue. El catálogo de patrones J2EE reconoce de tres a cinco capas, dependiendo de los requerimientos de cada aplicación: Capa del cliente. Esta capa soporta la interfaz de usuario, que es normalmente HTML, la cual puede extenderse con applets en Java o lenguajes de guión. Capa de presentación. Esta capa es responsable de construir una clara separación entre modelo, vista y controlador. Capa de negocio. Esta capa contiene lógica de negocio. EJB puede ser una implementación de esta capa. Capa de integración. Esta capa proporciona lógica que se usa para acceder a sistemas complejos de legado. Los servicios Web y las arquitecturas de colas pertenecen a esta capa. Capa de recursos. Son recursos las bases de datos o aplicaciones no J2EE. Un recurso puede diseñarse específicamente para J2EE o puede ser una aplicación independiente que requiera una capa de integración. Diferentes configuraciones de despliegue La figura 4 nuestra una configuración común de despliegue, en la que se incluyen dos muros de fuego (“firewalls”) para aumentar la seguridad. La zona entre los dos muros de fuego (llamada la zona desmilitarizada o DMZ) contiene sistemas que necesitan una seguridad moderada y buen tiempo de acceso. Como los clientes externos deben atravesar el muro de fuego para llegar a la capa de presentación, los modelos que usan HTTP son la mejor opción para esta configuración. Figura 4. Diseño con cinco capas. Usualmente se utilizan dos muros de fuego. Aún así, es importante remarcar que se debería adaptar el entorno a los requerimientos de cada aplicación. Las arquitecturas complejas pueden escalar bien y ofrecer buena disponibilidad, pero estos beneficios tienen un costo. Por otra parte, algunos despliegues no necesitan capa de integración y otros pueden desplegar las capas de presentación y de negocio en una única máquina. Por ello, se puede optar por un entorno sencillo, como el que muestra la figura 5, que produce una configuración fácil de manejar y sorprendentemente robusta. Con el lenguaje Java y mainframes que son compatibles con Unix, se puede obtener un rendimiento sorprendentemente bueno con esta configuración. La ventaja de esta opción es que el sistema resulta más sencillo de diseñar y desplegar. Su inconveniente es que la lógica de negocio no está protegida detrás de un segundo muro de fuego. Figura 5. Este despliegue incluye un servidor que contiene conjuntamente las capas de presentación y de negocio. Este servidor está dentro del muro de fuego externo pero fuera del muro de fuego interno. Cuando se requiere alta disponibilidad y un entorno más escalable, se puede usar clustering. Es importante elegir una plataforma que soporte clustering en cada una de las capas más importantes. Además, necesita elegir entre clustering horizontal o vertical. Estas son las opciones con las que cuenta: Con una única CPU, puede añadir máquinas virtuales de Java adicionales y hacer balance de carga entre ellas. Conforme aumenta la carga, puede añadir CPUs adicionales y hacer clustering con múltiples máquinas virtuales de Java usando enrutamiento de conexiones, tolerancia a fallos y balance de carga. Puede hacer clustering con múltiples máquinas, con el servidor de aplicaciones proporcionando tolerancia a fallos, clustering, balance de carga y enrutamiento de conexiones. Considere el escenario en la figura 6. Este escenario despliega cuatro capas, con las capas de presentación, negocios y recursos implementando clustering. Además, se coloca el contenido Web estático en una caché, lo que ahorra comunicaciones costosas y puede mejorar dramáticamente el rendimiento. Una ventaja de este diseño es que no tiene un único punto de fallo, siempre que la implementación proporcione hardware de red redundante. Figura 6. En un despliegue con clustering se obtiene escalabilidad añadiendo hardware y se aumenta la disponibilidad añadiendo redundancia. Por supuesto, conforme el entorno se hace más complejo, también se aumenta la complejidad de su administración. Conforme se añaden clusters y capas, se debe trabajar duro para que la administración del sistema global siga siendo sencilla. 7.2. Buena práctica número 19. No restrinja sus opciones de despliegue en tiempo de diseño Cuando sea posible, debería mantener la máxima flexibilidad mientras se construye el sistema. De esta manera, si necesita cambios en el despliegue, estará preparado para adaptarse a ellos. Esto requiere prestar una gran atención al diseño de la aplicación, de lo que nos ocuparemos a continuación. Gestión del estado de sesión Se puede usar una amplia variedad de técnicas para manejar el estado de sesión en aplicaciones Java del lado del servidor. Con independencia de la que use, debería asegurarse de que soporta estado distribuido de sesión. Se puede usar la API J2EE de manejo de sesión, que es la mejor opción si el estado de sesión es ligero. Tenga presente que esta implementación no soporta cantidades masivas de datos, así que debería hacer que la cantidad de datos de la sesión sea pequeña. Se puede diseñar una arquitectura que asegure que todas las comunicaciones de un único cliente se producen siempre con el mismo servidor. Esta solución se conoce como el bit pegajoso (“sticky bit”). Se pueden usar beans EJB de entidad o beans de sesión con estado para implementar una gestión propia de estado distribuido. Si lo hace así, debe asegurarse de que cuenta con una estrategia para borrar periódicamente viejas conversaciones, si ello es necesario. Cada una de estas soluciones puede implementarse de forma que mantenga adecuadamente un estado distribuido de sesión. Caché Cualquier solución de caché que desarrolle para la aplicación debe soportar también acceso distribuido. Por esta razón, es mejor usar patrones de diseño y software preexistente para la mayoría de necesidades de caché. Además, tenga presente que el clustering puede degradar el rendimiento de la caché por dos razones: La invalidación distribuida de datos “sucios” implica una sobrecarga en las comunicaciones. Con N cachés diferentes, hay menos probabilidad de obtener una correspondencia en la caché, excepto si se implementa una solución “de bit pegajoso”. Muros de fuego Asegúrese que entiende donde va a ubicar cada capa de la solución en relación a los muros de fuego. Cuando sea posible, use soluciones que no limiten sus opciones de despliegue. Por ejemplo, el cliente debería comunicarse con el servidor de presentación estrictamente usando HTTP o HTTPS. Incluso los clientes pesados o los applets de Java pueden usar estos protocolos si se programa con un poco de cuidado. 7.3. Buena práctica número 20. Cree un entorno de desarrollo que responda De todos los componentes que se necesitan para desarrollar software altamente complejo, ninguno contribuye más a la productividad del programador y a la calidad del código resultante que el entorno de desarrollo. El tiempo invertido en crear este entorno normalmente se recupera multiplicado por muchas veces en el transcurso del proyecto de desarrollo. Debería considerar los siguientes temas: Tenga un repositorio único. Para asegurarse de que todos los programadores trabajan con la misma base de código, es esencial usar un sistema de gestión de código fuente. Herramientas como PVCS, Source Safe o Perforce permiten a los programadores asignar versión a los archivos y compartirlos. Debería usar estas herramientas con los archivos fuente y con todos los otros archivos de aplicación, incluyendo archivos de construcción, de guión, DDL’s, imágenes y documentos de diseño. Además, asegúrese de hacer frecuentemente copia de respaldo del repositorio. Integre con frecuencia. La integración empeora exponencialmente con el tiempo. Una integración continua ahorra tiempo total de integración, permitiendo una mayor productividad a los programadores. Incluso los proyectos más grandes pueden integrarse diariamente. Automatice por completo el proceso de construcción. Los programadores no deberían hacer nada más complejo que escribir un único comando para ejecutar el proceso de construcción desde cero. Para ello, pueden usarse herramientas de líneas de comando como Make y Ant. El proceso de construcción debería ser capaz de tomar una máquina completamente limpia, descargar el código del sistema de gestión de código fuente, construir todos los archivos necesarios, empaquetarlos y desplegarlos, todo ello en un único paso. Reproduzca el entorno de producción. Uno de los problemas más difíciles de resolver es cuando el código se ejecuta correctamente en una máquina pero falla en otra. Esto se debe normalmente a diferentes parámetros de configuración en el entorno. Para evitar estos problemas, los entornos de desarrollo deberían ser tan similares al de producción como sea posible. Esto se aplica a hardware, software, sistemas operativos y máquinas virtuales de Java. Cree un entorno autónomo. En las pruebas, los programadores hacen una serie de suposiciones. A veces, estas suposiciones demuestran ser falsas y se desperdicia tiempo intentando resolver problemas que realmente no existen. La corrupción de datos es un ejemplo. Para evitar estos problemas, cada desarrollador debería tener su propio entorno y espacio de datos a los cuales accede en exclusiva. Use un conjunto base de datos. Para probar correctamente el código, el personal de programación y de prueba necesita usar datos conocidos como insumo del proceso de pruebas. Estos datos se modifican rápidamente cuando se ejecuta el código. Para permitir que los programadores comiencen siempre con datos conocidos, es necesario que exista un conjunto base de datos que se instale fácil y rápidamente en el esquema de base de datos que usa el programador. Como con todas las buenas prácticas, considere las que funcionan para usted y descarte el resto. Estas sugerencias pueden mejorar dramáticamente su experiencia de desarrollo. 8. OTROS. 8.1. Buena práctica número 21. Utilice un framework MVC comprobado El patrón MVC es un patrón de diseño de vital importancia. MVC está compuesto de tres módulos diferentes, llamados Modelo, Vista y Controlador (de ahí su nombre). El Modelo está compuesto por el estado y los datos que la aplicación representa. La Vista es la interfaz de usuario que muestra información sobre el modelo y que representa el dispositivo de entrada que se usa para modificarlo. Finalmente, el Controlador es lo que une a los dos anteriores. Hace corresponder las peticiones que llegan del cliente con las acciones correspondientes y dirige las respuestas a las vistas adecuadas. Estas funciones de los componentes MVC pueden verse en la figura 7. Figura 7. Interacción con el patrón MVC y dentro de él. Puede conseguir algunos resultados espectaculares sin más que separar la arquitectura de la aplicación entre los tres componentes MVC. Algunos de los beneficios del patrón MVC son: Múltiples vistas. La aplicación puede mostrar el estado del modelo en una variedad de formas y hacerlo de forma dinámica. Modular. El modelo y la vista están flojamente acoplados así que pueden cambiarse por completo de forma independiente sin necesidad de mucha reprogramación. Adecuado para el crecimiento. Se pueden añadir y actualizar controladores y vistas conforme cambia el modelo. Para implementar un framework MVC, hay una serie de opciones. Puede implementar uno propio o puede aprovecharse de una solución preexistente de código abierto o de un vendedor específico, permitiendo una implementación más rápida y una solución MVC más robusta y mantenible. Compruebe la calidad de nuestro desarrollo offshore. Aurum Solutions tiene experiencia en desarrollo offshore en J2EE y .NET de alta calidad y reducidos costos para empresas europeas y norteamericanas. Conozca nuestro desarrollo offshore haciendo clic aquí o en la página Web http://espanol.aurumsol.com/offshore.html. Cursos de Aurum Solutions relacionados con el tema de este artículo: “Buenas prácticas en arquitecturas J2EE” “Buenas prácticas en programación en Java” “Gestión de proyectos informáticos” “Análisis y diseño orientado a objetos” “Programación en n-capas” “Programación MVC” “Patrones de diseño” “Persistencia para Java” “Enterprise Javabeans” “Servlets y JSP” “Herramientas de pruebas para Java” |