Proyectos de clase
Preámbulo
El propósito principal de estos proyectos era superar mi formación profesional en programación multiplataforma. Aunque intenté hacer todo lo posible para entregar proyectos robustos que cumplieran con todos los requisitos, el ritmo al que trabajábamos a menudo nos obligó a buscar compromisos. En muchos casos, era preferible entregar a tiempo aunque no todos los errores estuvieran resueltos, por lo que seguramente hay errores en muchos de los programas. Por otro lado, aunque he intentado elegir proyectos que fueran exclusivamente míos, en algunos casos hay código de un compañero, Víctor Alocén, ya que era obligatorio trabajar en parejas. Gracias, Víctor, estos dos años no habrían sido tan edificantes si no hubiera tenido un compañero como tú, siempre me mostraste soluciones creativas que no se me habrían ocurrido y compartiste la ambición con la que afrontaba cada proyecto en aras de aprender lo máximo posible.
Buscador de Rutas para Centro Deportivo
Resumen del Proyecto
Esta es una aplicación hecha en Java para encontrar el camino más corto entre dos ubicaciones en un complejo deportivo. Implementa una estructura de datos de grafo y el algoritmo de Dijkstra para calcular rutas óptimas. Para hacerlo más atractivo, se añadió una GUI para visualizar el mapa del centro deportivo y mostrar la ruta óptima entre dos instalaciones.
Aspectos Técnicos Destacados
- Implementación de Grafos: Estructura de grafo personalizada usando listas de adyacencia.
- Algoritmo de Dijkstra: Búsqueda de caminos eficiente entre nodos.
- Desarrollo de GUI: Java Swing para interacción del usuario y visualización de rutas.
- Diseño Orientado a Objetos: Utilización de interfaces y herencia para flexibilidad.
- Manejo de Errores: Gestión robusta de excepciones para casos límite.
Características Principales
- Selección de ubicaciones de inicio y fin por el usuario.
- Cálculo y visualización en tiempo real del camino más corto.
- Cálculo de distancia para la ruta elegida.
- Diseño extensible para fácil agregación de nuevas ubicaciones.
Diseño del Grafo
Aunque el acoplamiento entre nodos y conexiones está implementado en el diseño, al final fue más fácil mapear el Grafo usando un HashMap en lugar de iterar en cada objeto internamente para averiguar cómo están conectados a través de sus propiedades.
Configuración para el Algoritmo
Aunque no hablaré en detalle sobre cómo implementar los pasos del algoritmo, puedes revisar mi configuración y pasos generales para ejecutarlo:
public ArrayList<Conexion> caminoMasCorto(Nodo inicio, Nodo fin) {
// Implementación del algoritmo de Dijkstra
Set<Nodo> nodosVisitados = new HashSet<>();
Map<Nodo, Integer> distanciasMinimas = new HashMap<>();
Map<Nodo, Nodo> nodosPrevios = new HashMap<>();
// Inicializar distancias
for (Nodo nodo : listaAdyacencia.keySet()) {
distanciasMinimas.put(nodo, nodo.equals(inicio) ? 0 : Integer.MAX_VALUE);
nodosPrevios.put(nodo, null);
}
// Bucle principal de Dijkstra
while (nodosVisitados.size() < listaAdyacencia.size()) {
// ... (implementación del algoritmo)
}
// Reconstruir camino
ArrayList<Conexion> caminoMasCorto = new ArrayList<>();
// ... (reconstrucción del camino)
return caminoMasCorto;
}
Desafíos y Soluciones
- Desafío: Representar el diseño de la instalación como un Grafo. Solución: Implementé clases 'Nodo' y 'Conexión' para representar ubicaciones y caminos, conectados a través de un HashMap.
- Desafío: Hacer la aplicación atractiva para el usuario. Solución: Uso de Java Swing para crear la GUI simple pero fácil de usar.
Aprendizajes
- Era la primera vez que trabajaba con algoritmos de búsqueda de caminos, implementar el código después de revisar la teoría fue bastante interesante.
- Mejoré mi dominio en el uso de estructuras de datos, POO y MVC.
Sistema de Punto de Venta para Floristería
Resumen del Proyecto
Esta aplicación Java fue diseñada para gestionar el inventario y las ventas de una pequeña floristería. Algunos de los requisitos eran la capacidad de gestionar artículos con diferentes propiedades, persistencia usando archivos, la capacidad de gestionar inventario y ventas (operaciones CRUD), la capacidad de verificar el inventario y las ventas de un día determinado y algún tipo de interfaz de usuario.
Aspectos Técnicos Destacados
- Implementación de persistencia usando archivos binarios: Cada subsistema (inventario y ventas) tiene su propio diseño y serialización personalizada.
- Arquitectura MVC: Sigue un patrón de diseño común para diseño de aplicaciones, separando los datos de las interacciones del usuario y la GUI.
- Componentes GUI personalizados: Desarrollé un componente Swing personalizado para mostrar una cuadrícula de productos usando el patrón observador para asegurar la sincronización con el resto del sistema.
- Diseño Orientado a Objetos: El uso de interfaces y herencia permite una transición flexible entre el sistema de inventario y ventas.
- Manejo de Errores y Validación: Gestión robusta de excepciones y validación de datos.
- Pruebas: Fueron necesarias pruebas unitarias básicas y pruebas de integración para validar los sistemas y su integración conforme el aplicativo escalaba.
Características Principales
- Gestión de Inventario: Seguimiento de artículos con diferentes propiedades.
- Sistema de Ventas: Procesa transacciones de ventas y genera facturas (JSON).
- Persistencia: Utiliza archivos binarios para el inventario y JSON para registros de ventas individuales.
- GUI: Interfaz de usuario intuitiva construida con Java Swing, JFormDesigner, con componentes personalizados.
Patrón Observador
Un patrón de diseño común que permite la sincronización entre el inventario y su representación visual. Ten en cuenta que estas clases proceden de diferentes archivos del proyecto, las presento en el mismo bloque para facilitar la comprensión de cómo funcionan juntas:
// Definimos la interfaz
public interface InventarioObserver {
void onInventarioChanged();
}
// El inventario tendrá una lista de observadores
public class GestionInventario {
// La lista se utiliza para notificar a los observadores cuando ocurran cambios
private List<InventarioObserver> observers = new ArrayList<>();
// Por ejemplo al agregar objetos
public void agregarItem(InventarioItem item) throws IOException {
// resto de la lógica
notifyObservers();
}
// Para notificar podemos utilizar un método sencillo
private void notifyObservers() {
for (InventarioObserver observer : observers) {
observer.onInventarioChanged();
}
}
}
// Luego nuestros elementos visuales implementarán la interfaz
public class PanelGridProductos extends JPanel implements InventarioObserver {
@Override
public void onInventarioChanged() {
try {
// Este método contiene la lógica para refrescar los objetos y sus propiedades
recargarProductos();
} catch (IOException e) {
// Lógica de manejo de excepciones
}
}
}
// Finalmente, el controlador tiene la tarea de inicializar y registrar los observadores
public class ControlPrincipal implements ActionListener {
// En este caso, la cuadricula de productos y la lista de compra
private void initObservers(){
gestorInventario.registerObserver((InventarioObserver)ventana.getPanelGridInventario());
gestorInventario.registerObserver((InventarioObserver)ventana.getPanelGridFactura());
}
}
Desafíos y Soluciones
- Sincronización de Inventario: Implementé un patrón observador para asegurar actualizaciones en tiempo real en toda la aplicación.
- Diseño e Integración de Sistemas: El sistema se tuvo que rediseñar en un par de ocasiones dado que no era una escala a la que nos hubiéramos enfrentado antes usando persistencia de objetos, mantener el inventario de forma robusta mientras los objetos pasaban al sistema de venta requirió soluciones como interfaces intermedias entre cada sub-sistema.
- Diseño de GUI: Java Swing es una biblioteca antigua para la creación de GUI, pero era la que nos enseñaron en clase. Implementar un componente moderno como una Cuadrícula fue bastante desafiante y requirió una buena comprensión de la biblioteca Swing.
Aprendizajes
- El alcance de este proyecto fue una buena prueba para ver si estaba progresando como programador, desde operaciones de bajo nivel como diseñar archivos binarios, hasta patrones de alto nivel como los mencionados anteriormente.
- Este proyecto requirió componentes robustos que fueron posibles porque gané familiaridad con el lenguaje y bibliotecas como Java Swing. Probar cada sistema y su integración fue crucial para lograr los comportamientos deseados.
Servidor Matemático
El Servidor Matemático es una aplicación cliente-servidor basada en Java capaz de realizar varias operaciones matemáticas. Los clientes pueden enviar operaciones matemáticas como consultas al servidor, que las procesa y devuelve los resultados. El servidor admite operaciones básicas, así como funciones más complejas como raíces, potencias y trigonometría.
Aspectos Técnicos Destacados
- Arquitectura Cliente-Servidor: Implementa un sistema de comunicación basado en sockets TCP/IP.
- Concurrencia: Maneja m últiples conexiones de clientes simultáneamente.
- Diseño de Protocolo: Protocolo personalizado para la comunicación cliente-servidor.
- Clientes Multiplataforma: Implementados tanto en Java (Swing) como en C# (WPF .NET Core 8).
- Integración de JavaScript: Usa la biblioteca Nashorn para ejecutar un motor JavaScript capaz de ejecutar cálculos complejos.
Características Principales
- Soporta operaciones: +, -, *, /, Seno, Coseno, Raíces y Potencias.
- Manejo robusto de errores para operaciones inválidas o problemas de comunicación.
- Interfaz gráfica de usuario para clientes, funcionando como una calculadora.
- Panel de control mínimo del lado del servidor para gestión (apagado).
Buscando la Solución
Para este proyecto, nuestra prioridad fue definir los pasos para hacer posible la comunicación del servidor con los clientes se realizó un diagrama de secuencia para este propósito:
El programa es bastante simple, no se requieren bucles, no hay condiciones que seguir, solo tenemos que validar las solicitudes, procesarlas en el servidor y devolver las respuestas para ser procesadas por el cliente. Sin embargo, gestionar operaciones basadas en cadenas dadas es complejo y la restricción de tiempo no nos dejó más opciones que confiar en una biblioteca externa para esta tarea, investigar las tecnologías utilizadas para estas características (motores de scripts) fue bastante interesante.
Definición y Uso del Protocolo
public enum Protocol {
public final String SEPARADOR = ":";
public final int OPERACION = 0;
public final int RESULTADO = 1;
public final int ERROR = 2;
public final int CLOSE = 3;
}
// Ejemplo de solicitud: 0:Math.cos(82)
// ejemplo de respuesta: 1:0.9496776978825432
// Método en el cliente Java para enviar solicitudes y leer respuestas
public String getResult(String operation) {
String result = "";
// (...) Algo de lógica para retorno temprano en caso de errores de entrada
this.out.println("" + OPERACION + SEPARADOR + operation); // Auto-flush activo
result = this.in.readLine();
if (result.startsWith("" + ERROR + SEPARADOR)) {
return "ERROR";
}
return result.split(SEPARADOR)[1];
// (...) Lógica de manejo de excepciones
}
Desafíos y Soluciones
- Desafío: Compatibilidad multiplataforma entre clientes Java y C#. Solución: Usé flujos basados en caracteres en lugar de DataInput/OutputStream para compatibilidad.
- Desafío: Implementar operaciones matemáticas complejas. Solución: Integré capacidades de ejecución de JavaScript usando la biblioteca Nashorn.
- Desafío: Gestionar múltiples conexiones de clientes. Solución: Implementamos multi-hilo en el lado del servidor para manejar solicitudes concurrentes de clientes.
Aprendizajes
- Ganamos experiencia en diseñar e implementar protocolos de red.
- Una mejor comprensión de aplicaciones de servidor multi-hilo.
- Integramos diferentes lenguajes de programación (Java, C#, JavaScript) en un solo proyecto.
- Mejoramos el manejo de errores y la robustez en aplicaciones en red.
Comportamientos No Intencionados
La aplicación cumple con varios requisitos obligatorios como que los clientes no se inicien a menos que el servidor esté en funcionamiento, sin embargo, puedes ver en la demo grabada un pequeño error una vez que se cierra el servidor, parece que deberíamos enviar una señal de servidor cerrado para vaciar los búferes y generar errores correctamente una vez que se cierra el servidor.
Conclusión
Aunque este proyecto fue bastante más corto que otros como el gestor de punto de venta, fue una buena introducción a la programación distribuida y es un tema en el que espero poder seguir trabajando en el futuro. Y como nota al pie; es cierto que el servidor y el cliente de java están agrupados en el mismo proyecto, pero esto fue solo para acelerar el desarrollo, pueden aislarse fácilmente en sus propios proyectos.
Proyecto de Simulación del Coeficiente de Fricción
Resumen del Proyecto
Esta aplicación hecha en Java simula el movimiento de un objeto en un plano inclinado, teniendo en cuenta varias propiedades físicas como la masa, el ángulo de inclinación y el coeficiente de fricción entre diferentes materiales. El proyecto cuenta con visualización e interfaz gráfica de usuario, permitiendo a los usuarios ver la simulación y ajustar diferentes parámetros.
Aspectos Técnicos Destacados
- Simulación Física: Implementa cálculos físicos básicos para simular el movimiento de objetos en un plano inclinado.
- Biblioteca Java Swing: Utilizada para crear una interfaz de usuario interactiva.
- Multi-hilo: Usa hilos separados para la animación y las actualizaciones de la GUI.
- Pintado Personalizado: Implementa rutinas de pintado usando la clase Task para una visualización suave.
- Propiedades de Materiales: Incorpora diferentes propiedades de materiales que afectan la fricción y hacen la visualización más atractiva, aportando interés al aplicativo.
- Arquitectura MVC: Sigue el patrón Modelo-Vista-Controlador para una mejor organización del código y pruebas.
Características Principales
- Animación en tiempo real de un objeto deslizándose en un plano inclinado.
- Parámetros ajustables por el usuario:
- Masa del objeto.
- Ángulo de inclinación.
- Materiales para el objeto y el plano.
- Representación visual de diferentes materiales usando colores y texturas.
- Cálculo y visualización de la aceleración basada en principios físicos.
Detalles de Implementación
Cálculo Físico
El núcleo de la simulación reside en calcular la aceleración del objeto basándose en las fuerzas que actúan sobre él:
private double calcularAceleracion() {
double componenteGravedad = GRAVEDAD * Math.sin(radianesPlano);
double componenteNormal = GRAVEDAD * Math.cos(radianesPlano);
double fuerzaFriccion = coeficienteFriccion * masa * componenteNormal;
double aceleracion = componenteGravedad - (fuerzaFriccion / masa);
return aceleracion > 0 ? aceleracion : 0;
}
Este método tiene en cuenta el ángulo de inclinación, la gravedad y la fuerza de fricción para determinar la aceleración del objeto.
Bucle de Animación
La animación se maneja en un hilo separado usando un Timer:
class ActualizarTarea extends TimerTask {
public void run() {
if (fin || estaFueraDeLaVentana()) {
timer.cancel();
timer.purge();
fin = true;
System.out.println("hilo parado");
return;
}
actualizarMovimiento(DELTA_TIME);
actualizarPosicionPeso();
panel.repaint();
}
}
Esto asegura una animación suave mientras mantiene la UI responsiva.
Representación de Materiales
La clase abstracta Material nos permite usar el principio de inversión de dependencia:
Este enfoque permite agregar con facilidad nuevos materiales y sus representaciones visuales.
Desafíos y Soluciones
- Desafío: Implementar física realista en una simulación simple. Solución: Investigué e implementé cuidadosamente fórmulas físicas básicas para el movimiento en plano inclinado y fricción.
- Desafío: Crear una UI responsiva mientras se ejecuta una animación continua. Solución: Uso de multi-hilo para separar la lógica de animación del hilo de la UI.
- Desafío: Representar visualmente diferentes materiales. Solución: Usando Graphics 2D de Java creé texturas y gradientes únicos para cada material.
Aprendizajes
- Gané experiencia práctica en aplicar conceptos físicos en el desarrollo de software.
- Mejoré habilidades en el desarrollo de GUI con Java Swing y renderizado de gráficos personalizados.
- Puse en práctica mis conocimientos de programación concurrente a través de Java.
Conclusión
Aunque esta simulación está lejos de ser perfecta (por ejemplo, solo se tiene en cuenta el coeficiente de fricción dinámico), despertó mi interés en las Matemáticas. Ser capaz de utilizarlas de forma práctica me anima a estudiarlas de nuevo y ver hasta donde puedo llegar con el conocimiento adquirido.
Otros Proyectos Destacados
Realicé otros proyectos que creo que vale la pena mostrar, pero para mantener las cosas más o menos breves, solo mostraré las demos y señalaré un par de características o puntos de interés:
Juanito Brokers SL (Aplicación Cliente-Servidor del Mercado de Valores)
- El servidor implementa concurrencia para manejar múltiples clientes.
- Gestión de base de datos y persistencia: El servidor implementa una conexión a una base de datos Access.
- Patrón DAO: Separa la lógica de negocio de la lógica de acceso a datos.
- Visualización de datos reales: La aplicación utiliza datos reales obtenidos de Yahoo Finance, estos datos se limpian usando un script de Python, que luego se importa a la base de datos. La visualización se maneja en el cliente usando la biblioteca
org.knowm.xchart
.
Aplicación para API de Pronóstico del Tiempo
- Integración de API: Integración funcional con una API de terceros (OpenWeather), usando las bibliotecas java.net.http y google.gson.
- Manejo de Errores y Experiencia de Usuario: El manejo robusto de errores proporciona retroalimentación clara a los usuarios cuando ocurren problemas (nombres de ciudad inválidos o errores de API).
- Conversión de Temperatura: Clases de utilidad manejan la conversión entre Fahrenheit y Celsius.
- Gestión de Configuración: Este proyecto implementa prácticas seguras al utilizar un archivo
application.properties
protegiendo información sensible como claves de API.
Aplicación de Toma de Notas en Kotlin
Aunque esta aplicación es bastante simple, muestra conceptos importantes del desarrollo de Android:
- Kotlin: Hoy en día el lenguaje preferido para el desarrollo de Android.
- Componentes de Android Jetpack: El uso de elementos como RecyclerView y ConstraintLayout demuestra familiaridad con las prácticas modernas de desarrollo de Android.
- Gestión del Ciclo de Vida de la Actividad: Los eventos del ciclo de vida de la actividad se manejan correctamente, particularmente en la comunicación entre MainActivity y EditNoteActivity.
- View Binding: El proyecto utiliza view binding, una característica que genera una clase de binding para cada archivo de layout XML, lo cual es más seguro y eficiente que findViewById().
Espero que esta lectura haya sido amena y con ella entiendas que abarca la FPGS de Programación de aplicaciones Multiplataforma, obviamente, no pretendo exponer todo lo que se puede aprender, y parte del contenido depende de la cátedra de cada profesor y las circunstancias de la clase, además en este caso me he enfocado con el contenido del segundo curso, mucho de lo que se aprende en primero de una u otra forma está englobado en estos proyectos.