Categoría: Proyectos JEE

  • Proyecto Mundo Oficios: diseño y desarrollo de una tienda online de muñecos por profesiones

    Proyecto Mundo Oficios: diseño y desarrollo de una tienda online de muñecos por profesiones

    Introducción

    En este proyecto vamos a diseñar, analizar y desarrollar una aplicación web completa basada en una tienda online ficticia llamada Mundo Oficios.

    La idea principal de la aplicación es crear una tienda de muñecos de estilo coleccionable, inspirados en diferentes profesiones: bomberos, doctoras, astronautas, chefs, policías, profesoras, constructores y muchas otras figuras similares.

    El objetivo no es únicamente crear una página bonita, sino recorrer varias fases reales del desarrollo de una aplicación web: desde la idea inicial y el diseño visual, hasta el análisis de clases, la base de datos, los casos de uso y la futura implementación en Java.

    Este proyecto nos servirá para practicar conceptos fundamentales del desarrollo de aplicaciones web, combinando diseño, programación, bases de datos y organización de un proyecto.


    1. Objetivo general del proyecto

    El objetivo de Mundo Oficios es construir una aplicación web que permita mostrar y gestionar productos de una tienda online.

    La aplicación tendrá dos partes principales:

    Parte pública

    Será la zona visible para cualquier usuario que visite la web. En esta parte se mostrarán los productos, las categorías, los packs, la información corporativa y las llamadas a la acción para consultar o comprar productos.

    La parte pública representa lo que vería un cliente normal al entrar en la tienda.

    Parte privada o panel de administración

    Será la zona interna de la aplicación. Solo podrán acceder usuarios autorizados, como administradores o gestores de la tienda.

    Desde esta zona privada se podrán dar de alta productos, modificar información, gestionar categorías, subir imágenes, controlar stock, revisar pedidos y consultar datos importantes de la tienda.


    2. Qué vamos a trabajar con este proyecto

    Este proyecto está pensado para trabajar varias competencias al mismo tiempo.

    Durante el desarrollo de Mundo Oficios vamos a practicar:

    • Diseño de interfaces con Figma.
    • Creación de prototipos de páginas web.
    • Organización visual de una web pública.
    • Diseño de una zona privada de administración.
    • Análisis de requisitos.
    • Diagrama de casos de uso.
    • Diagrama de clases.
    • Diagrama entidad-relación de base de datos.
    • Modelado de clases Java.
    • Diseño de tablas SQL.
    • Relación entre frontend, backend y base de datos.
    • Preparación de formularios web.
    • Gestión de productos.
    • Organización de imágenes, categorías y etiquetas.
    • Conceptos básicos de una tienda online.

    La intención es que el alumno no vea cada parte como algo aislado, sino como piezas de un mismo proyecto.

    Una aplicación real no se construye solo escribiendo código. Antes de programar hay que pensar qué necesita el usuario, cómo se va a organizar la información, qué pantallas va a tener la aplicación, qué datos se van a guardar y qué clases representarán esos datos dentro del programa.


    3. Descripción de la aplicación

    Mundo Oficios será una tienda online ficticia especializada en muñecos de diferentes profesiones.

    Cada producto representará una figura concreta. Por ejemplo:

    • Leo Bombero.
    • Nora Doctora.
    • Max Astronauta.
    • Sofía Chef.
    • Policía urbano.
    • Profesora de primaria.
    • Constructor.
    • Mecánica.
    • Científica.
    • Piloto.
    • Enfermero.

    Cada muñeco tendrá una ficha de producto con información básica:

    • Nombre del producto.
    • Profesión.
    • Categoría.
    • Precio.
    • Precio en oferta, si existe.
    • Stock disponible.
    • Edad recomendada.
    • Descripción corta.
    • Descripción completa.
    • Imágenes del producto.
    • Etiquetas.
    • Estado de publicación.
    • Indicación de si está visible o no en la tienda.

    La tienda estará organizada por categorías. Por ejemplo:

    • Emergencias.
    • Salud.
    • Educación.
    • Espacio.
    • Construcción.
    • Cocina.

    Esto nos permite trabajar una estructura típica de comercio electrónico, pero con una temática sencilla, visual y fácil de entender.


    4. Diseño de la página de inicio pública

    La primera parte del proyecto será el diseño de la home o página de inicio.

    La home es una de las páginas más importantes de una web, porque suele ser la primera pantalla que ve el usuario al entrar. Debe explicar de forma rápida qué ofrece la tienda, transmitir confianza y guiar al visitante hacia las secciones principales.

    En nuestro caso, la home de Mundo Oficios tendrá un diseño corporativo, limpio y moderno.

    Elementos principales de la home

    La página de inicio incluirá:

    Cabecera

    La cabecera contendrá el logotipo de Mundo Oficios, un menú de navegación y algunos iconos básicos.

    El menú puede incluir opciones como:

    • Inicio.
    • Profesiones.
    • Categorías.
    • Packs.
    • Sobre nosotros.
    • Contacto.

    También aparecerán iconos de búsqueda, usuario y carrito.

    La cabecera debe ser clara, sencilla y fácil de reconocer. En una web real, el usuario debe poder encontrar rápidamente las secciones importantes.

    Hero principal

    El hero es la sección principal que aparece al inicio de la página.

    En este proyecto, el hero tendrá una frase destacada como:

    Figuras articuladas que inspiran futuros

    Debajo se puede incluir un texto breve explicando la propuesta de la tienda:

    Descubre muñecos de profesiones diseñados para imaginar, jugar y aprender sin límites.

    También aparecerán botones de acción, por ejemplo:

    • Ver catálogo.
    • Conoce nuestra historia.

    El hero debe combinar texto, botones e imagen. En este caso, la imagen principal será un conjunto de muñecos representando varias profesiones.

    Sección Nuestra propuesta

    Esta sección explicará el valor de la marca.

    Puede incluir tres bloques:

    • Calidad que se siente.
    • Colecciona y combina.
    • Aprende jugando.

    Esta parte es importante porque no todo en una tienda online debe ser producto y precio. También hay que transmitir una idea de marca.

    Categorías

    La sección de categorías permitirá al usuario entrar en grupos de productos.

    Por ejemplo:

    • Emergencias.
    • Salud.
    • Espacio.
    • Construcción.

    Cada categoría puede representarse con una tarjeta sencilla, un icono y un enlace para acceder a ella.

    Productos destacados

    En lugar de mostrar muchos productos, la versión más corporativa de la home mostrará pocos productos destacados.

    Esto ayuda a que el diseño sea más limpio y elegante.

    Por ejemplo, podemos mostrar solo dos productos:

    • Leo Bombero.
    • Nora Doctora.

    Cada tarjeta de producto puede incluir:

    • Imagen.
    • Nombre.
    • Categoría.
    • Precio.
    • Valoración.
    • Botón de añadir al carrito.

    La home también puede incluir un banner para packs o colecciones.

    Por ejemplo:

    Packs y colecciones para cada aventura

    Esta sección puede servir para promocionar productos agrupados o descuentos especiales.

    Sección de confianza

    Una tienda online necesita transmitir seguridad.

    Por eso incluiremos una sección con elementos como:

    • Seguridad ante todo.
    • Diseño pensado para niños.
    • Envíos rápidos y seguros.
    • Atención cercana.

    Estos bloques ayudan a reforzar la confianza del usuario antes de comprar.

    Newsletter o llamada a la acción

    Al final de la página puede incluirse una sección para que el usuario deje su correo electrónico y reciba novedades u ofertas.

    Por ejemplo:

    Inspírate con novedades y ofertas exclusivas

    Esta parte nos permite practicar formularios simples dentro del diseño.

    El footer cerrará la página con enlaces secundarios, redes sociales, métodos de pago y datos básicos de la tienda.


    5. Diseño de la parte privada

    Además de la web pública, el proyecto tendrá una parte privada.

    La parte privada es el panel de administración de la tienda. No está pensada para clientes, sino para las personas que gestionan el contenido y los productos.

    En una aplicación real, este panel estaría protegido mediante usuario y contraseña.

    Página de login

    Antes de entrar al panel, el administrador deberá iniciar sesión.

    La pantalla de login tendrá un diseño corporativo y limpio, con dos zonas principales:

    Zona visual de marca

    En una parte de la pantalla aparecerá una imagen corporativa, con varios muñecos y un mensaje relacionado con la gestión de la tienda.

    Por ejemplo:

    Gestiona tu tienda de profesiones desde un solo lugar

    Esta zona ayuda a reforzar la identidad visual del proyecto.

    Formulario de acceso

    En la otra parte de la pantalla aparecerá el formulario de login.

    Tendrá los siguientes campos:

    • Correo electrónico.
    • Contraseña.
    • Checkbox de recordarme.
    • Enlace para recuperar contraseña.
    • Botón de iniciar sesión.
    • Opción secundaria de acceso con Google.
    • Texto de ayuda o soporte.

    Aunque en una primera fase no implementemos todas estas funcionalidades, sí es interesante diseñarlas para entender cómo sería una aplicación real.


    6. Alta de productos en la parte privada

    Una de las pantallas más importantes del panel privado será la de alta de producto.

    Esta pantalla permitirá al administrador crear una nueva ficha de producto para la tienda.

    Estructura general

    La pantalla tendrá:

    • Menú lateral.
    • Barra superior.
    • Zona principal de formulario.
    • Paneles auxiliares a la derecha.

    El menú lateral permitirá acceder a las diferentes secciones del panel.

    Puede incluir opciones como:

    • Resumen.
    • Productos.
    • Categorías.
    • Pedidos.
    • Clientes.
    • Promociones.
    • Estadísticas.
    • Ajustes.

    La opción Productos aparecerá destacada, porque estaremos dentro de esa sección.

    Barra superior

    La barra superior puede incluir:

    • Campo de búsqueda.
    • Icono de notificaciones.
    • Perfil del administrador.

    Estos elementos son habituales en paneles de administración modernos.

    Formulario de alta

    La parte central de la pantalla contendrá el formulario para crear un producto.

    Los campos principales serán:

    • Nombre del producto.
    • SKU.
    • Precio.
    • Precio oferta.
    • Categoría.
    • Profesión.
    • Stock.
    • Edad recomendada.
    • Descripción corta.
    • Descripción completa.
    • Etiquetas.
    • Producto destacado.
    • Visible en tienda.
    • Estado del producto.

    Este formulario nos permitirá trabajar muchos tipos de controles:

    • Inputs de texto.
    • Inputs numéricos.
    • Selectores desplegables.
    • Textareas.
    • Switches.
    • Radios.
    • Botones.
    • Zona de subida de imágenes.

    Galería de imágenes

    Cada producto podrá tener varias imágenes.

    Por ejemplo:

    • Imagen frontal.
    • Imagen lateral.
    • Imagen trasera.
    • Imagen de detalle.

    En el diseño aparecerá una sección de galería donde se podrán subir imágenes del producto.

    En una aplicación real, estas imágenes se guardarían en el servidor o en un servicio de almacenamiento, y en la base de datos se guardaría la ruta o URL de cada imagen.

    Panel de vista previa

    A la derecha del formulario puede aparecer una vista previa del producto.

    Esta vista previa permite comprobar cómo se verá el producto antes de publicarlo.

    Puede mostrar:

    • Imagen principal.
    • Nombre.
    • Categoría.
    • Precio.
    • Precio de oferta.
    • Stock.
    • SKU.

    Este tipo de panel ayuda mucho en aplicaciones de administración, porque permite validar la información antes de guardarla.

    Panel de organización

    También puede haber un bloque para información interna:

    • Colección.
    • Proveedor.
    • Clase de envío.
    • Notas internas.

    Esto no siempre será visible para el cliente, pero sí puede ser útil para la gestión interna de la tienda.

    Botones finales

    Al final del formulario tendremos dos acciones principales:

    • Guardar borrador.
    • Publicar producto.

    Esto nos permite explicar la diferencia entre un producto que está guardado pero no publicado y un producto que ya aparece en la tienda pública.


    7. Diagrama de casos de uso

    El diagrama de casos de uso nos ayuda a entender qué puede hacer cada tipo de usuario dentro del sistema.

    En este proyecto tenemos varios actores:

    Visitante

    Es una persona que entra en la web sin estar registrada.

    Puede:

    • Ver la página de inicio.
    • Ver el catálogo.
    • Filtrar productos.
    • Ver fichas de producto.
    • Registrarse o iniciar sesión.

    Cliente

    Es un usuario que puede comprar o consultar sus pedidos.

    Puede:

    • Ver productos.
    • Añadir productos al carrito.
    • Realizar pedidos.
    • Pagar pedidos.
    • Consultar sus pedidos.

    Administrador o gestor

    Es el usuario que accede a la parte privada.

    Puede:

    • Acceder al panel privado.
    • Gestionar productos.
    • Dar de alta productos.
    • Editar productos.
    • Eliminar o desactivar productos.
    • Subir imágenes.
    • Gestionar categorías.
    • Gestionar profesiones.
    • Gestionar pedidos.
    • Gestionar promociones.
    • Consultar estadísticas.
    • Gestionar usuarios.

    Pasarela de pago

    Es un sistema externo que interviene cuando el cliente paga un pedido.

    En una aplicación real, podría ser PayPal, Stripe, Redsys u otra plataforma similar.

    El diagrama de casos de uso nos sirve para ver el sistema desde el punto de vista funcional. No se centra en las clases ni en la base de datos, sino en las acciones que los usuarios pueden realizar.


    8. Diagrama de clases

    El diagrama de clases representa la estructura del sistema desde el punto de vista de la programación orientada a objetos.

    En Java, cada clase representará un concepto importante del proyecto.

    Clase Producto

    La clase Producto es una de las más importantes.

    Representa cada muñeco que se vende en la tienda.

    Puede tener atributos como:

    • id.
    • nombre.
    • sku.
    • precio.
    • precioOferta.
    • stock.
    • descripcionCorta.
    • descripcionCompleta.
    • edadRecomendada.
    • destacado.
    • visible.
    • estado.

    También puede tener métodos como:

    • publicar.
    • guardarBorrador.
    • aplicarOferta.
    • quitarOferta.
    • actualizarStock.
    • estaDisponible.

    Esta clase estará relacionada con otras clases como Categoria, Profesion, ImagenProducto, Etiqueta, Coleccion y Proveedor.

    Clase Categoria

    La clase Categoria sirve para agrupar productos.

    Por ejemplo:

    • Emergencias.
    • Salud.
    • Educación.
    • Espacio.
    • Construcción.
    • Cocina.

    Una categoría puede tener muchos productos.

    Clase Profesion

    La clase Profesion representa la profesión concreta del muñeco.

    Por ejemplo:

    • Bombero.
    • Doctora.
    • Astronauta.
    • Chef.
    • Policía.
    • Profesora.

    Es importante diferenciar categoría y profesión.

    Por ejemplo, un producto puede estar en la categoría Emergencias y tener la profesión Bombero.

    Clase ImagenProducto

    Un producto puede tener varias imágenes.

    Por eso existe la clase ImagenProducto.

    Esta clase puede guardar:

    • URL de la imagen.
    • Texto alternativo.
    • Si es imagen principal.
    • Orden de aparición.

    Esto permite que un mismo producto tenga una galería de imágenes.

    Clase Etiqueta

    Las etiquetas permiten clasificar productos de forma más flexible.

    Por ejemplo:

    • bombero.
    • emergencias.
    • regalo.
    • colección.
    • oferta.
    • niños.

    Un producto puede tener muchas etiquetas y una etiqueta puede estar asociada a muchos productos.

    Por eso esta relación suele convertirse en una tabla intermedia en la base de datos.

    Clase Cliente

    La clase Cliente representa a una persona que compra en la tienda.

    Puede tener:

    • nombre.
    • apellidos.
    • email.
    • teléfono.

    Un cliente puede tener varias direcciones y varios pedidos.

    Clase Pedido

    La clase Pedido representa una compra realizada por un cliente.

    Puede tener:

    • fecha.
    • estado.
    • subtotal.
    • gastos de envío.
    • total.

    Un pedido estará formado por varias líneas de pedido.

    Clase LineaPedido

    La clase LineaPedido representa cada producto concreto dentro de un pedido.

    Por ejemplo, si un cliente compra dos muñecos de bombero y uno de astronauta, el pedido tendrá dos líneas:

    • Línea 1: Leo Bombero, cantidad 2.
    • Línea 2: Max Astronauta, cantidad 1.

    Cada línea tiene su cantidad, precio unitario y subtotal.

    Clase Pago

    La clase Pago representa la información relacionada con el pago del pedido.

    Puede tener:

    • método de pago.
    • estado del pago.
    • importe.
    • fecha de pago.

    Clase Usuario

    La clase Usuario representa a quien accede al sistema.

    Puede tener distintos roles:

    • ADMIN.
    • GESTOR.
    • CLIENTE.

    En la parte privada nos interesan especialmente los usuarios administradores y gestores.


    9. Diagrama de base de datos

    El diagrama de base de datos representa cómo se guardará la información en tablas.

    Aunque el diagrama de clases y el diagrama de base de datos están relacionados, no son exactamente lo mismo.

    El diagrama de clases representa la estructura del programa en Java.

    El diagrama de base de datos representa cómo se almacenan los datos en MySQL, MariaDB u otro sistema similar.

    Tabla productos

    La tabla principal será productos.

    Contendrá campos como:

    • id_producto.
    • id_categoria.
    • id_profesion.
    • id_coleccion.
    • id_proveedor.
    • id_clase_envio.
    • nombre.
    • sku.
    • precio.
    • precio_oferta.
    • stock.
    • edad_recomendada.
    • descripcion_corta.
    • descripcion_completa.
    • destacado.
    • visible.
    • estado.
    • fecha_creacion.

    Esta tabla tendrá varias claves foráneas para relacionarse con otras tablas.

    Tabla categorias

    La tabla categorias guardará las categorías comerciales de la tienda.

    Campos principales:

    • id_categoria.
    • nombre.
    • descripcion.
    • icono.
    • activa.

    Tabla profesiones

    La tabla profesiones guardará las profesiones disponibles.

    Campos principales:

    • id_profesion.
    • nombre.
    • descripcion.
    • icono.

    Tabla imagenes_producto

    La tabla imagenes_producto guardará las imágenes asociadas a cada producto.

    Campos principales:

    • id_imagen.
    • id_producto.
    • url.
    • texto_alternativo.
    • principal.
    • orden.

    Esta tabla tiene una relación de uno a muchos con productos: un producto puede tener muchas imágenes.

    Tabla etiquetas

    La tabla etiquetas guardará etiquetas reutilizables.

    Como la relación entre productos y etiquetas es de muchos a muchos, necesitaremos una tabla intermedia llamada producto_etiqueta.

    Tabla clientes

    La tabla clientes guardará la información de los compradores.

    Campos principales:

    • id_cliente.
    • nombre.
    • apellidos.
    • email.
    • teléfono.
    • fecha_alta.

    Tabla direcciones

    La tabla direcciones permitirá guardar una o varias direcciones por cliente.

    Campos principales:

    • id_direccion.
    • id_cliente.
    • calle.
    • ciudad.
    • provincia.
    • codigo_postal.
    • país.

    Tabla pedidos

    La tabla pedidos guardará la información general de cada pedido.

    Campos principales:

    • id_pedido.
    • id_cliente.
    • id_direccion.
    • fecha.
    • estado.
    • subtotal.
    • gastos_envio.
    • total.

    Tabla lineas_pedido

    La tabla lineas_pedido guardará los productos incluidos en cada pedido.

    Campos principales:

    • id_linea.
    • id_pedido.
    • id_producto.
    • cantidad.
    • precio_unitario.
    • subtotal.

    Tabla pagos

    La tabla pagos guardará la información de pago del pedido.

    Campos principales:

    • id_pago.
    • id_pedido.
    • metodo.
    • estado.
    • importe.
    • fecha_pago.

    Tabla promociones

    La tabla promociones permitirá gestionar códigos de descuento o promociones.

    Campos principales:

    • id_promocion.
    • nombre.
    • codigo.
    • descuento.
    • fecha_inicio.
    • fecha_fin.
    • activa.

    Como una promoción puede aplicarse a varios productos, y un producto puede tener varias promociones, usaremos una tabla intermedia llamada producto_promocion.


    10. Fases del proyecto

    Para que el trabajo sea más ordenado, dividiremos el proyecto en varias fases.

    Fase 1: Comprensión del proyecto

    En esta fase analizaremos qué queremos construir.

    Responderemos preguntas como:

    • ¿Qué es Mundo Oficios?
    • ¿Qué usuarios tendrá?
    • ¿Qué productos se venderán?
    • ¿Qué partes tendrá la web?
    • ¿Qué necesita el administrador?
    • ¿Qué necesita el cliente?

    El objetivo de esta fase es entender el problema antes de diseñar o programar.

    Fase 2: Diseño visual en Figma

    En esta fase usaremos Figma para diseñar las pantallas principales.

    Trabajaremos:

    • Home pública.
    • Login de la parte privada.
    • Pantalla de alta de producto.
    • Elementos reutilizables.
    • Botones.
    • Tarjetas.
    • Formularios.
    • Menús.
    • Espaciados.
    • Colores.
    • Tipografía.

    El objetivo es que los alumnos entiendan que antes de programar una pantalla conviene diseñarla.

    Figma nos permite pensar la experiencia del usuario sin preocuparnos todavía por el código.

    Fase 3: Casos de uso

    En esta fase identificaremos qué puede hacer cada actor dentro del sistema.

    Trabajaremos con el diagrama de casos de uso para representar:

    • Visitante.
    • Cliente.
    • Administrador.
    • Pasarela de pago.

    El objetivo es comprender las funcionalidades principales de la aplicación.

    Fase 4: Diagrama de clases

    En esta fase pasaremos del análisis funcional a la estructura orientada a objetos.

    Diseñaremos las clases Java que representarán el sistema:

    • Producto.
    • Categoría.
    • Profesión.
    • ImagenProducto.
    • Cliente.
    • Pedido.
    • LíneaPedido.
    • Pago.
    • Usuario.

    El objetivo es entender cómo se transforma una idea de negocio en clases, atributos, métodos y relaciones.

    Fase 5: Diseño de base de datos

    En esta fase diseñaremos las tablas necesarias para almacenar la información.

    Trabajaremos con:

    • Claves primarias.
    • Claves foráneas.
    • Relaciones uno a muchos.
    • Relaciones muchos a muchos.
    • Tablas intermedias.
    • Tipos de datos.
    • Normalización básica.

    El objetivo es que los alumnos comprendan que la base de datos debe reflejar correctamente la estructura de la aplicación.

    Fase 6: Implementación de clases Java

    Una vez diseñado el modelo, podremos empezar a programar las clases Java.

    Por ejemplo:

    • Producto.java.
    • Categoria.java.
    • Profesion.java.
    • Cliente.java.
    • Pedido.java.
    • LineaPedido.java.

    Al principio pueden ser clases sencillas con atributos, constructores, getters, setters y algunos métodos básicos.

    Después podremos avanzar hacia una estructura más completa.

    Fase 7: Formularios web

    En esta fase empezaremos a transformar el diseño en páginas reales.

    Trabajaremos formularios como:

    • Login.
    • Alta de producto.
    • Registro de cliente.
    • Edición de producto.

    El formulario de alta de producto será especialmente importante, porque conectará con la lógica de productos de la aplicación.

    Fase 8: Conexión con base de datos

    En esta fase conectaremos la aplicación Java con la base de datos.

    Dependiendo del nivel del grupo, podremos hacerlo con:

    • JDBC.
    • DAO.
    • Servlets.
    • HTML o plantillas.
    • Frameworks más avanzados si procede.

    El objetivo será guardar, consultar, modificar y eliminar productos desde la base de datos.

    Fase 9: Panel privado funcional

    En esta fase construiremos la parte privada.

    El administrador podrá:

    • Iniciar sesión.
    • Ver productos.
    • Crear productos.
    • Editar productos.
    • Desactivar productos.
    • Subir o asignar imágenes.
    • Gestionar categorías.
    • Ver pedidos.

    No es necesario hacer todo perfecto desde el principio. Lo importante es avanzar por versiones.

    Fase 10: Revisión, pruebas y mejoras

    En la última fase revisaremos el proyecto.

    Comprobaremos:

    • Que las pantallas funcionan.
    • Que los formularios envían datos correctamente.
    • Que se validan los campos importantes.
    • Que la base de datos guarda la información.
    • Que las relaciones están bien planteadas.
    • Que el código está organizado.
    • Que el diseño es coherente.

    También podremos plantear mejoras futuras.


    11. Versión mínima del proyecto

    Como el proyecto puede crecer mucho, conviene definir una versión mínima.

    La versión mínima debería permitir:

    • Ver una home pública.
    • Ver un catálogo básico.
    • Entrar al panel privado.
    • Dar de alta productos.
    • Listar productos.
    • Editar productos.
    • Desactivar productos.
    • Guardar productos en base de datos.

    Con eso ya tendríamos una aplicación bastante completa para trabajar conceptos esenciales.

    No hace falta implementar desde el primer momento pagos reales, estadísticas avanzadas o gestión completa de clientes.


    12. Versión ampliada del proyecto

    Una vez terminada la versión mínima, se pueden añadir mejoras.

    Por ejemplo:

    • Carrito de compra.
    • Registro de clientes.
    • Gestión de pedidos.
    • Subida real de imágenes.
    • Buscador de productos.
    • Filtros por categoría.
    • Filtros por profesión.
    • Promociones y códigos de descuento.
    • Productos destacados.
    • Gestión de stock.
    • Panel de estadísticas.
    • Control de roles.
    • Validaciones avanzadas.
    • Diseño responsive.
    • API REST.
    • Integración con pasarela de pago simulada.

    Estas mejoras nos permitirían convertir el proyecto en una aplicación mucho más completa.


    13. Relación entre diseño, Java y base de datos

    Uno de los puntos más importantes de este proyecto es entender cómo se conectan las diferentes partes.

    Por ejemplo, en el diseño de Figma tenemos un formulario con el campo:

    Nombre del producto

    En Java ese dato puede corresponder al atributo:

    nombre

    Dentro de la clase:

    Producto

    Y en la base de datos se guardará en la columna:

    nombre

    Dentro de la tabla:

    productos

    Este mismo razonamiento se repite con otros campos:

    • Precio.
    • Stock.
    • Categoría.
    • Profesión.
    • Descripción.
    • Estado.
    • Visible.
    • Destacado.

    Así los alumnos pueden ver que el diseño no está separado del código. Lo que se dibuja en Figma después se convierte en HTML, formularios, clases Java y columnas de base de datos.


    14. Ejemplo de flujo: alta de producto

    Vamos a pensar en un ejemplo concreto.

    Un administrador quiere dar de alta un nuevo producto llamado Leo Bombero.

    Paso 1: Accede al panel privado

    El administrador entra en la pantalla de login e introduce su correo y contraseña.

    Si los datos son correctos, accede al panel.

    Paso 2: Entra en productos

    Dentro del menú lateral, pulsa en la opción Productos.

    Desde ahí selecciona la opción para crear un nuevo producto.

    Paso 3: Rellena el formulario

    Introduce los datos:

    • Nombre: Leo Bombero.
    • SKU: BOM-001.
    • Precio: 12,95 €.
    • Precio oferta: 9,95 €.
    • Categoría: Emergencias.
    • Profesión: Bombero.
    • Stock: 50.
    • Edad recomendada: 3+ años.
    • Descripción corta.
    • Descripción completa.
    • Etiquetas.
    • Imágenes.

    Paso 4: Guarda o publica

    El administrador puede guardar el producto como borrador o publicarlo directamente.

    Si lo publica, el producto pasará a estar visible en la tienda pública.

    Paso 5: El cliente lo ve en la tienda

    Un visitante o cliente entra en la web, ve el catálogo, filtra por emergencias y encuentra el producto Leo Bombero.

    Este ejemplo nos ayuda a conectar varias partes del proyecto:

    • Login.
    • Panel privado.
    • Formulario.
    • Producto.
    • Base de datos.
    • Catálogo público.

    15. Organización recomendada para el proyecto Java

    Cuando pasemos a Java, podemos organizar el proyecto por paquetes.

    Una estructura sencilla podría ser:

    src/
    ├── modelo/
    │ ├── Producto.java
    │ ├── Categoria.java
    │ ├── Profesion.java
    │ ├── ImagenProducto.java
    │ ├── Cliente.java
    │ ├── Pedido.java
    │ └── LineaPedido.java

    ├── dao/
    │ ├── ProductoDAO.java
    │ ├── CategoriaDAO.java
    │ └── ClienteDAO.java

    ├── controlador/
    │ ├── ProductoServlet.java
    │ ├── LoginServlet.java
    │ └── PedidoServlet.java

    ├── util/
    │ └── ConexionBD.java

    └── vista/
    ├── index.html
    ├── login.html
    ├── alta-producto.html
    └── listado-productos.html

    Esta organización separa responsabilidades:

    modelo contiene las clases principales.

    dao contiene las clases que trabajan con la base de datos.

    controlador contiene los servlets o controladores.

    util contiene clases auxiliares, como la conexión a base de datos.

    vista contiene las páginas HTML o plantillas.


    16. Posibles ampliaciones

    Los alumnos que avancen más rápido pueden añadir funcionalidades extra.

    Algunas ideas son:

    Buscador de productos

    Permitir buscar productos por nombre, profesión o categoría.

    Filtros avanzados

    Filtrar por:

    • Precio.
    • Categoría.
    • Profesión.
    • Edad recomendada.
    • Disponibilidad.
    • Producto destacado.

    Subida real de imágenes

    Permitir subir imágenes desde el formulario y guardarlas en el servidor.

    Control de roles

    Diferenciar permisos entre administrador y gestor.

    Por ejemplo:

    • El administrador puede gestionar usuarios.
    • El gestor solo puede gestionar productos y pedidos.

    Panel de estadísticas

    Mostrar datos como:

    • Número de productos.
    • Productos con poco stock.
    • Pedidos pendientes.
    • Ventas del mes.
    • Categorías más vendidas.

    Carrito completo

    Implementar un carrito con:

    • Añadir producto.
    • Modificar cantidad.
    • Eliminar producto.
    • Calcular total.
    • Confirmar pedido.

    API REST

    Crear una API para que el frontend pueda consultar productos desde Java.


    17. Qué aprenderemos realmente con este proyecto

    Aunque el proyecto se presenta como una tienda de muñecos, en realidad estamos trabajando conceptos aplicables a muchas aplicaciones web.

    Lo que aprendamos aquí se puede aplicar después a:

    • Una tienda de ropa.
    • Una tienda de videojuegos.
    • Una biblioteca.
    • Un sistema de reservas.
    • Una plataforma de cursos.
    • Un inventario de material.
    • Un sistema de gestión de alumnos.
    • Una aplicación de pedidos.

    La temática de los muñecos nos ayuda a que el proyecto sea visual y fácil de entender, pero la estructura técnica es la de una aplicación web real.


    18. Consejos

    Antes de empezar a programar, es importante entender bien el proyecto.

    No hay que lanzarse directamente al código sin pensar.

    Un buen desarrollo suele seguir este orden:

    Primero entendemos qué queremos construir.

    Después diseñamos las pantallas.

    Luego analizamos los casos de uso.

    Después pensamos las clases.

    Luego diseñamos la base de datos.

    Finalmente empezamos a programar.

    Si seguimos este proceso, el proyecto será más ordenado y será más fácil detectar errores.

    También es importante no intentar hacerlo todo de golpe. Una aplicación grande se construye por partes.

    Primero hacemos una versión sencilla que funcione. Después vamos añadiendo mejoras.


    19. Conclusión

    El proyecto Mundo Oficios nos permitirá trabajar un caso completo de desarrollo de aplicación web.

    Partiremos de una idea sencilla: una tienda online de muñecos por profesiones.

    A partir de esa idea iremos construyendo todo lo necesario:

    • Diseño visual de la web.
    • Prototipo en Figma.
    • Home pública.
    • Login privado.
    • Panel de administración.
    • Alta de productos.
    • Diagrama de casos de uso.
    • Diagrama de clases.
    • Diagrama de base de datos.
    • Clases Java.
    • Formularios.
    • Conexión con base de datos.
    • Gestión de productos.

    El objetivo final no es solo tener una aplicación funcionando, sino comprender el proceso completo que hay detrás de un proyecto web.

    Este proyecto nos va a permitir unir diseño, análisis y programación en un mismo trabajo, acercándonos a la forma en la que se desarrollan aplicaciones reales.

  • Subida de imágenes en Java JEE usando Servlets (Multipart)

    Subida de imágenes en Java JEE usando Servlets (Multipart)

    En esta práctica aprenderás a:

    • Enviar un archivo desde un formulario HTML hacia un Servlet.
    • Procesar peticiones multipart/form-data.
    • Guardar una imagen en el servidor.
    • Obtener el nombre del archivo subido.
    • Persistir la referencia en base de datos.
    • Entender el flujo completo cliente → servidor → disco → BD → vista.

    Arquitectura del proceso

    El flujo que vamos a implementar es el siguiente:

    [Formulario HTML]
            ↓
    POST multipart/form-data
            ↓
    [Servlet controlador]
            ↓
    Procesa campos + fichero
            ↓
    Guarda imagen en disco
            ↓
    Inserta datos en BD (nombre del archivo)
            ↓
    Redirección a galería
            ↓
    [Vista HTML muestra la imagen]

    Video

    El Servlet

    Anotaciones necesarias

    @WebServlet("/BestiasFoto")
    @MultipartConfig
    public class BestiasFoto extends HttpServlet {

    @WebServlet

    Define la URL que ejecuta el servlet:

    http://servidor/app/BestiasFoto

    @MultipartConfig

    Activa el soporte de subida de archivos.

    Sin esta anotación:

    • request.getPart("foto") no funcionará
    • El servidor no podrá procesar multipart/form-data

    Variables del Servlet

    private String pathFiles = "C:\\entornos\\clasesMaster\\BestiarioJEE\\bestiario\\src\\main\\webapp\\img\\";
    private File uploads = new File(pathFiles);
    • pathFiles → carpeta donde se guardarán las imágenes
    • uploads → objeto File que representa esa carpeta

    En esta práctica guardamos las imágenes dentro del proyecto. En producción normalmente se usa una carpeta externa.


    Método doPost()

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    
        if(request.getParameter("opcion").equals("1")) {
            insertar_actualizarBestia(request,response);
        } else if(request.getParameter("opcion").equals("2")) {
            listarBestias(Integer.parseInt(request.getParameter("id")),request,response);
        }
    }

    Este método actúa como enrutador.

    opcionAcción
    1Inserta o actualiza una bestia + sube imagen
    2Listado de bestias

    Método insertar_actualizarBestia()

    Este método:

    1. Recoge los datos del formulario
    2. Sube la imagen
    3. Inserta en BD
    4. Redirige a la galería
    public void insertar_actualizarBestia(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
    
        int id = Integer.parseInt(request.getParameter("id"));
        String nombre = request.getParameter("nombre");
        int tam = Integer.parseInt(request.getParameter("tam"));
        int alineamiento = Integer.parseInt(request.getParameter("alineamiento"));
        String descripcion = request.getParameter("descripcion");
    
        String ruta = this.subirFoto(request, response);
    
        if(id != 0) {
            // actualizar
        } else {
            Bestia b = new Bestia(nombre, tam, alineamiento, descripcion, ruta);
            try {
                b.insertar();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            response.sendRedirect("galeria.html");
        }
    }

    Explicación

    • getParameter() → obtiene datos normales del formulario
    • subirFoto() → guarda la imagen y devuelve el nombre del archivo
    • sendRedirect() → evita reenviar el formulario al refrescar

    Método subirFoto()

    Este método recibe el archivo, lo guarda en el servidor y devuelve el nombre.

    private String subirFoto(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
    
        Part part = request.getPart("foto");
    
        Path path = Paths.get(part.getSubmittedFileName());
        String fileName = path.getFileName().toString();
    
        InputStream input = part.getInputStream();
    
        String ruta ="";
        String nombreArchivo ="";
    
        if(input != null) {
    
            File file = new File(uploads, fileName);
    
            ruta = file.getAbsolutePath();
            nombreArchivo = file.getName();
    
            System.out.println("La ruta de la foto es: " + ruta);
    
            try {
                Files.copy(input, file.toPath());
            } catch (Exception e) {
                PrintWriter respuesta = response.getWriter();
                respuesta.print("Error al copiar la foto");
                System.out.println("Error al copiar la foto");
            }
        }
    
        return nombreArchivo;
    }

    Qué ocurre paso a paso

    1. Obtener el archivo enviado

    Part part = request.getPart("foto");

    Part representa el archivo enviado desde el formulario.


    2. Obtener el nombre del archivo

    Path path = Paths.get(part.getSubmittedFileName());
    String fileName = path.getFileName().toString();

    Se usa Path para eliminar posibles rutas y quedarse solo con el nombre.


    3. Obtener los bytes del archivo

    InputStream input = part.getInputStream();

    Un InputStream permite leer el contenido del archivo.


    4. Crear el archivo destino

    File file = new File(uploads, fileName);

    Esto crea el archivo dentro de la carpeta img.


    5. Copiar el archivo

    Files.copy(input, file.toPath());

    Copia los bytes desde el InputStream al fichero del servidor.


    6. Devolver el nombre del archivo

    returnnombreArchivo;

    En la base de datos solo guardamos el nombre, no la ruta completa.


    Formulario HTML necesario

    <form action="BestiasFoto" method="post" enctype="multipart/form-data">
    
      <input type="hidden" name="opcion" value="1">
      <input type="hidden" name="id" value="0">
    
      <input type="text" name="nombre" placeholder="Nombre">
      <input type="number" name="tam" placeholder="Tamaño">
      <input type="number" name="alineamiento" placeholder="Alineamiento">
      <textarea name="descripcion"></textarea>
    
      <input type="file" name="foto" accept="image/*">
    
      <button type="submit">Guardar</button>
    
    </form>

    Mostrar la imagen guardada

    Si en BD se guarda dragon.jpg:

    <img src="img/dragon.jpg">

    Métodos utilizados

    HttpServletRequest

    MétodoFunción
    getParameter()Obtiene valores del formulario
    getPart()Obtiene el archivo subido

    Part

    MétodoFunción
    getSubmittedFileName()Nombre original del archivo
    getInputStream()Bytes del archivo
    getSize()Tamaño del archivo
    getContentType()Tipo MIME

    Files / Path

    MétodoFunción
    Files.copy()Copia el archivo al servidor
    Paths.get()Crea un Path
    getFileName()Obtiene el nombre del archivo

    Buenas prácticas

    • Guardar solo el nombre del archivo en BD
    • Validar que el archivo no esté vacío
    • Validar tipo MIME (image/png, image/jpeg…)
    • Evitar nombres duplicados
    • Crear la carpeta si no existe

    Tabla de métodos/clases usados

    Servlet / Request / Response

    ElementoMétodo¿Para qué sirve?
    HttpServletRequestgetParameter("x")Lee un campo normal del formulario (texto, hidden, number…)
    HttpServletRequestgetPart("foto")Recupera un archivo (o parte) del multipart con ese name
    HttpServletResponsesendRedirect("url")Redirige al navegador a otra página tras el POST
    HttpServletResponsegetWriter()Permite escribir una respuesta de texto al cliente

    Multipart / Part

    ElementoMétodo¿Para qué sirve?
    PartgetSubmittedFileName()Devuelve el nombre original del fichero enviado
    PartgetInputStream()Devuelve un stream con los bytes del archivo
    PartgetSize()Tamaño del archivo en bytes (útil para validar)
    PartgetContentType()Tipo MIME (ej. image/png)

    Files / Path

    ElementoMétodo¿Para qué sirve?
    Pathsget(String)Crea un Path a partir de una ruta o nombre
    PathgetFileName()Devuelve el último segmento (nombre)
    Filescopy(InputStream, Path, ...)Copia datos de un stream a un fichero
    FilegetAbsolutePath()Ruta absoluta en disco
    FilegetName()Nombre del archivo sin ruta

    Validaciones en la subida de imágenes (métodos independientes)

    • Validar el nombre del archivo
    • Evitar archivos duplicados
    • Validar tipo MIME
    • Validar extensión
    • Validar tamaño
    • Validar que el archivo sea realmente una imagen
    • Generar nombre seguro (UUID)

    Cada método tiene una responsabilidad única.


    1. Validar nombre de archivo

    Evitar nombres peligrosos, rutas maliciosas o caracteres inválidos.

    Método

    private String validarNombreArchivo(String originalName) throws ServletException {
    
        if (originalName == null || originalName.isBlank()) {
            throw new ServletException("El archivo no tiene nombre válido");
        }
    
        // eliminar rutas potenciales
        String nombre = Paths.get(originalName).getFileName().toString();
    
        // permitir solo letras, números, punto, guion y guion bajo
        if (!nombre.matches("[a-zA-Z0-9._-]+")) {
            throw new ServletException("Nombre de archivo inválido");
        }
    
        return nombre;
    }

    Qué hace

    • Evita nombres vacíos
    • Elimina rutas (C:\algo\malicioso\file.jpg)
    • Bloquea caracteres peligrosos
    • Reduce riesgo de path traversal

    2. Validar tipo MIME

    Evitar que suban archivos ejecutables disfrazados de imagen.

    Método

    private void validarTipoMime(Part part) throws ServletException {
    
        String mime = part.getContentType();
    
        if (mime == null || !mime.startsWith("image/")) {
            throw new ServletException("Solo se permiten archivos de imagen");
        }
    }

    Qué hace

    • Comprueba que el archivo es image/*
    • Bloquea .exe, .jsp, .php, etc.

    3. Validar extensión

    Permitir solo formatos conocidos.

    Método

    private void validarExtension(String fileName) throws ServletException {
    
        String ext = "";
    
        int punto = fileName.lastIndexOf('.');
        if (punto >= 0) {
            ext = fileName.substring(punto + 1).toLowerCase();
        }
    
        switch (ext) {
            case "jpg":
            case "jpeg":
            case "png":
            case "gif":
            case "webp":
                return;
            default:
                throw new ServletException("Extensión no permitida");
        }
    }

    Qué hace

    • Extrae la extensión
    • Permite solo imágenes conocidas
    • Evita subir scripts renombrados

    4. Validar tamaño del archivo

    Evitar archivos demasiado grandes.

    Método

    private void validarTamano(Part part, long maxBytes) throws ServletException {
    
        if (part.getSize() == 0) {
            throw new ServletException("El archivo está vacío");
        }
    
        if (part.getSize() > maxBytes) {
            throw new ServletException("El archivo supera el tamaño permitido");
        }
    }

    Uso típico

    validarTamano(part, 2*1024*1024); // 2 MB

    5. Validar que realmente sea imagen

    Evitar archivos falsos con extensión .jpg pero que no son imagen.

    Método

    private void validarContenidoImagen(InputStream input) throws ServletException, IOException {
    
        BufferedImage img = ImageIO.read(input);
    
        if (img == null) {
            throw new ServletException("El archivo no es una imagen válida");
        }
    }

    Qué hace

    • Intenta decodificar la imagen
    • Si falla → no es imagen real

    6. Evitar archivos duplicados

    No sobrescribir archivos existentes.

    Método

    private File evitarDuplicado(File carpeta, String fileName) {
    
        File file = new File(carpeta, fileName);
    
        if (!file.exists()) {
            return file;
        }
    
        String nombre = fileName;
        String base = nombre;
        String ext = "";
    
        int punto = nombre.lastIndexOf('.');
        if (punto >= 0) {
            base = nombre.substring(0, punto);
            ext = nombre.substring(punto);
        }
    
        int contador = 1;
        File nuevo;
    
        do {
            nuevo = new File(carpeta, base + "_" + contador + ext);
            contador++;
        } while (nuevo.exists());
    
        return nuevo;
    }

    Qué hace

    Si ya existe:

    dragon.jpg

    Genera:

    dragon_1.jpg
    dragon_2.jpg
    dragon_3.jpg

    7. Generar nombre seguro (UUID)

    Esto penalizaria el SEO si se trata de archivos que los buscadores pudiesen indexar

    Evitar colisiones y nombres maliciosos.

    Método

    private String generarNombreSeguro(String original) {
    
        String ext = "";
    
        int punto = original.lastIndexOf('.');
        if (punto >= 0) {
            ext = original.substring(punto);
        }
    
        return java.util.UUID.randomUUID().toString() + ext;
    }

    Qué hace

    Convierte:

    dragon.png

    en algo como:

    f47ac10b-58cc-4372-a567-0e02b2c3d479.png

    8. Crear carpeta si no existe

    private void asegurarCarpeta(File carpeta) throws ServletException {
    
        if (!carpeta.exists()) {
            if (!carpeta.mkdirs()) {
                throw new ServletException("No se pudo crear la carpeta de subida");
            }
        }
    }

    9. Ejemplo integrando todas las validaciones

    Este sería el flujo dentro de subirFoto():

    Part part = request.getPart("foto");
    
    validarTipoMime(part);
    validarTamano(part, 2 * 1024 * 1024);
    
    String nombreOriginal = validarNombreArchivo(part.getSubmittedFileName());
    validarExtension(nombreOriginal);
    
    InputStream input = part.getInputStream();
    validarContenidoImagen(input);
    
    // resetear stream tras validación
    input = part.getInputStream();
    
    asegurarCarpeta(uploads);
    
    String nombreSeguro = generarNombreSeguro(nombreOriginal);
    
    File destino = evitarDuplicado(uploads, nombreSeguro);
    
    Files.copy(input, destino.toPath());
    
    return destino.getName();


    Subir un archivo parece trivial, pero en realidad es una intersección entre E/S, seguridad, validación, arquitectura y persistencia. Cuando el alumno domina esto, deja de “subir fotos” y empieza a construir backend serio.

  • Reto –  “Madrid bajo cero”

    Reto – “Madrid bajo cero”

    Hoy ha nevado en Madrid. El ayuntamiento quiere un pequeño programa en Java para registrar datos comunes de la nevada, sin entrar en sistemas complejos ni lógica avanzada.

    Repasa la documentación sobre este reto: https://laaventuradeaprender.com/una-clase-una-instancia-y-cero-excusas-static-y-singleton-en-java/

    EstacionMeteorologica

    Crear una clase llamada EstacionMeteorologica.
    Cada estación representa un punto de medición distinto de Madrid (Sol, Chamartín, Moncloa…), pero hay datos que son globales, iguales para todas las estaciones.

    1. Crea una clase llamada EstacionMeteorologica.
    2. Cada estación tendrá un atributo que es su numero de serie
    3. Añade los siguientes atributos estáticos:
      • ciudad (String) → siempre será "Madrid"
      • temperaturaMedia (double)
      • nevadaActiva (boolean)
    4. Crea solo un constructor vacío (no parámetros).
    5. En el main:
      • Asigna valores a los atributos estáticos usando el nombre de la clase.
      • Crea al menos tres objetos de EstacionMeteorologica.
      • Muestra por pantalla los valores de los atributos estáticos desde cada objeto.

    Reglas del reto

    • No usar métodos static (solo atributos).
    • No usar herencia.
    • No usar arrays, listas ni entrada por teclado.
    • No usar getters ni setters.
    • No usar lógica condicional (if, switch, etc.)

    Ejemplo de salida esperada (orientativa)

    Ciudad: Madrid
    Numero de serie: 124
    Temperatura media: -1.5
    Nevada activa: true
    
    Ciudad: Madrid
    Numero de serie: 234
    Temperatura media: -1.5
    Nevada activa: true

    Emergencia por nieve en Madrid

    Tras la nevada, el ayuntamiento necesita acciones rápidas y globales: avisar a la población, consultar el estado general y actualizar la temperatura.
    Estas acciones no dependen de una estación concreta, sino del estado general de la ciudad.

    Se trabaja con una clase llamada CentroEmergencias.
    No representa un objeto físico concreto, sino un servicio global que actúa sobre el estado general de Madrid.

    1. Crea una clase llamada CentroEmergencias.
    2. Añade los siguientes atributos estáticos:
      • ciudad (String) → "Madrid"
      • temperatura (double)
      • nevadaActiva (boolean)
    3. Añade los siguientes métodos estáticos:
      • activarNevada() → pone nevadaActiva a true
      • desactivarNevada() → pone nevadaActiva a false
      • actualizarTemperatura(double t)
      • mostrarEstado() → muestra todos los datos por pantalla
    4. En el main:
      • Llama a los métodos sin crear ningún objeto.
      • Cambia el estado varias veces.
      • Muestra el estado tras cada cambio.

    Ejemplo de ejecución esperada (orientativa)

    Ciudad: Madrid
    Temperatura: -2.0
    Nevada activa: false
    
    Activando nevada...
    
    Ciudad: Madrid
    Temperatura: -2.0
    Nevada activa: true
    
    Actualizando temperatura...
    
    Ciudad: Madrid
    Temperatura: -4.5
    Nevada activa: true

    Parte meteorológico express

    Tras la nevada, los medios necesitan cálculos rápidos y mensajes claros, pero no quieren guardar estado.
    Solo quieren usar herramientas que reciban datos, hagan su trabajo y desaparezcan sin dejar rastro.

    El objetivo del reto es entender que un método static puede existir sin ningún atributo static.

    Se crea una clase llamada InformeNieve.
    Esta clase no guarda información, solo procesa datos que se le pasan y devuelve resultados o muestra mensajes.

    Es una clase “herramienta”, no un objeto con estado.

    Crea una clase llamada InformeNieve.

    No declares ningún atributo (ni estático ni no estático).

    Crea los siguientes métodos estáticos:

    • mostrarAviso(double temperatura)
    • convertirCelsiusAFahrenheit(double celsius)
    • calcularSensacionTermica(double temperatura, double viento)

    En el main:

    • Llama a los métodos directamente usando el nombre de la clase.
    • Usa valores literales (números escritos directamente).
    • Muestra los resultados por pantalla.

    Reglas del reto

    • Prohibido declarar atributos.
    • Prohibido usar new.
    • Prohibido usar getters, setters o constructores.
    • No usar colecciones ni entrada por teclado.
    • Mantener los métodos simples y claros.

    Ejemplo de uso

    Aviso: Temperatura crítica (-3.0 ºC)
    
    Temperatura en Fahrenheit: 26.6
    
    Sensación térmica estimada: -8.5 ºC

    Este reto es deliberadamente austero.

    “El Quitanieves Supremo de Madrid” (Singleton)

    Madrid ha amanecido blanco. El ayuntamiento activa un único centro de control que coordina todo: no puede haber dos, porque acabarían pisándose órdenes (y eso en una nevada acaba en caos).

    Vas a crear la clase CentroQuitanieves que gestiona el estado de la emergencia:

    • cuántas máquinas quitanieves están activas
    • cuántas toneladas de sal se han repartido
    • cuántos avisos se han emitido

    Todo eso debe estar dentro de una única instancia.

    1. Crea la clase CentroQuitanieves.
    2. Implementa Singleton clásico:
      • atributo privado static para la instancia
      • constructor private
      • método public static CentroQuitanieves getInstancia()
    3. Añade atributos NO estáticos (del objeto único):
      • int maquinasActivas
      • int toneladasSal
      • int avisosEmitidos
    4. Añade métodos (no estáticos) simples:
      • activarMaquina() → suma 1 a maquinasActivas
      • repartirSal(int toneladas) → suma a toneladasSal
      • emitirAviso() → suma 1 a avisosEmitidos
      • mostrarResumen() → imprime todos los datos
    • No puedes hacer new CentroQuitanieves()
    • Debes pedir el centro así:
    CentroQuitanieves c1 = CentroQuitanieves.getInstancia();
    CentroQuitanieves c2 = CentroQuitanieves.getInstancia();
    
    • Comprueba que es el mismo objeto:
    System.out.println(c1 == c2); // debería ser true
    
    • Realiza acciones con c1 y muestra el resumen con c2 (o al revés) para comprobar que comparten estado.

    Ejemplo de salida:

    ¿Mismo centro? true
    
    Resumen Madrid:
    Máquinas activas: 2
    Toneladas de sal: 15
    Avisos emitidos: 3
  • Una clase, una instancia y cero excusas: static y Singleton en Java

    Una clase, una instancia y cero excusas: static y Singleton en Java

    Un atributo static es un atributo que pertenece a la clase, no a los objetos creados a partir de ella.
    Existe una única copia del atributo para toda la aplicación, compartida por todas las instancias.

    • Se declara con la palabra clave static.
    • Se accede usando el nombre de la clase.
    • No depende de ningún objeto concreto.
    • Se inicializa cuando la clase se carga en memoria.

    Cuándo usarlo

    • Contadores globales.
    • Constantes compartidas.
    • Información común a todos los objetos.
    • Estados que deben ser únicos para la clase.

    public class Jugador {
        private String nombre;
        private int puntos;
        public static int totalJugadores = 0;
    
        public Jugador(String nombre) {
            this.nombre = nombre;
            this.puntos = 0;
            totalJugadores++; // actualiza el contador compartido
        }
    }
    


    Si luego haces:

    new Jugador("Ana");
    new Jugador("Luis");
    System.out.println(Jugador.totalJugadores); // Imprime 2
    Da igual cuántos jugadores existan o cuáles sean sus puntos: el contador es único.

    Este tipo de atributo se usa cuando algo pertenece conceptualmente a la clase como conjunto, no a cada objeto. Desde aquí la curiosidad lleva a campos como los métodos static, inicialización estática y patrones de diseño como Singleton o constantes globales, donde las clases juegan a ser “contenedores” más que “plantillas de objetos”.

    Errores comunes

    • Usar static para guardar datos que deberían ser propios de cada objeto.
    • Acceder a un atributo estático usando una instancia en vez de la clase.

    Métodos static en Java

    Un método static es un método que pertenece a la clase, no a los objetos.
    Se puede ejecutar sin crear ninguna instancia.

    • Se llama directamente desde la clase.
    • No puede usar this.
    • No puede acceder directamente a atributos no estáticos.
    • Ideal para lógica independiente del estado de los objetos.

    Cuándo usarlo

    • Métodos de utilidad (cálculos, conversiones, validaciones).
    • Operaciones matemáticas o lógicas puras.
    • Métodos fábrica.
    • Acceso a recursos compartidos.
    public class Conversor {
        public static double celsiusAFahrenheit(double c) {
            return (c * 9/5) + 32;
        }
    }
    double f = Conversor.celsiusAFahrenheit(20);
    System.out.println(f); // 68.0

    Diferencia clave frente a métodos normales

    Un método normal trabaja sobre un objeto concreto.
    Un método static trabaja sobre la idea de la clase.


    Relación entre static y diseño de clases

    • Lo no estático representa el estado individual de cada objeto.
    • Lo estático representa el estado o comportamiento global de la clase.

    Entender esta diferencia es fundamental para:

    • Diseñar bien las clases.
    • Evitar estado global innecesario.
    • Preparar el terreno para patrones de diseño.

    Introducción al patrón Singleton

    El patrón Singleton garantiza que una clase tenga una única instancia y proporciona un punto de acceso global a ella.

    Por qué existe

    Hay elementos del sistema que deben existir una sola vez:

    • Configuración global.
    • Logger.
    • Gestor de recursos.
    • Controladores centrales.

    Características básicas

    • Constructor privado (no se puede usar new desde fuera).
    • Atributo static que guarda la única instancia.
    • Método static que devuelve esa instancia.

    Qué problema resuelve

    • Crear múltiples objetos que representan lo mismo.
    • Inconsistencias de estado.
    • Accesos descontrolados a recursos compartidos.

    Singleton y static: conexión directa

    El patrón Singleton combina directamente los conceptos vistos:

    • Usa un atributo static para almacenar la instancia única.
    • Usa un método static como punto de acceso.
    • Refuerza la idea de que algunas cosas pertenecen a la clase, no a los objetos.

    Ejemplo

    1. La clase se crea a sí misma por dentro.
    2. La clase guarda la única instancia en un atributo estático.
    3. Como el constructor es privado, nadie puede hacer new.
    4. Se accede usando un método static que devuelve siempre la misma instancia.

    Un ejemplo:

    public class Configuracion {
    
        // La única instancia
        private static Configuracion instancia;
    
        // Constructor privado para evitar new desde fuera
        private Configuracion() {
            // Inicialización
        }
    
        // Punto de acceso controlado
        public static Configuracion getInstancia() {
            if (instancia == null) {
                instancia = new Configuracion();
            }
            return instancia;
        }
    
        public void saludar() {
            System.out.println("Soy la única configuración.");
        }
    }
    

    Uso:

    Configuracion c1 = Configuracion.getInstancia();
    Configuracion c2 = Configuracion.getInstancia();
    
    System.out.println(c1 == c2); // true
    

    Ese true es la prueba de que ambos apuntan al mismo objeto.

    Singleton = una clase que garantiza una sola instancia y un único punto de acceso.