En esta sección, vamos a llevar a cabo la implementación del proyecto final utilizando los conocimientos adquiridos a lo largo del curso. Este proyecto integrará varios aspectos de Groovy, desde la sintaxis básica hasta características avanzadas y prácticas recomendadas.
Objetivos del Proyecto
- Desarrollar una aplicación completa que utilice Groovy para resolver un problema específico.
- Aplicar conceptos de programación orientada a objetos, incluyendo clases, herencia, y polimorfismo.
- Utilizar colecciones y closures para manejar datos de manera eficiente.
- Implementar características avanzadas como metaprogramación y transformaciones AST.
- Integrar Groovy con Java y otras tecnologías como XML, JSON, y bases de datos.
- Desarrollar pruebas unitarias y realizar depuración del código.
- Utilizar herramientas del ecosistema Groovy como Gradle y Spock.
Descripción del Proyecto
Vamos a desarrollar una aplicación de gestión de tareas (To-Do List) que permita a los usuarios:
- Crear, leer, actualizar y eliminar tareas.
- Categorizar tareas.
- Marcar tareas como completadas.
- Guardar y cargar tareas desde un archivo JSON.
- Interactuar con una base de datos para persistencia de datos.
Estructura del Proyecto
El proyecto se dividirá en varios módulos:
- Modelo de Datos: Definición de clases y estructuras de datos.
- Persistencia: Manejo de almacenamiento y recuperación de datos.
- Lógica de Negocio: Implementación de la lógica principal de la aplicación.
- Interfaz de Usuario: Interacción con el usuario a través de la consola.
- Pruebas: Desarrollo de pruebas unitarias y de integración.
Paso 1: Modelo de Datos
Definición de Clases
Vamos a definir las clases principales que representarán las tareas y las categorías.
class Task { String id String title String description boolean completed Category category String toString() { return "${completed ? '[x]' : '[ ]'} ${title} - ${description} (Category: ${category?.name})" } } class Category { String id String name String toString() { return name } }
Explicación del Código
- Task: Representa una tarea con un
id
,title
,description
,completed
(booleano) y unacategory
. - Category: Representa una categoría con un
id
yname
. - toString(): Método sobrescrito para proporcionar una representación legible de las tareas y categorías.
Paso 2: Persistencia
Guardar y Cargar Tareas desde JSON
Vamos a utilizar la biblioteca groovy.json.JsonSlurper
y groovy.json.JsonOutput
para manejar la persistencia de datos.
import groovy.json.JsonSlurper import groovy.json.JsonOutput class TaskRepository { List<Task> tasks = [] List<Category> categories = [] void saveToFile(String filePath) { def data = [tasks: tasks, categories: categories] new File(filePath).text = JsonOutput.prettyPrint(JsonOutput.toJson(data)) } void loadFromFile(String filePath) { def file = new File(filePath) if (file.exists()) { def data = new JsonSlurper().parseText(file.text) tasks = data.tasks.collect { new Task(it) } categories = data.categories.collect { new Category(it) } } } }
Explicación del Código
- TaskRepository: Clase que maneja la lista de tareas y categorías.
- saveToFile(): Método para guardar las tareas y categorías en un archivo JSON.
- loadFromFile(): Método para cargar las tareas y categorías desde un archivo JSON.
Paso 3: Lógica de Negocio
Operaciones CRUD
Implementaremos métodos para crear, leer, actualizar y eliminar tareas.
class TaskService { TaskRepository repository TaskService(TaskRepository repository) { this.repository = repository } void addTask(String title, String description, String categoryId) { def category = repository.categories.find { it.id == categoryId } def task = new Task(id: UUID.randomUUID().toString(), title: title, description: description, completed: false, category: category) repository.tasks << task } List<Task> getAllTasks() { return repository.tasks } void updateTask(String taskId, String newTitle, String newDescription, boolean completed) { def task = repository.tasks.find { it.id == taskId } if (task) { task.title = newTitle task.description = newDescription task.completed = completed } } void deleteTask(String taskId) { repository.tasks.removeIf { it.id == taskId } } }
Explicación del Código
- TaskService: Clase que maneja la lógica de negocio de las tareas.
- addTask(): Método para agregar una nueva tarea.
- getAllTasks(): Método para obtener todas las tareas.
- updateTask(): Método para actualizar una tarea existente.
- deleteTask(): Método para eliminar una tarea.
Paso 4: Interfaz de Usuario
Interacción con el Usuario
Implementaremos una interfaz de usuario simple basada en la consola.
class TaskApp { TaskService taskService TaskApp(TaskService taskService) { this.taskService = taskService } void run() { println "Welcome to the To-Do List App" while (true) { println "\n1. Add Task\n2. View Tasks\n3. Update Task\n4. Delete Task\n5. Save and Exit" def choice = System.console().readLine("Choose an option: ") switch (choice) { case '1': addTask() break case '2': viewTasks() break case '3': updateTask() break case '4': deleteTask() break case '5': saveAndExit() return default: println "Invalid option. Please try again." } } } void addTask() { def title = System.console().readLine("Enter task title: ") def description = System.console().readLine("Enter task description: ") def categoryId = System.console().readLine("Enter category ID: ") taskService.addTask(title, description, categoryId) println "Task added successfully." } void viewTasks() { def tasks = taskService.getAllTasks() tasks.each { println it } } void updateTask() { def taskId = System.console().readLine("Enter task ID to update: ") def newTitle = System.console().readLine("Enter new task title: ") def newDescription = System.console().readLine("Enter new task description: ") def completed = System.console().readLine("Is the task completed? (yes/no): ") == 'yes' taskService.updateTask(taskId, newTitle, newDescription, completed) println "Task updated successfully." } void deleteTask() { def taskId = System.console().readLine("Enter task ID to delete: ") taskService.deleteTask(taskId) println "Task deleted successfully." } void saveAndExit() { taskService.repository.saveToFile("tasks.json") println "Tasks saved. Exiting..." } }
Explicación del Código
- TaskApp: Clase que maneja la interacción con el usuario.
- run(): Método principal que muestra el menú y maneja las opciones del usuario.
- addTask(): Método para agregar una nueva tarea.
- viewTasks(): Método para ver todas las tareas.
- updateTask(): Método para actualizar una tarea existente.
- deleteTask(): Método para eliminar una tarea.
- saveAndExit(): Método para guardar las tareas y salir de la aplicación.
Paso 5: Pruebas
Pruebas Unitarias
Vamos a utilizar Spock para escribir pruebas unitarias.
import spock.lang.Specification class TaskServiceSpec extends Specification { def "should add a new task"() { given: def repository = new TaskRepository() def service = new TaskService(repository) when: service.addTask("Test Task", "This is a test task", null) then: repository.tasks.size() == 1 repository.tasks[0].title == "Test Task" } def "should update an existing task"() { given: def repository = new TaskRepository() def service = new TaskService(repository) def task = new Task(id: "1", title: "Old Task", description: "Old description", completed: false) repository.tasks << task when: service.updateTask("1", "New Task", "New description", true) then: task.title == "New Task" task.description == "New description" task.completed == true } }
Explicación del Código
- TaskServiceSpec: Clase de especificación de Spock para probar
TaskService
. - should add a new task: Prueba que verifica que una nueva tarea se agrega correctamente.
- should update an existing task: Prueba que verifica que una tarea existente se actualiza correctamente.
Conclusión
En esta sección, hemos implementado una aplicación de gestión de tareas utilizando Groovy. Hemos cubierto la definición del modelo de datos, la persistencia, la lógica de negocio, la interfaz de usuario y las pruebas unitarias. Este proyecto final integra varios conceptos y características de Groovy, proporcionando una experiencia práctica y completa.
Próximos Pasos
- Refactorización y Mejora: Revisa el código y busca oportunidades para mejorar la estructura y eficiencia.
- Añadir Funcionalidades: Considera agregar nuevas características como notificaciones, integración con servicios externos, etc.
- Despliegue: Implementa el despliegue de la aplicación en un entorno de producción.
¡Felicidades por completar el proyecto final y el curso de Groovy!
Curso de Programación Groovy
Módulo 1: Introducción a Groovy
Módulo 2: Sintaxis de Groovy y Características del Lenguaje
Módulo 3: Programación Orientada a Objetos en Groovy
Módulo 4: Características Avanzadas de Groovy
Módulo 5: Groovy en la Práctica
- Entrada/Salida de Archivos
- Trabajando con XML y JSON
- Acceso a Bases de Datos
- Desarrollo Web con Groovy
Módulo 6: Pruebas y Depuración
Módulo 7: Ecosistema y Herramientas de Groovy
- Herramienta de Construcción Gradle
- Framework de Pruebas Spock
- Framework Grails
- Otras Bibliotecas y Herramientas de Groovy
Módulo 8: Mejores Prácticas y Temas Avanzados
- Estilo de Código y Convenciones
- Optimización del Rendimiento
- Consideraciones de Seguridad
- Concurrencia en Groovy