├── Readme.md ├── Sesion-01 ├── Ejemplo-01 │ └── Readme.md ├── Ejemplo-02 │ └── Readme.md ├── Ejemplo-03 │ └── Readme.md ├── Imagenes │ ├── Bedu.png │ ├── S01.jpg │ ├── S01_Fig01.jpg │ └── S01_Fig02.jpg ├── Prework │ └── Readme.md ├── Readme.md ├── Reto-01 │ └── Readme.md └── Reto-02 │ └── Readme.md ├── Sesion-02 ├── Ejemplo-01 │ └── Readme.md ├── Ejemplo-02 │ └── Readme.md ├── Ejemplo-03 │ └── Readme.md ├── Imagenes │ └── S02.jpg ├── Prework │ └── Readme.md ├── Readme.md ├── Reto-01 │ └── Readme.md └── Reto-02 │ └── Readme.md ├── Sesion-03 ├── Ejemplo-01 │ └── Readme.md ├── Ejemplo-02 │ └── Readme.md ├── Ejemplo-03 │ └── Readme.md ├── Imagenes │ └── S03.jpg ├── Prework │ └── Readme.md ├── Readme.md ├── Reto-01 │ └── Readme.md └── Reto-02 │ └── Readme.md ├── Sesion-04 ├── Ejemplo-01 │ └── Readme.md ├── Ejemplo-02 │ └── Readme.md ├── Ejemplo-03 │ └── Readme.md ├── Imagenes │ └── S04.jpg ├── Prework │ └── Readme.md ├── Readme.md ├── Reto-01 │ └── Readme.md └── Reto-02 │ └── Readme.md ├── Sesion-05 ├── Ejemplo-01 │ └── Readme.md ├── Ejemplo-02 │ └── Readme.md ├── Ejemplo-03 │ └── Readme.md ├── Imagenes │ └── S05.jpg ├── Prework │ └── Readme.md ├── Readme.md ├── Reto-01 │ └── Readme.md └── Reto-02 │ └── Readme.md ├── Sesion-06 ├── Ejemplo-01 │ └── Readme.md ├── Ejemplo-02 │ └── Readme.md ├── Ejemplo-03 │ └── Readme.md ├── Imagenes │ └── S06.jpg ├── Prework │ └── Readme.md ├── Readme.md ├── Reto-01 │ └── Readme.md └── Reto-02 │ └── Readme.md ├── Sesion-07 ├── Ejemplo-01 │ └── Readme.md ├── Ejemplo-02 │ └── Readme.md ├── Ejemplo-03 │ └── Readme.md ├── Imagenes │ └── S07.jpg ├── Prework │ └── Readme.md ├── Readme.md └── Reto-01 │ └── Readme.md ├── Sesion-08 ├── Ejemplo-01 │ └── Readme.md ├── Ejemplo-02 │ └── Readme.md ├── Ejemplo-03 │ └── Readme.md ├── Imagenes │ └── S08.jpg ├── Prework │ └── Readme.md ├── Readme.md ├── Reto-01 │ └── Readme.md └── Reto-02 │ └── Readme.md ├── Sesion-09 ├── Imagenes │ └── S09.jpg ├── Prework │ └── Readme.md └── Readme.md └── Sesion-10 ├── Imagenes ├── S00.jpg └── S10.jpg ├── Prework └── Readme.md └── Readme.md /Readme.md: -------------------------------------------------------------------------------- 1 |
2 | Sesion_01 3 |
4 | 5 | # 🚀 Curso de Java Standard Edition 2 6 | 7 | ## 🎯 Objetivo del Curso 8 | 🟡 Aprender conceptos avanzados de Java tales como programación funcional, concurrencia, asincronía, bases de datos y microservicios, aplicando buenas prácticas para el desarrollo de software escalable y moderno. 9 | 10 | ## 📘 Prework 11 | 12 | | # | Sesión | 13 | |----|--------| 14 | | 01 | 🧬 [Clases genéricas](Sesion-01/Prework/Readme.md) | 15 | | 02 | 🧵 [Multithreading y procesamiento concurrente](Sesion-02/Prework/Readme.md) | 16 | | 03 | 🧠 [Programación funcional](Sesion-03/Prework/Readme.md) | 17 | | 04 | 🌀 [Procesos asíncronos](Sesion-04/Prework/Readme.md) | 18 | | 05 | 🌊 [Streams reactivos](Sesion-05/Prework/Readme.md) | 19 | | 06 | 🛠️ [Gestión de bases de datos](Sesion-06/Prework/Readme.md) | 20 | | 07 | 🧩 [Microservicios](Sesion-07/Prework/Readme.md) | 21 | | 08 | ✅ [Buenas prácticas](Sesion-08/Prework/Readme.md) | 22 | | 09 | 🧭 [Mentorship 1](Sesion-09/Prework/Readme.md) | 23 | | 10 | 🔄 [Mentorship 2](Sesion-10/Prework/Readme.md) | 24 | 25 | ## 📚 Sesiones 26 | 27 | | # | Sesión | 28 | |----|--------| 29 | | 01 | 🧬 [Clases genéricas](Sesion-01/Readme.md) | 30 | | 02 | 🧵 [Multithreading y procesamiento concurrente](Sesion-02/Readme.md) | 31 | | 03 | 🧠 [Programación funcional](Sesion-03/Readme.md) | 32 | | 04 | 🌀 [Procesos asíncronos](Sesion-04/Readme.md) | 33 | | 05 | 🌊 [Streams reactivos](Sesion-05/Readme.md) | 34 | | 06 | 🛠️ [Gestión de bases de datos](Sesion-06/Readme.md) | 35 | | 07 | 🧩 [Microservicios](Sesion-07/Readme.md) | 36 | | 08 | ✅ [Buenas prácticas](Sesion-08/Readme.md) | 37 | | 09 | 🔁 [Mentorship 1](Sesion-09/Readme.md) | 38 | | 10 | 🎯 [Mentorship 2](Sesion-10/Readme.md) | 39 | 40 | --- 41 | 42 | 🏆 ¡Mucho éxito! Nos vemos en la siguiente sesión. 43 | 44 | --- 45 | -------------------------------------------------------------------------------- /Sesion-01/Ejemplo-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 01**](../Readme.md) ➡️ / 📝 `Ejemplo 01: Introducción a clases y métodos genéricos` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Comprender qué son las **clases genéricas** en Java, cómo se declaran y cuál es su importancia para crear **código reutilizable y seguro en tiempo de compilación**, evitando duplicación de lógica. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - IntelliJ IDEA o cualquier editor compatible con Java 13 | - Conocimientos básicos de clases y métodos en Java 14 | 15 | --- 16 | 17 | ## 🧠 Contexto del ejemplo 18 | 19 | Imagina que gestionas **almacenes** para distintos tipos de productos: **electrónicos**, **ropa**, **alimentos**. Aunque el tipo de producto es diferente, **la lógica para almacenar, recuperar y consultar el stock es la misma**. 20 | 21 | Si creas una clase específica para cada tipo, duplicarías código. Aquí es donde **los genéricos** entran en acción: 22 | Permiten definir **una sola clase que funcione para cualquier tipo de dato**, manteniendo la **seguridad de tipos**. 23 | 24 | --- 25 | 26 | ## 🧱 Paso 1: Crear la clase genérica `Almacen` 27 | 28 | ```java 29 | public class Almacen { 30 | 31 | private T producto; 32 | 33 | // Guarda un producto de cualquier tipo 34 | public void guardarProducto(T producto) { 35 | this.producto = producto; 36 | System.out.println("📦 Producto guardado: " + producto); 37 | } 38 | 39 | // Devuelve el producto almacenado 40 | public T obtenerProducto() { 41 | return producto; 42 | } 43 | 44 | // Verifica si el almacén está vacío 45 | public boolean estaVacio() { 46 | return producto == null; 47 | } 48 | 49 | // Muestra el tipo de producto almacenado 50 | public void mostrarTipoProducto() { 51 | if (producto != null) { 52 | System.out.println("🔍 Tipo de producto almacenado: " + producto.getClass().getSimpleName()); 53 | } else { 54 | System.out.println("🚫 El almacén está vacío."); 55 | } 56 | } 57 | } 58 | ``` 59 | 60 | > 🔍 **`T`** es un **parámetro de tipo genérico** que se reemplaza por el tipo real (ej. `String`, `Integer`, `Producto`) cuando se usa la clase. 61 | 62 | --- 63 | 64 | ## 🚀 Paso 2: Uso de `Almacen` con distintos tipos 65 | 66 | ```java 67 | public class Main { 68 | public static void main(String[] args) { 69 | // 🧺 Almacén de ropa 70 | Almacen almacenRopa = new Almacen<>(); 71 | System.out.println("¿Almacén de ropa vacío? " + almacenRopa.estaVacio()); 72 | almacenRopa.guardarProducto("Camisa"); 73 | almacenRopa.mostrarTipoProducto(); 74 | 75 | // 🔢 Almacén de números 76 | Almacen almacenNumeros = new Almacen<>(); 77 | almacenNumeros.guardarProducto(42); 78 | almacenNumeros.mostrarTipoProducto(); 79 | 80 | // 🍏 Almacén de alimentos 81 | Almacen almacenAlimentos = new Almacen<>(); 82 | almacenAlimentos.guardarProducto("Manzana"); 83 | almacenAlimentos.mostrarTipoProducto(); 84 | 85 | // 🎯 Mostrar productos recuperados 86 | System.out.println("\n🎯 Productos recuperados:"); 87 | System.out.println("🧺 Ropa: " + almacenRopa.obtenerProducto()); 88 | System.out.println("🔢 Número: " + almacenNumeros.obtenerProducto()); 89 | System.out.println("🍏 Alimento: " + almacenAlimentos.obtenerProducto()); 90 | } 91 | } 92 | ``` 93 | 94 | --- 95 | 96 | ## 🧪 Resultado esperado 97 | 98 | ``` 99 | ¿Almacén de ropa vacío? true 100 | 📦 Producto guardado: Camisa 101 | 🔍 Tipo de producto almacenado: String 102 | 📦 Producto guardado: 42 103 | 🔍 Tipo de producto almacenado: Integer 104 | 📦 Producto guardado: Manzana 105 | 🔍 Tipo de producto almacenado: String 106 | 107 | 🎯 Productos recuperados: 108 | 🧺 Ropa: Camisa 109 | 🔢 Número: 42 110 | 🍏 Alimento: Manzana 111 | ``` 112 | 113 | --- 114 | 115 | ## 🔍 Conceptos clave utilizados 116 | 117 | | Concepto | Descripción | 118 | |-----------------------|-------------| 119 | | `T` | Parámetro de tipo genérico (puede ser cualquier identificador como `T`, `E`, `K`, `V`). | 120 | | `Almacen` | Clase genérica que adapta su comportamiento al tipo especificado en tiempo de compilación. | 121 | | `guardarProducto(T)` | Método que recibe un parámetro del tipo genérico. | 122 | | `obtenerProducto()` | Devuelve el objeto almacenado del tipo genérico `T`. | 123 | | `mostrarTipoProducto()` | Muestra el tipo real del producto almacenado usando **reflection** (`getClass().getSimpleName()`). | 124 | | `estaVacio()` | Método que verifica si el almacén está vacío (sin producto almacenado). | 125 | 126 | --- 127 | 128 | ## 📝 En resumen 129 | 130 | - Los **genéricos** permiten crear **clases y métodos reutilizables** para diferentes tipos sin duplicar código. 131 | - Son **seguros en tiempo de compilación**, evitando errores por tipo incorrecto. 132 | - Se usan ampliamente en **estructuras de datos** como **List**, **Map**, **Set** y en **APIs modernas**. 133 | 134 | > 💡 **Tip:** En el siguiente ejemplo, aprenderemos a aplicar **restricciones** a los genéricos y a utilizar **wildcards** (`?`, `extends`, `super`) para mayor control. 135 | 136 | --- 137 | 138 | 📘 **Recursos adicionales:** 139 | 140 | - 🔗 [Guía de genéricos en Java – Oracle](https://docs.oracle.com/javase/tutorial/java/generics/index.html) 141 | - 🔗 [Generics – GeeksForGeeks](https://www.geeksforgeeks.org/generics-in-java/) 142 | 143 | --- 144 | 145 | ⬅️ [**Anterior**](../Readme.md) | [**Siguiente**](../Reto-01/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-01/Ejemplo-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 01**](../Readme.md) ➡️ / 📝 `Ejemplo 02: Uso de wildcards` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Comprender el uso de **wildcards (`?`)** y **restricciones** (`extends`, `super`) en genéricos, para crear métodos **flexibles y seguros** que manipulen listas de diferentes tipos relacionados, utilizando el ejemplo de **componentes aeroespaciales**. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - IntelliJ IDEA o cualquier editor compatible con Java 13 | - Conocimientos básicos de **genéricos** en Java (`List`, clases genéricas) 14 | 15 | --- 16 | 17 | ## 🧠 Contexto del ejemplo 18 | 19 | En una **fábrica aeroespacial**, gestionas diferentes tipos de **componentes**: 20 | 21 | - **Motores** (subtipo de componente). 22 | - **Alas** (otro subtipo). 23 | 24 | Algunos métodos deben **aceptar cualquier tipo de componente** o **limitarse a ciertos subtipos**. Usarás **wildcards (`?`)** para **flexibilizar o restringir** los tipos permitidos en listas de componentes. 25 | 26 | --- 27 | 28 | ## 📄 Código base 29 | 30 | ### 🧱 Paso 1: Crear la jerarquía de componentes 31 | 32 | ```java 33 | // Superclase Componentes 34 | public class Componente { 35 | private final String nombre; 36 | 37 | public Componente(String nombre) { 38 | this.nombre = nombre; 39 | } 40 | 41 | public String getNombre() { return nombre; } 42 | } 43 | ``` 44 | 45 | ```java 46 | // Subclase Motor 47 | public class Motor extends Componente { 48 | public Motor(String nombre) { 49 | super(nombre); 50 | } 51 | } 52 | ``` 53 | 54 | ```java 55 | // Subclase Ala 56 | public class Ala extends Componente { 57 | public Ala(String nombre) { 58 | super(nombre); 59 | } 60 | } 61 | ``` 62 | 63 | --- 64 | 65 | ### 🧱 Paso 2: Método flexible para imprimir cualquier tipo de componente 66 | 67 | ```java 68 | import java.util.List; 69 | 70 | public class InspeccionComponentes { 71 | 72 | // Método flexible: acepta cualquier tipo que sea Componente o subclase de Componente 73 | public static void imprimirComponentes(List componentes) { 74 | for (Componente c : componentes) { 75 | System.out.println("🔍 Inspeccionando componente: " + c.getNombre()); 76 | } 77 | } 78 | 79 | public static void main(String[] args) { 80 | List motores = List.of(new Motor("Motor Falcon 9"), new Motor("Motor Raptor")); 81 | List alas = List.of(new Ala("Ala Delta"), new Ala("Ala Supersónica")); 82 | 83 | // ✅ Método acepta ambos tipos gracias a la wildcard con extends 84 | imprimirComponentes(motores); 85 | imprimirComponentes(alas); 86 | } 87 | } 88 | ``` 89 | 90 | --- 91 | 92 | ## 🧪 Resultado esperado 93 | 94 | ``` 95 | 🔍 Inspeccionando componente: Motor Falcon 9 96 | 🔍 Inspeccionando componente: Motor Raptor 97 | 🔍 Inspeccionando componente: Ala Delta 98 | 🔍 Inspeccionando componente: Ala Supersónica 99 | ``` 100 | 101 | --- 102 | 103 | ## 🔍 Conceptos clave utilizados 104 | 105 | | Concepto | Descripción | 106 | |------------------------|-------------| 107 | | `?` (wildcard) | Representa **cualquier tipo desconocido**. | 108 | | `? extends Componente` | Permite **Componente o cualquier subclase** (ej. `Motor`, `Ala`). Se usa para **leer elementos**. | 109 | | `? super Motor` | Permite **Motor o cualquier superclase** (ej. `Componente`). Se usa cuando quieres **insertar elementos**. | 110 | | Flexibilidad | Permite crear métodos que trabajen con diferentes tipos relacionados. | 111 | 112 | --- 113 | 114 | ## 📝 En resumen 115 | 116 | - **Wildcards (`?`)** permiten crear métodos que trabajan con **diferentes tipos genéricos**. 117 | - **`extends`** es ideal cuando **solo necesitas leer** (evitas romper el tipo). 118 | - **`super`** es útil cuando **insertas elementos** (garantiza compatibilidad hacia arriba). 119 | - En este ejemplo, se inspeccionan diferentes **componentes aeroespaciales** (motores, alas) usando **wildcards con restricciones**. 120 | 121 | > 💡 **Tip:** Si solo **lees elementos** en una lista → usa **`? extends Tipo`**. 122 | > Si necesitas **insertar elementos** → usa **`? super Tipo`**. 123 | 124 | --- 125 | 126 | 📘 Recursos adicionales: 127 | 128 | - 🔗 [Guía de wildcards en Java – Baeldung](https://www.baeldung.com/java-generics-type-parameter-vs-wildcard) 129 | - 🔗 [Wildcards en Genéricos – Oracle Docs](https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html) 130 | 131 | --- 132 | 133 | ⬅️ [**Anterior**](../Reto-01/Readme.md) | [**Siguiente**](../Reto-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-01/Ejemplo-03/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 01**](../Readme.md) ➡️ / 📝 `Ejemplo 03: Métodos genéricos en acción` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Aplicar **genéricos** y **wildcards** en un contexto **financiero**, diferenciando entre **cuentas bancarias** (ahorro, corriente, inversión) y simulando **transacciones** con validaciones. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - IntelliJ IDEA o cualquier editor compatible con Java 13 | - Conocimientos previos de **genéricos** (`List`, wildcards) 14 | 15 | --- 16 | 17 | ## 🧠 Contexto del ejemplo 18 | 19 | Imagina que trabajas en un **sistema bancario** donde debes: 20 | 21 | - **Gestionar cuentas** de distintos tipos (**Ahorro**, **Corriente**, **Inversión**). 22 | - **Procesar transacciones** financieras como **depósitos** y **retiros**. 23 | - Validar operaciones usando **genéricos** y **wildcards** para **flexibilizar** los métodos. 24 | 25 | --- 26 | 27 | ## 📄 Código base: `GestionFinanciera.java` 28 | 29 | ```java 30 | import java.util.*; 31 | 32 | public class GestionFinanciera { 33 | 34 | // Superclase Cuenta 35 | static abstract class Cuenta { 36 | private final String titular; 37 | protected double saldo; 38 | 39 | public Cuenta(String titular, double saldoInicial) { 40 | this.titular = titular; 41 | this.saldo = saldoInicial; 42 | } 43 | 44 | public String getTitular() { return titular; } 45 | public double getSaldo() { return saldo; } 46 | 47 | public void mostrarEstado() { 48 | System.out.println("👤 " + titular + " - Saldo: $" + saldo); 49 | } 50 | } 51 | 52 | // Subclases de cuentas 53 | static class CuentaAhorro extends Cuenta { 54 | public CuentaAhorro(String titular, double saldoInicial) { super(titular, saldoInicial); } 55 | } 56 | 57 | static class CuentaCorriente extends Cuenta { 58 | public CuentaCorriente(String titular, double saldoInicial) { super(titular, saldoInicial); } 59 | } 60 | 61 | static class CuentaInversion extends Cuenta { 62 | public CuentaInversion(String titular, double saldoInicial) { super(titular, saldoInicial); } 63 | } 64 | 65 | // Método genérico para mostrar cuentas (wildcard extends) 66 | public static void mostrarCuentas(List cuentas) { 67 | System.out.println("📋 Estado de cuentas:"); 68 | cuentas.forEach(Cuenta::mostrarEstado); 69 | } 70 | 71 | // Método para procesar depósitos (wildcard super) 72 | public static void procesarDepositos(List cuentas, double cantidad) { 73 | System.out.println("\n💰 Procesando depósitos..."); 74 | cuentas.forEach(c -> { 75 | if (c instanceof CuentaCorriente) { 76 | CuentaCorriente cc = (CuentaCorriente) c; 77 | cc.saldo += cantidad; 78 | System.out.println("✅ Depósito de $" + cantidad + " en cuenta de " + cc.getTitular()); 79 | } 80 | }); 81 | } 82 | 83 | public static void main(String[] args) { 84 | List ahorros = List.of( 85 | new CuentaAhorro("Ana", 1500.0), 86 | new CuentaAhorro("Carlos", 2200.0) 87 | ); 88 | 89 | List corrientes = List.of( 90 | new CuentaCorriente("Luis", 1200.0), 91 | new CuentaCorriente("Sofía", 1800.0) 92 | ); 93 | 94 | List inversiones = List.of( 95 | new CuentaInversion("Marta", 5000.0) 96 | ); 97 | 98 | // 1️⃣ Mostrar cada tipo de cuenta 99 | mostrarCuentas(ahorros); // Impresión 1-2 100 | mostrarCuentas(corrientes); // Impresión 3-4 101 | mostrarCuentas(inversiones); // Impresión 5 102 | 103 | // 2️⃣ Procesar depósitos en cuentas corrientes 104 | procesarDepositos(corrientes, 500.0); // Impresión 6-7 105 | 106 | // 3️⃣ Mostrar cuentas corrientes actualizadas 107 | mostrarCuentas(corrientes); // Impresión 8-9 108 | 109 | System.out.println("\n🔍 Fin de la simulación financiera."); // Impresión 10 110 | } 111 | } 112 | ``` 113 | 114 | > 💡 **Nota**: Es posible **separar este código en diferentes archivos**, organizando cada clase (como `Cuenta`, `CuentaAhorro`, `CuentaCorriente`, etc.) en su propio archivo para mejorar la **escalabilidad y mantenibilidad** del proyecto. 115 | > Sin embargo, en esta ocasión decidimos **mantener todo en un solo archivo** para **facilitar la comprensión** del ejemplo y enfocarnos en el uso de **métodos genéricos** y **wildcards** sin distraernos con detalles de estructura de carpetas o configuración adicional. 116 | 117 | --- 118 | 119 | ## 🧪 Resultado esperado 120 | 121 | ``` 122 | 📋 Estado de cuentas: 123 | 👤 Ana - Saldo: $1500.0 124 | 👤 Carlos - Saldo: $2200.0 125 | 📋 Estado de cuentas: 126 | 👤 Luis - Saldo: $1200.0 127 | 👤 Sofía - Saldo: $1800.0 128 | 📋 Estado de cuentas: 129 | 👤 Marta - Saldo: $5000.0 130 | 131 | 💰 Procesando depósitos... 132 | ✅ Depósito de $500.0 en cuenta de Luis 133 | ✅ Depósito de $500.0 en cuenta de Sofía 134 | 135 | 📋 Estado de cuentas: 136 | 👤 Luis - Saldo: $1700.0 137 | 👤 Sofía - Saldo: $2300.0 138 | 139 | 🔍 Fin de la simulación financiera. 140 | ``` 141 | 142 | --- 143 | 144 | ## 🔍 Conceptos clave utilizados 145 | 146 | | Concepto | Descripción | 147 | |-------------------|-------------| 148 | | `List` | Permite **leer** objetos de tipo `T` o subtipos (para mostrar cuentas). | 149 | | `List` | Permite **modificar o insertar** objetos de tipo `T` o supertipos (para depósitos). | 150 | | Genéricos y subclases | Aplicado en **cuentas bancarias** para **flexibilizar métodos** según el contexto. | 151 | 152 | --- 153 | 154 | ## 📝 En resumen 155 | 156 | - **Wildcards (`?`)** ayudan a **flexibilizar operaciones** sobre listas de **diferentes tipos relacionados**. 157 | - **`extends`** → Usado para **leer** (ej. mostrar saldos de cualquier cuenta). 158 | - **`super`** → Usado para **modificar** (ej. depositar en cuentas corrientes). 159 | - Este patrón es común en **finanzas**, pero aplicable a **otros dominios** como logística, medicina, ingeniería, etc. 160 | 161 | 162 | --- 163 | 164 | ⬅️ [**Anterior**](../Reto-02/Readme.md) | [**Siguiente**](../../Sesion-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-01/Imagenes/Bedu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-01/Imagenes/Bedu.png -------------------------------------------------------------------------------- /Sesion-01/Imagenes/S01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-01/Imagenes/S01.jpg -------------------------------------------------------------------------------- /Sesion-01/Imagenes/S01_Fig01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-01/Imagenes/S01_Fig01.jpg -------------------------------------------------------------------------------- /Sesion-01/Imagenes/S01_Fig02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-01/Imagenes/S01_Fig02.jpg -------------------------------------------------------------------------------- /Sesion-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../Readme.md) ➡️ / 📖 `Sesión 01` 2 | 3 |
4 | Sesion_01 5 |
6 | 7 | # 🎯 Objetivo 8 | 9 | ⚒️ Comprender los fundamentos de las **clases y métodos genéricos en Java**, incluyendo su sintaxis, beneficios y aplicaciones prácticas en estructuras de datos y métodos reutilizables. 10 | 11 | --- 12 | 13 | 📘 Material del prework: 14 | En el prework revisamos **qué son los genéricos**, cómo ayudan a evitar **errores de tipo** en tiempo de compilación y cómo se aplican en colecciones como `List` o `Map`. Hoy profundizaremos con ejemplos prácticos y casos de uso comunes. 15 | 16 | 🔥 ¡Vamos a comenzar! 🔥 17 | 18 | --- 19 | 20 | ## 📂 Temas de la sesión... 21 | 22 | ### 📖 Introducción a los genéricos en Java 23 | 24 | 🔹 ¿Qué son y por qué usarlos? 25 | 🔹 Declaración de clases y métodos genéricos 26 | 🔹 Beneficios en estructuras de datos 27 | 28 | 📜 **[Ejemplo 01: Clase genérica simple](Ejemplo-01/Readme.md)** 29 | 🔥 **[Reto 01: Gestión de órdenes de producción en planta industrial](Reto-01/Readme.md)** 30 | 31 | --- 32 | 33 | ### 📖 Wildcards y restricciones 34 | 35 | 🔹 `?`, `extends`, `super` 36 | 🔹 Reglas de compatibilidad de tipos 37 | 🔹 Ejemplos prácticos y errores comunes 38 | 39 | 📜 **[Ejemplo 02: Uso de wildcards](Ejemplo-02/Readme.md)** 40 | 🔥 **[Reto 02: Aplicación de wildcards en colecciones](Reto-02/Readme.md)** 41 | 42 | --- 43 | 44 | ### 📖 Aplicaciones comunes 45 | 46 | 🔹 Uso con `List`, `Map` 47 | 🔹 Métodos genéricos reutilizables 48 | 🔹 Buenas prácticas 49 | 50 | 📜 **[Ejemplo 03: Métodos genéricos en acción](Ejemplo-03/Readme.md)** 51 | 52 | --- 53 | 54 | ⬅️ [**Anterior**](../Readme.md) | [**Siguiente**](../Sesion-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-01/Reto-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 01**](../Readme.md) ➡️ / ⚡ `Reto 01: Gestión de órdenes de producción en planta industrial` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Implementar **genéricos** y **wildcards** para gestionar diferentes tipos de **órdenes de producción** en una **planta industrial**, clasificando entre **producción en masa**, **personalizada** y **prototipos**. 6 | Además, deberás procesar las órdenes utilizando métodos flexibles con **restricciones de tipo**. 7 | 8 | --- 9 | 10 | ## 🧠 Contexto del reto 11 | 12 | Imagina que trabajas en una **planta industrial** que produce: 13 | 14 | - 🔧 **Órdenes de producción en masa** (productos estándar). 15 | - 🛠️ **Órdenes personalizadas** (adaptadas a cliente). 16 | - 🧪 **Prototipos** (productos en prueba). 17 | 18 | Debes implementar un sistema que: 19 | 20 | 1. **Gestione listas de órdenes de diferentes tipos** (usando genéricos). 21 | 2. **Muestre información de las órdenes** sin importar el tipo. 22 | 3. **Procese las órdenes personalizadas**, agregando un **costo adicional** por ajuste. 23 | 24 | --- 25 | 26 | ## 📝 Instrucciones 27 | 28 | 1. Crea una **clase abstracta** llamada `OrdenProduccion` con los siguientes atributos: 29 | 30 | - `codigo` (String) 31 | - `cantidad` (int) 32 | 33 | Incluye un método `mostrarResumen()` para imprimir información básica. 34 | 35 | 2. Crea tres subclases: 36 | 37 | - `OrdenMasa` (producción en masa) 38 | - `OrdenPersonalizada` (agrega `cliente` como atributo) 39 | - `OrdenPrototipo` (agrega `faseDesarrollo` como atributo) 40 | 41 | 3. Implementa un método genérico: 42 | 43 | - `mostrarOrdenes(List lista)` 44 | (Debe **leer** cualquier tipo de orden y mostrar sus datos). 45 | 46 | 4. Implementa otro método: 47 | 48 | - `procesarPersonalizadas(List lista, int costoAdicional)` 49 | (Debe **modificar** solo las órdenes personalizadas, mostrando un mensaje con el costo agregado). 50 | 51 | 5. En el método `main`, crea listas con varios tipos de órdenes (mínimo **2 por tipo**) y prueba los métodos anteriores. 52 | 53 | --- 54 | 55 | ## 💪 Desafío adicional (opcional) 56 | 57 | - Implementa una función que **cuente** el total de órdenes de **cada tipo** en la planta. 58 | 59 | --- 60 | 61 | ## 💡 Ejemplo de salida esperada 62 | 63 | ``` 64 | 📋 Órdenes registradas: 65 | 🔧 OrdenMasa - Código: A123 - Cantidad: 500 66 | 🔧 OrdenMasa - Código: A124 - Cantidad: 750 67 | 68 | 📋 Órdenes registradas: 69 | 🛠️ OrdenPersonalizada - Código: P456 - Cantidad: 100 - Cliente: ClienteX 70 | 🛠️ OrdenPersonalizada - Código: P789 - Cantidad: 150 - Cliente: ClienteY 71 | 72 | 📋 Órdenes registradas: 73 | 🧪 OrdenPrototipo - Código: T789 - Cantidad: 10 - Fase: Diseño 74 | 🧪 OrdenPrototipo - Código: T790 - Cantidad: 5 - Fase: Pruebas 75 | 76 | 💰 Procesando órdenes personalizadas... 77 | ✅ Orden P456 ajustada con costo adicional de $200 78 | ✅ Orden P789 ajustada con costo adicional de $200 79 | 80 | 📊 Resumen total de órdenes: 81 | 🔧 Producción en masa: 2 82 | 🛠️ Personalizadas: 2 83 | 🧪 Prototipos: 2 84 | ``` 85 | 86 | --- 87 | 88 | 📘 **Recursos útiles**: 89 | 90 | - 🔗 [Genéricos y wildcards – Java Tutorial](https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html) 91 | - 🔗 [Casos de uso de genéricos – Baeldung](https://www.baeldung.com/java-generics) 92 | 93 | --- 94 | 95 | 🏆 Si logras **leer y procesar diferentes tipos de órdenes** utilizando **genéricos** y **wildcards**, ¡reto completado! 96 | 97 | --- 98 | 99 | ⬅️ [**Anterior**](../Ejemplo-01/Readme.md) | [**Siguiente**](../Ejemplo-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-01/Reto-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 01**](../Readme.md) ➡️ / ⚡ `Reto 02: Gestión de materiales de curso en una plataforma educativa` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Utilizar **genéricos**, **wildcards** (`?`, `extends`, `super`) y **restricciones de tipo** para gestionar diferentes **materiales de un curso** (videos, artículos, ejercicios) en una **plataforma educativa**, aplicando **filtros** y **acciones específicas** por tipo de material. 6 | 7 | --- 8 | 9 | ## 🧠 Contexto del reto 10 | 11 | En una **plataforma educativa online**, los cursos están compuestos por diferentes tipos de **materiales**: 12 | 13 | - 🎥 **Videos** 14 | - 📄 **Artículos** 15 | - 📝 **Ejercicios** 16 | 17 | El sistema debe: 18 | 19 | 1. **Mostrar todos los materiales disponibles** de un curso. 20 | 2. **Filtrar solo los videos** para contar su duración total. 21 | 3. **Actualizar** los materiales de tipo **ejercicio**, marcándolos como **revisados**. 22 | 23 | --- 24 | 25 | ## 📝 Instrucciones 26 | 27 | 1. Define una **clase abstracta** `MaterialCurso` con: 28 | 29 | - `titulo` (String) 30 | - `autor` (String) 31 | - Método abstracto `mostrarDetalle()`. 32 | 33 | 2. Crea las subclases: 34 | 35 | - `Video` (agrega `duracion` en minutos). 36 | - `Articulo` (agrega `palabras` como conteo). 37 | - `Ejercicio` (agrega `revisado` como booleano). 38 | 39 | 3. Implementa los siguientes métodos genéricos: 40 | 41 | - `mostrarMateriales(List lista)` 42 | (Muestra el detalle de todos los materiales). 43 | 44 | - `contarDuracionVideos(List lista)` 45 | (Suma y muestra la duración total de los videos). 46 | 47 | - `marcarEjerciciosRevisados(List lista)` 48 | (Actualiza el estado de los ejercicios a `revisado = true` y muestra un mensaje por cada uno). 49 | 50 | 4. En el `main`, crea una lista con al menos **2 videos**, **2 artículos** y **2 ejercicios**, y prueba los métodos anteriores. 51 | 52 | --- 53 | 54 | ## 💪 Desafío adicional (opcional) 55 | 56 | - Implementa un método genérico que **filtre materiales** por **autor** usando `Predicate`. 57 | 58 | --- 59 | 60 | ## 💡 Ejemplo de salida esperada 61 | 62 | ``` 63 | 📚 Materiales del curso: 64 | 🎥 Video: Introducción a Java - Autor: Mario - Duración: 15 min 65 | 🎥 Video: POO en Java - Autor: Carlos - Duración: 20 min 66 | 📄 Artículo: Historia de Java - Autor: Ana - Palabras: 1200 67 | 📄 Artículo: Tipos de datos - Autor: Luis - Palabras: 800 68 | 📝 Ejercicio: Variables y tipos - Autor: Luis - Revisado: false 69 | 📝 Ejercicio: Condicionales - Autor: Mario - Revisado: false 70 | 71 | 🎥 Duración total de videos: 35 minutos 72 | 73 | ✅ Ejercicio 'Variables y tipos' marcado como revisado. 74 | ✅ Ejercicio 'Condicionales' marcado como revisado. 75 | 76 | 🔍 Filtrando materiales por autor: 77 | 🎥 Video: Introducción a Java - Autor: Mario - Duración: 15 min 78 | 📝 Ejercicio: Condicionales - Autor: Mario - Revisado: true 79 | ``` 80 | 81 | --- 82 | 83 | 📘 **Recursos útiles**: 84 | 85 | - 🔗 [Wildcards en Java – Oracle](https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html) 86 | - 🔗 [Java Predicate – Baeldung](https://www.baeldung.com/java-predicate-chain) 87 | 88 | --- 89 | 90 | 🏆 Si logras **mostrar**, **filtrar** y **actualizar** los materiales correctamente usando **genéricos** y **wildcards**, ¡reto completado con éxito! 91 | 92 | --- 93 | 94 | ⬅️ [**Anterior**](../Reto-02/Readme.md) | [**Siguiente**](../Ejemplo-03/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-02/Ejemplo-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 02**](../Readme.md) ➡️ / 📝 `Ejemplo 01: Introducción a hilos con Runnable` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Comprender cómo se crean y ejecutan hilos en Java utilizando la interfaz `Runnable`, y visualizar el comportamiento concurrente mediante impresiones en consola. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - IntelliJ IDEA Community Edition 12 | - JDK 17 o superior 13 | - Conocimientos previos de programación orientada a objetos 14 | - Proyecto Java (sin necesidad de Spring Boot) 15 | 16 | --- 17 | 18 | ## 🧱 Creación del ejemplo 19 | 20 | 1. Crea una clase llamada `Tarea` que implemente `Runnable`. 21 | 2. En el método `run()`, imprime un mensaje junto con el nombre del hilo actual. 22 | 3. Crea varios hilos en la clase principal y observa su ejecución simultánea. 23 | 24 | --- 25 | 26 | ## 📄 Clase `Tarea.java` 27 | 28 | ```java 29 | public class Tarea implements Runnable { 30 | 31 | private String nombre; 32 | 33 | public Tarea(String nombre) { 34 | this.nombre = nombre; 35 | } 36 | 37 | @Override 38 | public void run() { 39 | for (int i = 1; i <= 5; i++) { 40 | System.out.println("Ejecutando " + nombre + " - Iteración " + i + 41 | " - Hilo: " + Thread.currentThread().getName()); 42 | try { 43 | Thread.sleep(500); // Simula una operación que toma tiempo 44 | } catch (InterruptedException e) { 45 | Thread.currentThread().interrupt(); 46 | System.out.println(nombre + " fue interrumpido"); 47 | } 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | --- 54 | 55 | ## 🚀 Clase principal `Main.java` 56 | 57 | ```java 58 | public class Main { 59 | public static void main(String[] args) { 60 | Thread hilo1 = new Thread(new Tarea("Tarea 1")); 61 | Thread hilo2 = new Thread(new Tarea("Tarea 2")); 62 | 63 | hilo1.start(); // Inicia la ejecución del hilo 1 64 | hilo2.start(); // Inicia la ejecución del hilo 2 65 | 66 | System.out.println("Hilos iniciados desde el hilo principal: " + 67 | Thread.currentThread().getName()); 68 | } 69 | } 70 | ``` 71 | 72 | --- 73 | 74 | ## 🧪 Resultado esperado 75 | 76 | Al ejecutar el programa, verás las tareas ejecutándose de manera intercalada: 77 | 78 | ``` 79 | Hilos iniciados desde el hilo principal: main 80 | Ejecutando Tarea 1 - Iteración 1 - Hilo: Thread-0 81 | Ejecutando Tarea 2 - Iteración 1 - Hilo: Thread-1 82 | Ejecutando Tarea 1 - Iteración 2 - Hilo: Thread-0 83 | Ejecutando Tarea 2 - Iteración 2 - Hilo: Thread-1 84 | ... 85 | ``` 86 | 87 | > ⚠️ El orden puede variar cada vez que ejecutes el programa, debido a la naturaleza concurrente de los hilos. 88 | 89 | --- 90 | 91 | ## 🔍 Conceptos clave utilizados 92 | 93 | | Concepto | Descripción | 94 | |--------------|-------------| 95 | | `Runnable` | Interfaz funcional para definir tareas que pueden ser ejecutadas por un hilo | 96 | | `Thread` | Clase que representa un hilo de ejecución en Java | 97 | | `start()` | Inicia la ejecución del hilo llamando internamente al método `run()` | 98 | | `sleep(ms)` | Suspende temporalmente el hilo actual (útil para simular procesos) | 99 | | `currentThread()` | Permite obtener el nombre del hilo en ejecución | 100 | 101 | --- 102 | 103 | ## 📝 En resumen 104 | 105 | - Aprendimos a **crear y ejecutar hilos** en Java usando la interfaz **`Runnable`** y la clase **`Thread`**. 106 | - Cada hilo ejecuta su tarea de forma **independiente**, pero pueden **intercalarse en la ejecución** dependiendo del planificador del sistema operativo. 107 | - **`start()`** inicia el hilo de manera **concurrente**, mientras que **`run()`** ejecuta la tarea **secuencialmente** en el hilo actual. 108 | - Este enfoque es útil para **entender los conceptos básicos de concurrencia**, antes de avanzar hacia herramientas más modernas como **`ExecutorService`** o **`CompletableFuture`**. 109 | 110 | --- 111 | 112 | ## 💡 ¿Sabías que...? 113 | 114 | - Java trata cada hilo como una unidad de ejecución independiente, lo que permite realizar múltiples tareas al mismo tiempo (concurrencia). 115 | - Usar `Thread.sleep()` puede simular operaciones largas, como accesos a bases de datos o servicios web. 116 | - Si llamas a `run()` directamente en lugar de `start()`, **el hilo no se ejecuta concurrentemente**, sino de forma secuencial dentro del hilo actual. 117 | - El método `Thread.currentThread().getName()` ayuda a identificar qué hilo está ejecutando una parte del código, muy útil para depuración. 118 | 119 | --- 120 | 121 | 📘 Recursos adicionales: 122 | 🔗 [Documentación oficial de `Thread`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html) 123 | 🔗 [Programación concurrente en Java](https://java.codeandcoke.com/apuntes:concurrencia) 124 | 125 | --- 126 | 127 | ⬅️ [**Anterior**](../Readme.md) | [**Siguiente**](../Ejemplo-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-02/Ejemplo-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 02**](../Readme.md) ➡️ / 📝 `Ejemplo 02: ExecutorService y tareas concurrentes` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Simular un sistema de procesamiento de pedidos en una tienda en línea, donde múltiples pedidos se procesan en paralelo utilizando `ExecutorService`, `Callable` y `Future`. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - IntelliJ IDEA o cualquier editor con soporte para Java 13 | - Conocimientos básicos de hilos y concurrencia 14 | 15 | --- 16 | 17 | ## 🧱 Contexto del ejemplo 18 | 19 | Imagina una tienda en línea que recibe múltiples pedidos simultáneamente. En lugar de procesar uno por uno, podemos usar **hilos concurrentes** para manejar cada pedido en paralelo, reduciendo el tiempo total de atención. 20 | 21 | Este ejemplo simula 3 pedidos que se procesan de manera concurrente con tiempos de espera variables. 22 | 23 | --- 24 | 25 | ## 📄 Código base: `ProcesadorDePedidos.java` 26 | 27 | ```java 28 | import java.util.concurrent.*; 29 | 30 | public class ProcesadorDePedidos { 31 | 32 | public static void main(String[] args) throws InterruptedException, ExecutionException { 33 | ExecutorService executor = Executors.newFixedThreadPool(3); 34 | 35 | Callable pedido1 = () -> { 36 | Thread.sleep(1200); // Simula procesamiento 37 | return "📦 Pedido #1 entregado en 1.2 segundos"; 38 | }; 39 | 40 | Callable pedido2 = () -> { 41 | Thread.sleep(800); 42 | return "📦 Pedido #2 entregado en 0.8 segundos"; 43 | }; 44 | 45 | Callable pedido3 = () -> { 46 | Thread.sleep(1500); 47 | return "📦 Pedido #3 entregado en 1.5 segundos"; 48 | }; 49 | 50 | System.out.println("🛒 Procesando pedidos..."); 51 | Future r1 = executor.submit(pedido1); 52 | Future r2 = executor.submit(pedido2); 53 | Future r3 = executor.submit(pedido3); 54 | 55 | System.out.println(r1.get()); 56 | System.out.println(r2.get()); 57 | System.out.println(r3.get()); 58 | 59 | executor.shutdown(); 60 | System.out.println("✅ Todos los pedidos fueron procesados."); 61 | } 62 | } 63 | ``` 64 | 65 | --- 66 | 67 | ## 🧪 Resultado esperado 68 | 69 | ``` 70 | 🛒 Procesando pedidos... 71 | 📦 Pedido #1 entregado en 1.2 segundos 72 | 📦 Pedido #2 entregado en 0.8 segundos 73 | 📦 Pedido #3 entregado en 1.5 segundos 74 | ✅ Todos los pedidos fueron procesados. 75 | ``` 76 | 77 | > ⚠️ El orden puede variar dependiendo de los tiempos de ejecución de cada tarea. 78 | 79 | --- 80 | 81 | ## 🔍 Conceptos clave utilizados 82 | 83 | | Concepto | Descripción | 84 | |--------------------|-------------| 85 | | `ExecutorService` | Gestiona un conjunto de hilos reutilizables | 86 | | `Callable` | Tarea que puede devolver un resultado y lanzar excepciones | 87 | | `Future` | Objeto que representa el resultado futuro de una tarea | 88 | | `submit()` | Envía la tarea al pool de hilos | 89 | | `get()` | Espera y obtiene el resultado de una tarea | 90 | | `shutdown()` | Detiene el pool de hilos tras finalizar las tareas | 91 | 92 | --- 93 | 94 | ## 📝 En resumen 95 | 96 | - **`ExecutorService`** permite **gestionar un conjunto de hilos reutilizables** para procesar múltiples tareas en paralelo, sin necesidad de crear y manejar hilos manualmente. 97 | - **`Callable`** y **`Future`** nos permiten **ejecutar tareas concurrentes que devuelven resultados** y manejan excepciones. 98 | - **El flujo de trabajo típico** incluye: 99 | 1. Definir las tareas (`Callable`). 100 | 2. Enviarlas al **pool de hilos** con `submit()`. 101 | 3. Obtener los resultados con `Future.get()`. 102 | 4. Cerrar el pool con `shutdown()`. 103 | - Este enfoque es ideal para **procesar pedidos o tareas en paralelo**, mejorando el rendimiento en **aplicaciones concurrentes**. 104 | 105 | 106 | --- 107 | 108 | ## 💡 ¿Sabías que...? 109 | 110 | - Usar `ExecutorService` es más eficiente que crear hilos manualmente, especialmente en aplicaciones con muchas tareas pequeñas. 111 | - Puedes cambiar `newFixedThreadPool(3)` por `newCachedThreadPool()` para una gestión dinámica del número de hilos. 112 | - `Callable` y `Future` te permiten recibir datos de las tareas, a diferencia de `Runnable`. 113 | 114 | --- 115 | 116 | 📘 Recursos útiles: 117 | 🔗 [Java Executors – Oracle Docs](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) 118 | 🔗 [Concurrencia moderna en Java – Baeldung](https://www.baeldung.com/java-executor-service-tutorial) 119 | 120 | --- 121 | 122 | ⬅️ [**Anterior**](../Ejemplo-01/Readme.md) | [**Siguiente**](../Reto-01/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-02/Ejemplo-03/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 02**](../Readme.md) ➡️ / 📝 `Ejemplo 03: Sincronización con synchronized y Locks` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Entender cómo evitar errores de concurrencia (como condiciones de carrera) en programas multihilo usando `synchronized` y estructuras modernas de bloqueo como `ReentrantLock`. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - IntelliJ IDEA o cualquier editor compatible con Java 13 | - Conocimientos previos de programación concurrente (`Runnable`, `ExecutorService`) 14 | 15 | --- 16 | 17 | ## 🧠 Contexto del ejemplo 18 | 19 | En entornos multihilo, varios hilos pueden intentar **modificar al mismo tiempo una misma variable o recurso**, lo que genera inconsistencias. En este ejemplo simulamos una cuenta bancaria compartida, donde múltiples hilos intentan retirar dinero a la vez. 20 | 21 | Veremos cómo proteger ese acceso usando dos enfoques: 22 | 23 | 1. `synchronized` (bloqueo implícito de objetos) 24 | 2. `ReentrantLock` (bloqueo explícito más flexible) 25 | 26 | --- 27 | 28 | ## 🧱 Paso 1: Simulación con `synchronized` 29 | 30 | ### 📄 `CuentaBancaria.java` 31 | 32 | ```java 33 | public class CuentaBancaria { 34 | private int saldo = 1000; 35 | 36 | public synchronized void retirar(String nombre, int cantidad) { 37 | if (saldo >= cantidad) { 38 | System.out.println(nombre + " está retirando $" + cantidad); 39 | saldo -= cantidad; 40 | System.out.println(nombre + " completó el retiro. Saldo restante: $" + saldo); 41 | } else { 42 | System.out.println(nombre + " no pudo retirar: fondos insuficientes."); 43 | } 44 | } 45 | 46 | public int getSaldo() { 47 | return saldo; 48 | } 49 | } 50 | ``` 51 | 52 | --- 53 | 54 | ## 🚀 Paso 2: Clase principal con múltiples hilos 55 | 56 | ### 📄 `Main.java` 57 | 58 | ```java 59 | public class Main { 60 | public static void main(String[] args) { 61 | CuentaBancaria cuenta = new CuentaBancaria(); 62 | 63 | Runnable tarea = () -> { 64 | String nombreHilo = Thread.currentThread().getName(); 65 | cuenta.retirar(nombreHilo, 300); 66 | }; 67 | 68 | Thread t1 = new Thread(tarea, "Astronauta-1"); 69 | Thread t2 = new Thread(tarea, "Astronauta-2"); 70 | Thread t3 = new Thread(tarea, "Astronauta-3"); 71 | 72 | t1.start(); 73 | t2.start(); 74 | t3.start(); 75 | } 76 | } 77 | ``` 78 | 79 | --- 80 | 81 | ## 🧪 Resultado esperado (usando `synchronized`) 82 | 83 | ``` 84 | Astronauta-1 está retirando $300 85 | Astronauta-1 completó el retiro. Saldo restante: $700 86 | Astronauta-2 está retirando $300 87 | Astronauta-2 completó el retiro. Saldo restante: $400 88 | Astronauta-3 está retirando $300 89 | Astronauta-3 completó el retiro. Saldo restante: $100 90 | ``` 91 | 92 | > ⚠️ Sin `synchronized`, los hilos podrían modificar el saldo al mismo tiempo y crear un saldo incorrecto. 93 | 94 | --- 95 | 96 | ## 🔁 Alternativa: `ReentrantLock` 97 | 98 | Puedes modificar `CuentaBancaria` para usar esta estructura de bloqueo más avanzada: 99 | 100 | ```java 101 | import java.util.concurrent.locks.ReentrantLock; 102 | 103 | public class CuentaBancaria { 104 | private int saldo = 1000; 105 | private final ReentrantLock lock = new ReentrantLock(); 106 | 107 | public void retirar(String nombre, int cantidad) { 108 | lock.lock(); 109 | try { 110 | if (saldo >= cantidad) { 111 | System.out.println(nombre + " está retirando $" + cantidad); 112 | saldo -= cantidad; 113 | System.out.println(nombre + " completó el retiro. Saldo restante: $" + saldo); 114 | } else { 115 | System.out.println(nombre + " no pudo retirar: fondos insuficientes."); 116 | } 117 | } finally { 118 | lock.unlock(); // ⚠️ ¡Siempre liberar el lock! 119 | } 120 | } 121 | 122 | public int getSaldo() { 123 | return saldo; 124 | } 125 | } 126 | ``` 127 | 128 | --- 129 | 130 | 131 | ### 🧪 Resultado esperado (usando `ReentrantLock`) 132 | 133 | La salida será muy similar a la de `synchronized`, pero ahora con control explícito del bloqueo: 134 | 135 | ``` 136 | Astronauta-1 está retirando $300 137 | Astronauta-1 completó el retiro. Saldo restante: $700 138 | Astronauta-3 está retirando $300 139 | Astronauta-3 completó el retiro. Saldo restante: $400 140 | Astronauta-2 está retirando $300 141 | Astronauta-2 completó el retiro. Saldo restante: $100 142 | ``` 143 | 144 | > ✅ El orden puede variar, pero el **control de acceso al recurso compartido está garantizado**, sin condiciones de carrera. 145 | 146 | --- 147 | 148 | ## 🔍 Conceptos clave utilizados 149 | 150 | | Concepto | Descripción | 151 | |-----------------|-------------| 152 | | `synchronized` | Bloquea automáticamente el objeto al entrar al método | 153 | | `ReentrantLock` | Permite controlar manualmente el bloqueo/desbloqueo, y usar condiciones | 154 | | `Thread.currentThread().getName()` | Permite identificar el hilo activo | 155 | 156 | --- 157 | 158 | ## 📝 En resumen 159 | 160 | - Las **condiciones de carrera** ocurren cuando varios **hilos acceden y modifican recursos compartidos sin control**, lo que puede provocar inconsistencias en los datos. 161 | - Para prevenirlas, usamos **mecanismos de sincronización** como: 162 | - **`synchronized`** → Bloqueo implícito más simple. 163 | - **`ReentrantLock`** → Bloqueo explícito que permite **mayor control** (como tiempo de espera, desbloqueo manual). 164 | - Ambos enfoques garantizan que **solo un hilo a la vez acceda a la sección crítica**, pero **ReentrantLock** es más flexible en sistemas complejos. 165 | - Este ejemplo muestra cómo proteger operaciones sensibles (como **retiros de una cuenta bancaria**) para asegurar **consistencia y seguridad** en entornos multihilo. 166 | 167 | 168 | --- 169 | 170 | ## 💡 ¿Sabías que...? 171 | 172 | - Usar `synchronized` es más simple, pero `ReentrantLock` da mayor control (por ejemplo, tiempo de espera, interrupción). 173 | - Las condiciones de carrera son uno de los errores más difíciles de depurar en sistemas concurrentes. 174 | - Java también ofrece estructuras seguras como `AtomicInteger`, `ConcurrentHashMap` y `Semaphore`. 175 | 176 | --- 177 | 178 | 📘 Recursos adicionales: 179 | 🔗 [Bloqueo con synchronized – Oracle](https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html) 180 | 🔗 [ReentrantLock – Java Docs](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html) 181 | 182 | --- 183 | 184 | ⬅️ [**Anterior**](../Reto-01/Readme.md) | [**Siguiente**](../Reto-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-02/Imagenes/S02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-02/Imagenes/S02.jpg -------------------------------------------------------------------------------- /Sesion-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../Readme.md) ➡️ / 📖 `Sesión 02` 2 | 3 |
4 | Sesion_02 5 |
6 | 7 | # 🎯 Objetivo 8 | 9 | ⚒️ Comprender los fundamentos de la programación concurrente en Java, conocer los distintos mecanismos para crear y sincronizar hilos, y aplicar buenas prácticas para evitar errores comunes como condiciones de carrera o bloqueos. 10 | 11 | --- 12 | 13 | 📘 Material del prework: 14 | Antes de comenzar con los ejercicios de esta sesión, recordemos que en el material de prework hemos cubierto los fundamentos teóricos que aplicaremos hoy. A lo largo de esta sesión, pondremos en práctica estos conceptos mediante una serie de ejercicios y retos diseñados para reforzar y validar nuestro entendimiento. 15 | 🔥¡Vamos a comenzar!🔥 16 | 17 | --- 18 | 19 | ## 📂 Temas de la sesión... 20 | 21 | ### 📖 Introducción a la concurrencia en Java 22 | Entender qué es la programación concurrente, cómo se manejan los hilos y cuáles son los riesgos y beneficios de su uso. 23 | 24 | 🔹 **Conceptos de procesos e hilos** 25 | 🔹 **Ciclo de vida de un hilo** 26 | 🔹 **Ventajas y riesgos del multihilo** 27 | 28 | 📜 **[Ejemplo 01: Introducción a hilos con Runnable](Ejemplo-01/Readme.md)** 29 | 30 | --- 31 | 32 | ### 📖 Creación de hilos en Java 33 | Aprender a implementar hilos en Java usando interfaces clave y servicios que permiten ejecutar tareas concurrentes. 34 | 35 | 🔹 **Uso de `Thread` y `Runnable`** 36 | 🔹 **`ExecutorService` y `Callable`** 37 | 🔹 **Manejo de resultados con `Future`** 38 | 39 | 📜 **[Ejemplo 02: ExecutorService y tareas concurrentes](Ejemplo-02/Readme.md)** 40 | 🔥 **[Reto 01: Simulación concurrente de sistemas en una misión espacial](Reto-01/Readme.md)** 41 | 42 | --- 43 | 44 | ### 📖 Sincronización de hilos 45 | Evitar errores comunes en entornos multihilo mediante técnicas de sincronización y bloqueo de recursos compartidos. 46 | 47 | 🔹 **Condiciones de carrera y uso de `synchronized`** 48 | 🔹 **Comunicación con `wait()` y `notify()`** 49 | 🔹 **Alternativas modernas: `Locks`, `ReentrantLock`** 50 | 51 | 📜 **[Ejemplo 03: Sincronización con synchronized y Locks](Ejemplo-03/Readme.md)** 52 | 🔥 **[Reto 02: Acceso controlado a un recurso médico crítico con ReentrantLock](Reto-02/Readme.md)** 53 | 54 | --- 55 | 56 | ⬅️ [**Anterior**](../Sesion-01/Readme.md) | [**Siguiente**](../Sesion-03/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-02/Reto-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 02**](../Readme.md) ➡️ / ⚡ `Reto 01: Simulación concurrente de sistemas en una misión espacial` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Simular el comportamiento paralelo de varios subsistemas críticos durante una misión espacial utilizando programación concurrente con `Runnable`, `Callable`, `ExecutorService` y `Future`. 6 | 7 | --- 8 | 9 | ## 🧠 Contexto del reto 10 | 11 | Durante una misión aeroespacial, múltiples sistemas trabajan **simultáneamente** para garantizar el éxito y la seguridad de la operación. En este reto, representarás 4 subsistemas: 12 | 13 | 1. 🛰️ **Sistema de navegación** – Calcula trayectoria y correcciones orbitales. 14 | 2. 🧪 **Sistema de soporte vital** – Monitorea presión, oxígeno y condiciones internas. 15 | 3. 🔥 **Sistema de control térmico** – Supervisa temperaturas internas y externas. 16 | 4. 📡 **Sistema de comunicaciones** – Establece contacto con la estación terrestre. 17 | 18 | Cada uno se ejecutará como una **tarea independiente en un hilo concurrente**. 19 | 20 | --- 21 | 22 | ## 📝 Instrucciones 23 | 24 | ### Parte 1️⃣: Crear clases que simulen subsistemas 25 | 26 | - Crea una clase por cada sistema que implemente `Callable`. 27 | - Simula el procesamiento con `Thread.sleep()` y devuelve un mensaje representando el estado del sistema. 28 | 29 | ```java 30 | public class SistemaNavegacion implements Callable { 31 | public String call() throws Exception { 32 | Thread.sleep(1000); 33 | return "🛰️ Navegación: trayectoria corregida con éxito."; 34 | } 35 | } 36 | ``` 37 | 38 | ### Parte 2️⃣: Ejecutar tareas con `ExecutorService` 39 | 40 | - Usa `Executors.newFixedThreadPool(4)` 41 | - Envía las tareas con `submit()` 42 | - Recupera los resultados con `Future.get()` 43 | 44 | ```java 45 | ExecutorService executor = Executors.newFixedThreadPool(4); 46 | Future nav = executor.submit(new SistemaNavegacion()); 47 | // ...otros sistemas 48 | System.out.println(nav.get()); 49 | ``` 50 | 51 | ### Parte 3️⃣: Mostrar los resultados al finalizar 52 | 53 | Asegúrate de imprimir todos los resultados y cerrar el executor con `shutdown()`. 54 | 55 | --- 56 | 57 | ## 🧪 Resultado esperado 58 | 59 | ``` 60 | 🚀 Simulación de misión espacial iniciada... 61 | 📡 Comunicaciones: enlace con estación terrestre establecido. 62 | 🧪 Soporte vital: presión y oxígeno dentro de parámetros normales. 63 | 🔥 Control térmico: temperatura estable (22°C). 64 | 🛰️ Navegación: trayectoria corregida con éxito. 65 | ✅ Todos los sistemas reportan estado operativo. 66 | ``` 67 | 68 | > 🧠 Recuerda que el orden de salida puede variar por los tiempos de procesamiento simulados. 69 | 70 | --- 71 | 72 | 73 | ## 📘 Recursos útiles 74 | 75 | - 🔗 [ExecutorService – Oracle Docs](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) 76 | - 🔗 [Callable & Future en Java](https://www.geeksforgeeks.org/callable-future-java/) 77 | 78 | --- 79 | 80 | 🏆 Si logras simular correctamente los 4 sistemas trabajando de forma paralela y mostrar los resultados correctamente, ¡reto completado con éxito! 81 | 82 | --- 83 | 84 | ⬅️ [**Anterior**](../Ejemplo-02/Readme.md) | [**Siguiente**](../Ejemplo-03/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-02/Reto-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 02**](../Readme.md) ➡️ / ⚡ `Reto 02: Acceso controlado a un recurso médico crítico con ReentrantLock` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Simular una situación hospitalaria donde múltiples profesionales médicos necesitan acceder a un recurso crítico (como una sala de cirugía), aplicando sincronización con `ReentrantLock` para evitar condiciones de carrera y garantizar la integridad del sistema. 6 | 7 | --- 8 | 9 | ## 🧠 Contexto del reto 10 | 11 | En hospitales, algunos recursos como quirófanos, equipos de resonancia magnética o camas de cuidados intensivos, solo pueden ser usados por **un profesional a la vez**. Este reto representa esa situación utilizando múltiples hilos que intentan acceder a un mismo recurso compartido. 12 | 13 | --- 14 | 15 | ## 📝 Instrucciones 16 | 17 | ### 1️⃣ Crear una clase `RecursoMedico` 18 | 19 | - Debe tener un atributo `String nombre` que represente el recurso (ej. "Sala de cirugía"). 20 | - Implementa un método `usar(String profesional)` que simule: 21 | - La entrada de un profesional al recurso 22 | - El tiempo de uso (puede usar `Thread.sleep()`) 23 | - La salida del recurso 24 | - Usa un `ReentrantLock` para asegurar que solo un hilo acceda al recurso a la vez. 25 | 26 | --- 27 | 28 | ### 2️⃣ Crear tareas que representen a profesionales médicos 29 | 30 | - Implementa varias clases o lambdas que usen `Runnable`. 31 | - Cada una representa a un médico o enfermero intentando usar el recurso médico. 32 | - El nombre del hilo debe indicar quién accede (ej. `"Dra. Sánchez"`). 33 | 34 | --- 35 | 36 | ### 3️⃣ Ejecutar la simulación 37 | 38 | - Usa un `ExecutorService` con al menos 4 hilos para simular concurrencia. 39 | - Ejecuta todas las tareas e imprime en consola el flujo de uso del recurso. 40 | 41 | --- 42 | 43 | ## 🧪 Resultado esperado 44 | 45 | ``` 46 | 🏥 Iniciando acceso a la Sala de cirugía... 47 | 👩‍⚕️ Dra. Sánchez ha ingresado a Sala de cirugía 48 | ✅ Dra. Sánchez ha salido de Sala de cirugía 49 | 👨‍⚕️ Dr. Gómez ha ingresado a Sala de cirugía 50 | ✅ Dr. Gómez ha salido de Sala de cirugía 51 | ... 52 | ``` 53 | 54 | > 🔒 El acceso debe estar completamente sincronizado para evitar que dos profesionales usen el recurso al mismo tiempo. 55 | 56 | --- 57 | 58 | 📘 Recursos útiles: 59 | 60 | - 🔗 [ReentrantLock – Oracle Docs](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html) 61 | - 🔗 [Java Threads & Concurrency – Baeldung](https://www.baeldung.com/java-concurrent-locks) 62 | 63 | --- 64 | 65 | 🏆 Si logras simular correctamente el acceso exclusivo al recurso médico por parte de múltiples profesionales, ¡reto completado con éxito! 66 | 67 | --- 68 | 69 | ⬅️ [**Anterior**](../Ejemplo-03/Readme.md) | [**Siguiente**](../../Sesion-03/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-03/Ejemplo-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 03**](../Readme.md) ➡️ / 📝 `Ejemplo 01: Uso de lambdas y funciones puras` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Comprender los fundamentos de la programación funcional en Java mediante el uso de expresiones lambda, funciones puras y `Predicate`, con un enfoque práctico basado en un escenario de evaluación de pacientes en un entorno médico. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - Editor compatible con Java (IntelliJ IDEA, VS Code, NetBeans, etc.) 13 | - Conocimientos previos sobre clases, objetos y colecciones en Java 14 | 15 | --- 16 | 17 | ## 🧠 Contexto del ejemplo 18 | 19 | Imagina que eres parte de un sistema médico que debe **filtrar y evaluar pacientes** con base en su edad o condición. En lugar de escribir múltiples `if/else`, puedes aplicar **lógica funcional** para filtrar listas, definir criterios reutilizables y mantener el código limpio y expresivo. 20 | 21 | --- 22 | 23 | ## 🧱 Paso 1: Crear la clase `Paciente` 24 | 25 | ```java 26 | public class Paciente { 27 | 28 | // Variables globales 29 | private final String nombre; 30 | private final int edad; 31 | private final boolean enObservacion; 32 | 33 | // Constructor 34 | public Paciente(String nombre, int edad, boolean enObservacion) { 35 | this.nombre = nombre; 36 | this.edad = edad; 37 | this.enObservacion = enObservacion; 38 | } 39 | 40 | // Métodos get 41 | public String getNombre() { return nombre; } 42 | public int getEdad() { return edad; } 43 | public boolean isEnObservacion() { return enObservacion; } 44 | 45 | // Método toString 46 | @Override 47 | public String toString() { 48 | return nombre + " (Edad: " + edad + ", Observación: " + enObservacion + ")"; 49 | } 50 | } 51 | ``` 52 | 53 | --- 54 | 55 | ## 🧱 Paso 2: Evaluar pacientes con funciones funcionales 56 | 57 | ```java 58 | import java.util.*; 59 | import java.util.function.*; 60 | import java.util.stream.Collectors; 61 | 62 | public class EvaluadorPacientes { 63 | 64 | public static void main(String[] args) { 65 | // Lista de pacientes simulada 66 | List pacientes = List.of( 67 | new Paciente("Ana", 34, false), 68 | new Paciente("Luis", 70, true), 69 | new Paciente("Marta", 45, true), 70 | new Paciente("Pedro", 28, false) 71 | ); 72 | 73 | // ✅ Lambda: Predicate para pacientes mayores de 60 74 | Predicate mayoresDe60 = p -> p.getEdad() > 60; 75 | 76 | // ✅ Method reference: Predicate para pacientes en observación 77 | Predicate enObservacion = Paciente::isEnObservacion; 78 | 79 | // ✅ Composición funcional con Predicate.and() 80 | Predicate casoCritico = mayoresDe60.and(enObservacion); 81 | 82 | System.out.println("🩺 Pacientes en estado crítico:"); 83 | 84 | // ✅ Uso de stream para recorrer la lista de pacientes 85 | pacientes.stream() // ← Stream inicia aquí 86 | .filter(casoCritico) // ← filter aplica Predicate 87 | .forEach(System.out::println); // ← forEach aplica método por referencia 88 | 89 | // ✅ Function: transforma un Paciente en un String resumen 90 | Function resumen = p -> 91 | "🧾 Paciente: " + p.getNombre() + " | Edad: " + p.getEdad(); 92 | 93 | System.out.println("\n📋 Resumen general:"); 94 | 95 | pacientes.stream() // ← Stream API 96 | .map(resumen) // ← map aplica Function 97 | .forEach(System.out::println); // ← Acción final (output en consola) 98 | } 99 | } 100 | ``` 101 | 102 | --- 103 | 104 | ¡Claro, Mario! Aquí tienes la sección que puedes agregar: 105 | 106 | --- 107 | 108 | 📘 **Nota:** 109 | En este ejemplo utilizamos `Stream API` como **vehículo** para aplicar lambdas e interfaces funcionales como `Predicate` y `Function`. Sin embargo, **profundizaremos formalmente en `Stream API`** (operaciones intermedias, terminales, composición) en el siguiente ejercicio. 110 | 111 | --- 112 | 113 | 114 | ## 🧪 Resultado esperado 115 | 116 | ``` 117 | 🩺 Pacientes en estado crítico: 118 | Luis (Edad: 70, Observación: true) 119 | 120 | 📋 Resumen general: 121 | 🧾 Paciente: Ana | Edad: 34 122 | 🧾 Paciente: Luis | Edad: 70 123 | 🧾 Paciente: Marta | Edad: 45 124 | 🧾 Paciente: Pedro | Edad: 28 125 | ``` 126 | 127 | --- 128 | 129 | 130 | ### 🔍 Elementos funcionales utilizados 131 | 132 | | Elemento | Descripción | 133 | |------------------|-------------| 134 | | `Predicate` | Evalúa una condición booleana sobre un objeto (usado para filtrar pacientes) | 135 | | `Function` | Transforma un objeto T en otro tipo R (para crear mensajes o resúmenes) | 136 | | `Stream` | Flujo de datos para aplicar operaciones funcionales sobre colecciones | 137 | | `filter()` | Operación intermedia que filtra elementos según una condición (`Predicate`) | 138 | | `map()` | Transforma cada elemento en otro usando una función (`Function`) | 139 | | `forEach()` | Operación terminal que ejecuta una acción sobre cada elemento del stream | 140 | | `Lambda` | Funciones anónimas usadas como argumentos para interfaces funcionales | 141 | | `Method reference` | Notación `::` para pasar métodos existentes como lambdas (`Paciente::isEnObservacion`) | 142 | 143 | 144 | --- 145 | 146 | ## 📝 En resumen 147 | 148 | - **Programación funcional** en Java permite **escribir código más expresivo y seguro**, evitando estructuras tradicionales como `if/else` o ciclos explícitos. 149 | - Usamos **`Predicate`** para **filtrar datos** (ej. pacientes mayores de 60 y en observación) de manera **reutilizable** y **componible**. 150 | - Usamos **`Function`** para **transformar objetos** en otros formatos, como **resúmenes en texto**. 151 | - **Streams** actúan como un **vehículo** para aplicar estas funciones sobre **colecciones de datos**, permitiendo encadenar operaciones como **`filter()`**, **`map()`** y **`forEach()`** de forma fluida y legible. 152 | - Esta aproximación mejora la **legibilidad**, **reduce errores** y facilita **pruebas unitarias**. 153 | 154 | 155 | --- 156 | 157 | ### 💡 ¿Sabías que...? 158 | 159 | - Las **funciones puras** no modifican variables externas ni dependen de estados mutables. 160 | - Puedes combinar múltiples `Predicate` con `.and()`, `.or()` y `.negate()` para lógica más expresiva. 161 | - Programación funcional reduce errores, mejora la legibilidad y facilita pruebas unitarias. 162 | 163 | --- 164 | 165 | 📘 Recursos adicionales: 166 | 167 | - 🔗 [Interfaces funcionales – Java Docs](https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html) 168 | - 🔗 [Programación funcional en Java – Baeldung](https://www.baeldung.com/java-functional-programming) 169 | 170 | --- 171 | 172 | ⬅️ [**Anterior**](../Readme.md) | [**Siguiente**](../Ejemplo-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-03/Ejemplo-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 03**](../Readme.md) ➡️ / 📝 `Ejemplo 02: Uso de Optional y transformaciones con Stream` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Comprender el funcionamiento de `Stream API` en Java, utilizando operaciones como `map`, `filter`, `forEach` y el manejo seguro de valores nulos con `Optional`. Aplicaremos estas técnicas para transformar y procesar listas de objetos de forma funcional y segura. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - Editor compatible con Java (IntelliJ IDEA, VS Code, etc.) 13 | - Conocimientos previos de programación funcional básica (`Predicate`, `Function`, lambdas) 14 | 15 | --- 16 | 17 | ## 🧠 Introducción a `Stream API` y `Optional` 18 | 19 | ### 🔹 ¿Qué es `Stream API`? 20 | 21 | `Stream API` permite procesar **colecciones de datos (listas, sets, etc.)** de manera **declarativa y funcional**, mediante una secuencia de operaciones: 22 | 23 | - **Operaciones intermedias**: transforman o filtran los elementos del stream (`map`, `filter`, etc.) 24 | - **Operaciones terminales**: consumen el stream y producen un resultado (`forEach`, `collect`, etc.) 25 | 26 | > 📘 *Un stream **no almacena datos**, sino que procesa los elementos de la colección en forma de flujo.* 27 | 28 | --- 29 | 30 | ### 🔹 ¿Qué es `Optional`? 31 | 32 | `Optional` es un **contenedor de valor opcional**, diseñado para **evitar el uso de `null`** y manejar posibles valores ausentes de manera segura. 33 | 34 | - Permite **encadenar operaciones** como `.map()`, `.filter()`, `.orElse()`. 35 | - Evita excepciones como `NullPointerException`. 36 | 37 | --- 38 | 39 | ## 🧱 Paso 1: Ampliar la clase `Paciente` para uso con `Optional` 40 | 41 | ```java 42 | public class Paciente { 43 | private final String nombre; 44 | private final int edad; 45 | private final boolean enObservacion; 46 | private final String correo; // Puede ser nulo 47 | 48 | public Paciente(String nombre, int edad, boolean enObservacion, String correo) { 49 | this.nombre = nombre; 50 | this.edad = edad; 51 | this.enObservacion = enObservacion; 52 | this.correo = correo; 53 | } 54 | 55 | public String getNombre() { return nombre; } 56 | public int getEdad() { return edad; } 57 | public boolean isEnObservacion() { return enObservacion; } 58 | 59 | // 📧 Método que devuelve Optional para evitar NullPointerException 60 | public Optional getCorreo() { 61 | return Optional.ofNullable(correo); 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return nombre + " (Edad: " + edad + ")"; 67 | } 68 | } 69 | ``` 70 | 71 | --- 72 | 73 | ## 🧱 Paso 2: Uso de `Stream API` y `Optional` 74 | 75 | ```java 76 | import java.util.*; 77 | import java.util.stream.*; 78 | 79 | public class EvaluadorPacientesOptional { 80 | 81 | public static void main(String[] args) { 82 | List pacientes = List.of( 83 | new Paciente("Ana", 34, false, "ana@example.com"), 84 | new Paciente("Luis", 70, true, null), 85 | new Paciente("Marta", 45, true, "marta@example.com"), 86 | new Paciente("Pedro", 28, false, null) 87 | ); 88 | 89 | System.out.println("📧 Correos electrónicos disponibles:"); 90 | 91 | // 🏁 Iniciamos el stream sobre la lista de pacientes 92 | pacientes.stream() 93 | .map(Paciente::getCorreo) // 🔄 map transforma Paciente → Optional (correo) 94 | .filter(Optional::isPresent) // 🔍 filter permite solo los Optionals que tienen valor (no vacíos) 95 | .map(Optional::get) // 📥 map extrae el valor del Optional 96 | .forEach(System.out::println); // 📤 forEach imprime los valores finales 97 | 98 | System.out.println("\n📝 Pacientes en observación (mayores de 40 años):"); 99 | 100 | pacientes.stream() 101 | .filter(p -> p.isEnObservacion() && p.getEdad() > 40) // 🔍 filter aplica condición booleana 102 | .forEach(System.out::println); // 📤 forEach imprime los pacientes filtrados 103 | } 104 | } 105 | ``` 106 | 107 | --- 108 | 109 | ## 🧪 Resultado esperado 110 | 111 | ``` 112 | 📧 Correos electrónicos disponibles: 113 | ana@example.com 114 | marta@example.com 115 | 116 | 📝 Pacientes en observación (mayores de 40 años): 117 | Luis (Edad: 70) 118 | Marta (Edad: 45) 119 | ``` 120 | 121 | --- 122 | 123 | ## 🔍 Operaciones explicadas en `Stream API` 124 | 125 | | Operación | Tipo | Descripción | 126 | |-----------------|--------------|-------------| 127 | | `stream()` | Inicialización | Inicia el flujo de datos desde la colección | 128 | | `map()` | Intermedia | Transforma cada elemento en otro tipo (ej. de `Paciente` a `Optional`) | 129 | | `filter()` | Intermedia | Filtra elementos según una condición booleana | 130 | | `forEach()` | Terminal | Ejecuta una acción sobre cada elemento (ej. imprimir) | 131 | 132 | --- 133 | 134 | ## 📝 En resumen 135 | 136 | - **`Stream API`** te permite procesar colecciones de forma **fluida y declarativa**, evitando ciclos manuales. 137 | - Usamos **`map()`** para **transformar datos**, por ejemplo, de `Paciente` a `Optional` (correo electrónico). 138 | - **`filter()`** nos permite **seleccionar solo los elementos que cumplen ciertas condiciones**, como pacientes en observación o correos disponibles. 139 | - **`Optional`** ayuda a **manejar valores nulos de forma segura**, eliminando la necesidad de verificaciones explícitas con `null`. 140 | - Este enfoque **simplifica la lógica**, mejora la **legibilidad del código** y **reduce errores** como el temido `NullPointerException`. 141 | 142 | --- 143 | 144 | ### 💡 ¿Sabías que...? 145 | 146 | - Puedes encadenar operaciones intermedias (`map`, `filter`) para transformar y filtrar colecciones sin modificar la colección original. 147 | - Las operaciones en un `Stream` son **perezosas**: solo se ejecutan cuando se llega a una **operación terminal** como `forEach` o `collect`. 148 | - `Optional` es una **herramienta poderosa** para eliminar el riesgo de `null`, permitiendo encadenar operaciones sin preocuparte por excepciones inesperadas. 149 | 150 | --- 151 | 152 | 📘 Recursos adicionales: 153 | 154 | - 🔗 [Optional – Java Docs](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) 155 | - 🔗 [Stream API – Baeldung](https://www.baeldung.com/java-8-streams) 156 | 157 | --- 158 | 159 | ⬅️ [**Anterior**](../Ejemplo-01/Readme.md) | [**Siguiente**](../Reto-01/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-03/Ejemplo-03/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 03**](../Readme.md) ➡️ / 📝 `Ejemplo 03: Composición funcional con flatMap y Function` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Comprender cómo **componer funciones** en Java, aplicando `flatMap` y encadenando transformaciones funcionales para procesar estructuras más complejas (como listas anidadas) de forma fluida y segura. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - Editor compatible con Java (IntelliJ IDEA, VS Code, etc.) 13 | - Conocimientos previos de programación funcional básica (`Stream`, `Optional`, `map`, `filter`) 14 | 15 | --- 16 | 17 | ## 🧠 Contexto del ejemplo 18 | 19 | En una **pizzería** con varias sucursales, cada sucursal tiene su propia lista de **pedidos**. 20 | El objetivo es: 21 | 22 | - Acceder a **todas las listas de pedidos** (listas anidadas). 23 | - Filtrar los **pedidos para entrega a domicilio**. 24 | - Recuperar los **teléfonos disponibles** (sin `null`) para confirmar los pedidos. 25 | - Transformar los teléfonos en **mensajes personalizados**. 26 | 27 | Usaremos `flatMap` para **desanidar listas** y procesar todos los pedidos de todas las sucursales como un solo flujo. 28 | 29 | --- 30 | 31 | ## 🧱 Paso 1: Crear las clases `Pedido` y `Sucursal` 32 | 33 | ### 📄 `Pedido.java` 34 | 35 | ```java 36 | public class Pedido { 37 | private final String cliente; 38 | private final String tipoEntrega; // "domicilio" o "local" 39 | private final String telefono; // Puede ser null 40 | 41 | public Pedido(String cliente, String tipoEntrega, String telefono) { 42 | this.cliente = cliente; 43 | this.tipoEntrega = tipoEntrega; 44 | this.telefono = telefono; 45 | } 46 | 47 | public String getCliente(){ return cliente; } 48 | public String getTipoEntrega() { return tipoEntrega; } 49 | public Optional getTelefono() { return Optional.ofNullable(telefono); } 50 | } 51 | ``` 52 | 53 | ### 📄 `Sucursal.java` 54 | 55 | ```java 56 | import java.util.List; 57 | 58 | public class Sucursal { 59 | private final String nombre; 60 | private final List pedidos; 61 | 62 | public Sucursal(String nombre, List pedidos) { 63 | this.nombre = nombre; 64 | this.pedidos = pedidos; 65 | } 66 | 67 | public List getPedidos() { return pedidos; } 68 | public String getNombre() { return nombre; } 69 | } 70 | ``` 71 | 72 | --- 73 | 74 | ## 🧱 Paso 2: Procesar todas las sucursales con `flatMap` y `Function` 75 | 76 | ```java 77 | package com.bedu.pizzeria; 78 | 79 | import java.util.*; 80 | import java.util.stream.*; 81 | 82 | public class ConfirmacionPedidosGlobal { 83 | 84 | public static void main(String[] args) { 85 | // 📦 Lista de sucursales con sus pedidos 86 | List sucursales = List.of( 87 | new Sucursal("Centro", List.of( 88 | new Pedido("Juan", "domicilio", "555-1234"), 89 | new Pedido("María", "local", null) 90 | )), 91 | new Sucursal("Norte", List.of( 92 | new Pedido("Carlos", "domicilio", null), 93 | new Pedido("Luisa", "domicilio", "555-5678") 94 | )), 95 | new Sucursal("Sur", List.of( 96 | new Pedido("Esteban", "domicilio", "555-9012"), 97 | new Pedido("Diana", "domicilio", null) 98 | )), 99 | new Sucursal("Este", List.of( 100 | new Pedido("Andrés", "local", null), 101 | new Pedido("Sofía", "domicilio", "555-3456") 102 | )) 103 | ); 104 | 105 | System.out.println("📋 Confirmaciones de pedidos globales:"); 106 | 107 | // 🏁 Procesamos todos los pedidos de todas las sucursales 108 | sucursales.stream() 109 | .flatMap(sucursal -> 110 | sucursal.getPedidos().stream() 111 | .filter(p -> p.getTipoEntrega().equalsIgnoreCase("domicilio")) // 🔍 Filtrar entregas a domicilio 112 | .map(pedido -> new Confirmacion(pedido, sucursal.getNombre())) // 📝 Combinar pedido + sucursal 113 | ) 114 | .filter(conf -> conf.getPedido().getTelefono().isPresent()) // 🔍 Filtrar Optional con valor 115 | .map(conf -> { 116 | String telefono = conf.getPedido().getTelefono().get(); 117 | return "📞 Pedido de " + conf.getPedido().getCliente() + 118 | " en la sucursal " + conf.getSucursal() + 119 | " confirmado al número: " + telefono; 120 | }) 121 | .forEach(System.out::println); // 📤 Imprimir mensajes 122 | } 123 | 124 | // Clase auxiliar para transportar Pedido + Sucursal juntos 125 | static class Confirmacion { 126 | private final Pedido pedido; 127 | private final String sucursal; 128 | 129 | public Confirmacion(Pedido pedido, String sucursal) { 130 | this.pedido = pedido; 131 | this.sucursal = sucursal; 132 | } 133 | 134 | public Pedido getPedido() { return pedido; } 135 | public String getSucursal() { return sucursal; } 136 | } 137 | } 138 | 139 | ``` 140 | 141 | --- 142 | 143 | ## 🧪 Resultado esperado 144 | 145 | ``` 146 | 📋 Confirmaciones de pedidos globales: 147 | 📞 Pedido de Juan en la sucursal Centro confirmado al número: 555-1234 148 | 📞 Pedido de Luisa en la sucursal Norte confirmado al número: 555-5678 149 | 📞 Pedido de Esteban en la sucursal Sur confirmado al número: 555-9012 150 | 📞 Pedido de Sofía en la sucursal Este confirmado al número: 555-3456 151 | ``` 152 | 153 | --- 154 | 155 | ## 🔍 Elementos funcionales utilizados 156 | 157 | | Elemento | Descripción | 158 | |------------------|-------------| 159 | | `flatMap()` | **Desanida listas** (lista de listas → flujo plano) | 160 | | `Optional` | Maneja valores nulos de forma segura | 161 | | `map()` | Transforma elementos (ej. Pedido → Optional → String) | 162 | | `filter()` | Filtra elementos según una condición | 163 | | `collect()` | Recolecta los elementos del stream en una colección (ej. `List`) | 164 | 165 | --- 166 | 167 | ## 📝 En resumen 168 | 169 | - Usamos **`flatMap()`** para **desanidar estructuras** complejas (listas dentro de listas), procesando todos los pedidos de varias sucursales como un **flujo continuo**. 170 | - Combinamos **`filter()`**, **`map()`** y **`Optional`** para **filtrar entregas a domicilio**, **eliminar pedidos sin teléfono** y **crear mensajes personalizados**. 171 | - Esta composición funcional permite **trabajar con colecciones anidadas de forma limpia y fluida**, evitando ciclos anidados y estructuras imperativas. 172 | - **`flatMap()`** es especialmente útil en contextos como **pedidos globales**, **consultas a múltiples fuentes de datos** o **procesos con estructuras jerárquicas**. 173 | 174 | 175 | --- 176 | 177 | ### 💡 ¿Sabías que...? 178 | 179 | - `flatMap` es esencial para **trabajar con listas anidadas** o estructuras complejas, permitiendo procesarlas como un solo flujo continuo. 180 | - Puedes **componer funciones** encadenando `map`, `filter`, `flatMap`, generando pipelines declarativos y expresivos. 181 | 182 | --- 183 | 184 | 📘 Recursos adicionales: 185 | 186 | - 🔗 [flatMap en Java](https://www.geeksforgeeks.org/stream-flatmap-java-examples/) 187 | - 🔗 [Stream API – Oracle Docs](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html) 188 | 189 | --- 190 | 191 | ⬅️ [**Anterior**](../Reto-01/Readme.md) | [**Siguiente**](../Reto-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-03/Imagenes/S03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-03/Imagenes/S03.jpg -------------------------------------------------------------------------------- /Sesion-03/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../Readme.md) ➡️ / 📖 `Sesión 03` 2 | 3 |
4 | Sesion_03 5 |
6 | 7 | # 🎯 Objetivo 8 | 9 | ⚒️ Comprender los fundamentos de la programación funcional en Java, identificar las ventajas del uso de funciones puras, `Optional`, `Stream API` y composición funcional para escribir código más conciso, expresivo y menos propenso a errores. 10 | 11 | --- 12 | 13 | 📘 Material del prework: 14 | Antes de comenzar con los ejercicios de esta sesión, recordemos que en el material de prework hemos cubierto los fundamentos teóricos que aplicaremos hoy. A lo largo de esta sesión, pondremos en práctica estos conceptos mediante una serie de ejercicios y retos diseñados para reforzar y validar nuestro entendimiento. 15 | 🔥¡Vamos a comenzar!🔥 16 | 17 | --- 18 | 19 | ## 📂 Temas de la sesión... 20 | 21 | ### 📖 Fundamentos del paradigma funcional 22 | Introducción a la programación funcional y sus diferencias frente al paradigma imperativo tradicional. 23 | 24 | 🔹 **Funciones puras y estado inmutable** 25 | 🔹 **Expresiones lambda y referencias a métodos** 26 | 🔹 **Interfaces funcionales (`Predicate`, `Function`, `Consumer`, etc.)** 27 | 28 | 📜 **[Ejemplo 01: Uso de lambdas y funciones puras](Ejemplo-01/Readme.md)** 29 | 30 | --- 31 | 32 | ### 📖 Optional y Streams básicos 33 | Uso de clases e interfaces funcionales para evitar errores comunes como `NullPointerException` y facilitar operaciones con colecciones. 34 | 35 | 🔹 **Prevención de nulls con `Optional`** 36 | 🔹 **Operaciones con `Stream API`: `map`, `filter`, `forEach`** 37 | 🔹 **Operaciones terminales y composición de transformaciones** 38 | 39 | 📜 **[Ejemplo 02: Uso de Optional y transformaciones con Stream](Ejemplo-02/Readme.md)** 40 | 🔥 **[Reto 01: Confirmación segura de pedidos en una pizzería](Reto-01/Readme.md)** 41 | 42 | --- 43 | 44 | ### 📖 Composición funcional en Java 45 | Aprender a combinar funciones, transformar estructuras complejas y aplicar buenas prácticas para un código más declarativo. 46 | 47 | 🔹 **Encadenamiento de funciones** 48 | 🔹 **Uso de `flatMap` y composición avanzada** 49 | 🔹 **Buenas prácticas en programación funcional** 50 | 51 | 📜 **[Ejemplo 03: Composición funcional con flatMap y Function](Ejemplo-03/Readme.md)** 52 | 🔥 **[Reto 02: Cadena funcional para procesamiento de encuestas en una clínica](Reto-02/Readme.md)** 53 | 54 | --- 55 | 56 | ⬅️ [**Anterior**](../Sesion-02/Readme.md) | [**Siguiente**](../Sesion-04/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-03/Reto-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 03**](../Readme.md) ➡️ / ⚡ `Reto 01: Confirmación segura de pedidos en una pizzería` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Aplicar `Optional` y `Stream API` para filtrar y transformar una lista de pedidos de una pizzería, asegurando el manejo seguro de datos incompletos (como números telefónicos) y generando mensajes personalizados para confirmar los envíos. 6 | 7 | --- 8 | 9 | ## 🧠 Contexto del reto 10 | 11 | En una **pizzería**, algunos clientes han realizado **pedidos para entrega a domicilio**, pero **no todos han dejado su número de teléfono** para la confirmación. 12 | 13 | El sistema debe: 14 | 15 | - Filtrar **solo los pedidos que sean para entrega a domicilio**. 16 | - Recuperar y **utilizar solo los números de teléfono disponibles** (sin `null`). 17 | - Transformar cada número en un **mensaje de confirmación**. 18 | 19 | --- 20 | 21 | ## 📝 Instrucciones 22 | 23 | ### 1️⃣ Crear la clase `Pedido` 24 | 25 | - Atributos: 26 | - `cliente` (`String`) 27 | - `tipoEntrega` (`String`) → `"domicilio"` o `"local"` 28 | - `telefono` (`String`, puede ser `null`) 29 | - Implementar el método `getTelefono()` que devuelva un `Optional`. 30 | 31 | --- 32 | 33 | ### 2️⃣ Procesar la lista de pedidos usando `Stream API` 34 | 35 | 1. Filtrar **solo los pedidos con tipo de entrega `"domicilio"`**. 36 | 2. Recuperar los **teléfonos disponibles** usando `Optional`. 37 | 3. Transformar cada teléfono en un **mensaje de confirmación** como: 38 | 39 | ``` 40 | 📞 Confirmación enviada al número: 555-1234 41 | ``` 42 | 43 | 4. Mostrar todos los mensajes en consola. 44 | 45 | --- 46 | 47 | ## 💡 Ejemplo de salida esperada 48 | 49 | ``` 50 | 📞 Confirmación enviada al número: 555-1234 51 | 📞 Confirmación enviada al número: 555-5678 52 | ``` 53 | 54 | --- 55 | 56 | 📘 Recursos útiles: 57 | 58 | - 🔗 [Optional – Java Docs](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) 59 | - 🔗 [Stream API – Baeldung](https://www.baeldung.com/java-8-streams) 60 | 61 | --- 62 | 63 | 🏆 Si logras filtrar correctamente los pedidos de entrega a domicilio y generar los mensajes de confirmación sin errores, ¡reto completado con éxito! 64 | 65 | --- 66 | 67 | ⬅️ [**Anterior**](../Ejemplo-02/Readme.md) | [**Siguiente**](../Ejemplo-03/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-03/Reto-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 03**](../Readme.md) ➡️ / ⚡ `Reto 02: Cadena funcional para procesamiento de encuestas en una clínica` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Aplicar composición funcional en Java utilizando `Stream API` y `flatMap` para procesar listas anidadas (encuestas de distintas sucursales de una clínica), filtrar respuestas específicas y transformar los datos en mensajes útiles para el área de calidad. 6 | 7 | --- 8 | 9 | ## 🧠 Contexto del reto 10 | 11 | Una **clínica** recopila **encuestas de satisfacción de pacientes** en distintas sucursales. 12 | Cada encuesta incluye: 13 | 14 | - El **nombre del paciente**. 15 | - El **comentario** (puede ser `null` si no dejó uno). 16 | - La **calificación** (escala del 1 al 5). 17 | 18 | El **área de calidad** desea: 19 | 20 | - Filtrar **solo las encuestas con calificación menor o igual a 3** (pacientes insatisfechos). 21 | - Recuperar los **comentarios disponibles** (sin `null`) de esas encuestas. 22 | - Transformar cada comentario en un **mensaje de seguimiento** para la sucursal correspondiente. 23 | 24 | --- 25 | 26 | ## 📝 Instrucciones 27 | 28 | ### 1️⃣ Crear las clases `Encuesta` y `Sucursal` 29 | 30 | - **`Encuesta`**: 31 | - `paciente` (`String`) 32 | - `comentario` (`String`, puede ser `null`) 33 | - `calificacion` (`int`) 34 | 35 | Implementa un método `getComentario()` que devuelva un `Optional`. 36 | 37 | - **`Sucursal`**: 38 | - `nombre` (`String`) 39 | - Lista de **encuestas** (`List`) 40 | 41 | --- 42 | 43 | ### 2️⃣ Procesar las encuestas con `Stream API` y `flatMap` 44 | 45 | 1. Desanidar todas las encuestas de las sucursales usando `flatMap`. 46 | 2. Filtrar **solo las encuestas con calificación menor o igual a 3**. 47 | 3. Recuperar los **comentarios disponibles** usando `Optional`. 48 | 4. Transformar cada comentario en un mensaje: 49 | 50 | ``` 51 | Sucursal [Nombre]: Seguimiento a paciente con comentario: "" 52 | ``` 53 | 54 | 5. Mostrar todos los mensajes en consola. 55 | 56 | --- 57 | 58 | ## 💡 Ejemplo de salida esperada 59 | 60 | ``` 61 | Sucursal Centro: Seguimiento a paciente con comentario: "El tiempo de espera fue largo" 62 | Sucursal Norte: Seguimiento a paciente con comentario: "La atención fue buena, pero la limpieza puede mejorar" 63 | ``` 64 | 65 | --- 66 | 67 | 📘 Recursos útiles: 68 | 69 | - 🔗 [Stream API – Oracle Docs](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html) 70 | - 🔗 [Optional – Baeldung](https://www.baeldung.com/java-optional) 71 | 72 | --- 73 | 74 | 🏆 Si logras filtrar correctamente las encuestas insatisfechas y generar los mensajes de seguimiento, ¡reto completado con éxito! 75 | 76 | --- 77 | 78 | ⬅️ [**Anterior**](../Ejemplo-03/Readme.md) | [**Siguiente**](../../Sesion-04/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-04/Ejemplo-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 04**](../Readme.md) ➡️ / 📝 `Ejemplo 01: Diferencias entre concurrencia y asincronía` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Entender las diferencias entre **concurrencia** y **asincronía** en Java mediante un ejemplo práctico, visualizando cómo se comportan los hilos en cada caso y cuáles son los beneficios de cada enfoque. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - Editor compatible con Java (IntelliJ IDEA, VS Code, etc.) 13 | - Conocimientos previos de hilos (`Thread`, `ExecutorService`) 14 | 15 | --- 16 | 17 | ## 🧠 Contexto del ejemplo 18 | 19 | Imagina un **restaurante** donde se reciben **órdenes de comida**. 20 | Queremos comparar dos enfoques para procesar esas órdenes: 21 | 22 | - **Concurrencia**: múltiples **hilos** procesan las órdenes al mismo tiempo (bloqueante). 23 | - **Asincronía**: las órdenes se procesan de forma **no bloqueante**, liberando recursos mientras esperan, usando `CompletableFuture`. 24 | 25 | Este ejemplo mostrará cómo **ambos enfoques se comportan de manera distinta** frente a **latencia** (ej. tiempos de cocción). 26 | 27 | --- 28 | 29 | ## 🧱 Código de ejemplo 30 | 31 | ```java 32 | import java.util.concurrent.*; 33 | 34 | public class ProcesadorOrdenes { 35 | 36 | public static void main(String[] args) throws InterruptedException { 37 | System.out.println("🍽️ Ejecución concurrente con ExecutorService:"); 38 | ExecutorService executor = Executors.newFixedThreadPool(3); // Concurrencia: 3 hilos 39 | 40 | // 🔹 Concurrencia: más órdenes 41 | executor.submit(() -> procesarOrden("Orden 1 (Pizza)", 3)); 42 | executor.submit(() -> procesarOrden("Orden 2 (Pasta)", 2)); 43 | executor.submit(() -> procesarOrden("Orden 3 (Hamburguesa)", 4)); 44 | executor.submit(() -> procesarOrden("Orden 4 (Ensalada)", 1)); 45 | 46 | executor.shutdown(); 47 | executor.awaitTermination(10, TimeUnit.SECONDS); // Espera que terminen las órdenes concurrentes 48 | 49 | System.out.println("\n⚡ Ejecución asincrónica con CompletableFuture:"); 50 | 51 | // 🔹 Asincronía: más órdenes 52 | CompletableFuture ordenAsync1 = CompletableFuture.runAsync(() -> procesarOrden("Orden 5 (Sopa)", 2)); 53 | CompletableFuture ordenAsync2 = CompletableFuture.runAsync(() -> procesarOrden("Orden 6 (Tacos)", 3)); 54 | CompletableFuture ordenAsync3 = CompletableFuture.runAsync(() -> procesarOrden("Orden 7 (Sandwich)", 2)); 55 | 56 | CompletableFuture.allOf(ordenAsync1, ordenAsync2, ordenAsync3).join(); // Espera que todas las órdenes asincrónicas terminen 57 | } 58 | 59 | // Simula procesamiento de una orden con tiempo de espera (latencia) 60 | public static void procesarOrden(String orden, int segundos) { 61 | System.out.println("⏳ Procesando " + orden); 62 | try { 63 | TimeUnit.SECONDS.sleep(segundos); 64 | } catch (InterruptedException e) { 65 | Thread.currentThread().interrupt(); 66 | } 67 | System.out.println("✅ " + orden + " completada"); 68 | } 69 | } 70 | ``` 71 | 72 | --- 73 | 74 | ## 🧪 Resultado esperado 75 | 76 | ``` 77 | 🍽️ Ejecución concurrente con ExecutorService: 78 | ⏳ Procesando Orden 1 (Pizza) 79 | ⏳ Procesando Orden 2 (Pasta) 80 | ⏳ Procesando Orden 3 (Hamburguesa) 81 | ✅ Orden 2 (Pasta) completada 82 | ⏳ Procesando Orden 4 (Ensalada) 83 | ✅ Orden 1 (Pizza) completada 84 | ✅ Orden 4 (Ensalada) completada 85 | ✅ Orden 3 (Hamburguesa) completada 86 | 87 | ⚡ Ejecución asincrónica con CompletableFuture: 88 | ⏳ Procesando Orden 5 (Sopa) 89 | ⏳ Procesando Orden 6 (Tacos) 90 | ⏳ Procesando Orden 7 (Sandwich) 91 | ✅ Orden 5 (Sopa) completada 92 | ✅ Orden 7 (Sandwich) completada 93 | ✅ Orden 6 (Tacos) completada 94 | ``` 95 | > ⚠️ El orden de finalización de las órdenes puede variar en cada ejecución debido a la naturaleza concurrente y asincrónica de los hilos. 96 | 97 | > 💡 Ambos enfoques procesan en paralelo, pero **CompletableFuture** permite mayor **flexibilidad para componer tareas** y liberar hilos mientras esperan resultados. 98 | 99 | --- 100 | 101 | ## 🔍 Conceptos clave utilizados 102 | 103 | | Concepto | Descripción | 104 | |---------------------|-------------| 105 | | **Concurrencia** | Ejecución simultánea de tareas usando **hilos** (pueden ser bloqueantes). | 106 | | **Asincronía** | Ejecución **no bloqueante**, liberando recursos mientras se espera el resultado (con `CompletableFuture`). | 107 | | **ExecutorService** | Manejador de hilos que permite controlar la concurrencia. | 108 | | **CompletableFuture** | Herramienta para manejar asincronía, permitiendo componer, encadenar y manejar tareas no bloqueantes. | 109 | 110 | --- 111 | 112 | ## 📝 En resumen 113 | 114 | - **Concurrencia** y **asincronía** son técnicas complementarias, pero **resuelven problemas distintos**: 115 | - **Concurrencia** te permite ejecutar **varias tareas al mismo tiempo** usando múltiples hilos, pero puede implicar **bloqueos** (si las tareas esperan resultados). 116 | - **Asincronía** permite que las tareas **no bloqueen** el hilo mientras esperan (ej. tiempos de cocción, operaciones I/O), usando herramientas como **`CompletableFuture`**. 117 | 118 | - **¿Y cual elegir?** 119 | - Usa **concurrencia** cuando necesitas **multiprocesamiento** (ej. cálculos intensivos en varios núcleos). 120 | - Usa **asincronía** cuando quieres **liberar recursos mientras esperas** (ej. llamadas a servicios externos, operaciones de red o I/O). 121 | 122 | > 🔍 **Clave:** 123 | > **`ExecutorService`** controla **cuántos hilos se usan** (concurrencia). 124 | > **`CompletableFuture`** permite **componer tareas** sin bloquear hilos (asincronía). 125 | 126 | --- 127 | 128 | 📘 Recursos adicionales: 129 | 130 | - 🔗 [CompletableFuture – Java Docs](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) 131 | 132 | --- 133 | 134 | ⬅️ [**Anterior**](../Readme.md) | [**Siguiente**](../Ejemplo-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-04/Ejemplo-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 04**](../Readme.md) ➡️ / 📝 `Ejemplo 02: Uso de CompletableFuture en operaciones asincrónicas` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Aprender a crear y ejecutar **tareas asincrónicas** con `CompletableFuture`, encadenar operaciones usando `thenApply`, `thenAccept`, y manejar errores con `exceptionally`, para lograr flujos de trabajo más robustos y flexibles. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - Editor compatible con Java (IntelliJ IDEA, VS Code, etc.) 13 | - Conocimientos básicos de **asincronía** y **ExecutorService** 14 | 15 | --- 16 | 17 | ## 🧠 Contexto del ejemplo 18 | 19 | Imagina un **sistema de pedidos en línea**, donde después de **procesar un pago**, se debe: 20 | 21 | 1. Confirmar el pago. 22 | 2. Generar una factura. 23 | 3. Enviar una notificación. 24 | 25 | Cada paso puede tomar tiempo (simulando **latencia**), pero el sistema debe continuar **de forma no bloqueante**, utilizando **`CompletableFuture`** para encadenar estas operaciones. 26 | 27 | --- 28 | 29 | ## 🧱 Código de ejemplo 30 | 31 | ```java 32 | import java.util.concurrent.*; 33 | 34 | public class SistemaPedidos { 35 | 36 | public static void main(String[] args) { 37 | System.out.println("⚙️ Procesando pedido asincrónicamente...\n"); 38 | 39 | CompletableFuture pedido = procesarPago() 40 | .thenApply(pago -> generarFactura(pago)) // 🔄 Encadena y transforma el resultado 41 | .thenAccept(factura -> enviarNotificacion(factura)) // 📤 Consume el resultado final 42 | .exceptionally(ex -> { // ❌ Maneja errores 43 | System.out.println("🚨 Error en el proceso: " + ex.getMessage()); 44 | return null; 45 | }); 46 | 47 | pedido.join(); // 🕒 Espera que termine todo el flujo 48 | } 49 | 50 | // 🏦 Simula el procesamiento de un pago 51 | public static CompletableFuture procesarPago() { 52 | return CompletableFuture.supplyAsync(() -> { 53 | System.out.println("💳 Procesando pago..."); 54 | dormir(2); 55 | System.out.println("✅ Pago confirmado"); 56 | return "Pago#123"; 57 | }); 58 | } 59 | 60 | // 🧾 Simula la generación de una factura 61 | public static String generarFactura(String pago) { 62 | System.out.println("📝 Generando factura para " + pago + "..."); 63 | dormir(1); 64 | String factura = "Factura#456"; 65 | System.out.println("✅ Factura generada: " + factura); 66 | return factura; 67 | } 68 | 69 | // 📧 Simula el envío de una notificación 70 | public static void enviarNotificacion(String factura) { 71 | System.out.println("📧 Enviando notificación por " + factura + "..."); 72 | dormir(1); 73 | System.out.println("✅ Notificación enviada"); 74 | } 75 | 76 | // 💤 Método auxiliar para simular latencia 77 | public static void dormir(int segundos) { 78 | try { 79 | TimeUnit.SECONDS.sleep(segundos); 80 | } catch (InterruptedException e) { 81 | Thread.currentThread().interrupt(); 82 | } 83 | } 84 | } 85 | ``` 86 | 87 | --- 88 | 89 | ## 🧪 Resultado esperado 90 | 91 | ``` 92 | ⚙️ Procesando pedido asincrónicamente... 93 | 94 | 💳 Procesando pago... 95 | ✅ Pago confirmado 96 | 📝 Generando factura para Pago#123... 97 | ✅ Factura generada: Factura#456 98 | 📧 Enviando notificación por Factura#456... 99 | ✅ Notificación enviada 100 | ``` 101 | 102 | --- 103 | 104 | ## 🔍 Conceptos clave utilizados 105 | 106 | | Concepto | Descripción | 107 | |----------------------|-------------| 108 | | `CompletableFuture` | Permite crear tareas asincrónicas y encadenarlas | 109 | | `thenApply()` | Transforma el resultado de una tarea previa | 110 | | `thenAccept()` | Consume el resultado final sin devolver nada | 111 | | `exceptionally()` | Maneja errores en cualquier parte del flujo asincrónico | 112 | | `supplyAsync()` | Ejecuta una tarea de forma asincrónica y devuelve un valor | 113 | 114 | --- 115 | 116 | ## 📝 En resumen 117 | 118 | - **`CompletableFuture`** permite **encadenar operaciones asincrónicas** de forma declarativa y flexible. 119 | - **`thenApply`** transforma el resultado de una operación (ideal para pasar de un estado a otro, como de pago a factura). 120 | - **`thenAccept`** es útil para **acciones finales** (como enviar una notificación). 121 | - **`exceptionally`** asegura que **cualquier error en el flujo sea manejado**, evitando que el sistema falle silenciosamente. 122 | - Esta técnica permite **evitar bloqueos** en procesos como **pagos en línea**, **consultas a bases de datos remotas** o **llamadas a APIs externas**, donde es común que haya **latencia**. 123 | 124 | --- 125 | 126 | 📘 Recursos adicionales: 127 | 128 | - 🔗 [CompletableFuture en Java – Baeldung](https://www.baeldung.com/java-completablefuture) 129 | - 🔗 [Asynchronous Programming – Oracle Docs](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) 130 | 131 | --- 132 | 133 | ⬅️ [**Anterior**](../Ejemplo-01/Readme.md) | [**Siguiente**](../Reto-01/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-04/Ejemplo-03/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 04**](../Readme.md) ➡️ / 📝 `Ejemplo 03: Caso práctico de asincronía con operaciones I/O` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Aplicar **asincronía con `CompletableFuture`** en un **caso práctico de operaciones I/O**, como la **consulta a servicios externos simulados**, para visualizar cómo se pueden realizar varias tareas simultáneamente sin bloquear el hilo principal. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - Editor compatible con Java (IntelliJ IDEA, VS Code, etc.) 13 | - Conocimientos previos de asincronía y `CompletableFuture` 14 | 15 | --- 16 | 17 | ## 🧠 Contexto del ejemplo 18 | 19 | Simularemos un **sistema de consulta de clima** que obtiene datos de **dos servicios externos distintos** (por ejemplo, **API de clima** y **API de tráfico**). 20 | Ambas consultas pueden tener **latencia variable** (representando tiempos de respuesta diferentes). 21 | Queremos: 22 | 23 | 1. **Realizar ambas consultas de manera asincrónica**. 24 | 2. **Combinar los resultados** para generar un **reporte final** sin bloquear el flujo principal. 25 | 26 | --- 27 | 28 | ## 🧱 Código de ejemplo 29 | 30 | ```java 31 | import java.util.concurrent.*; 32 | 33 | public class ConsultaServiciosExternos { 34 | 35 | public static void main(String[] args) { 36 | System.out.println("🌐 Iniciando consultas a servicios externos...\n"); 37 | 38 | CompletableFuture climaFuture = obtenerClima(); 39 | CompletableFuture traficoFuture = obtenerTrafico(); 40 | 41 | // 🔗 Combina ambas consultas en un solo reporte 42 | CompletableFuture reporteFinal = climaFuture.thenCombine(traficoFuture, 43 | (clima, trafico) -> { 44 | return "📊 Reporte del día:\n- Clima: " + clima + "\n- Tráfico: " + trafico; 45 | }) 46 | .thenAccept(System.out::println) // 📤 Imprimir reporte 47 | .exceptionally(ex -> { // ❌ Manejo de errores 48 | System.out.println("🚨 Error al generar el reporte: " + ex.getMessage()); 49 | return null; 50 | }); 51 | 52 | // Esperar que todo termine 53 | reporteFinal.join(); 54 | } 55 | 56 | // 🌦️ Simula consulta a un servicio de clima 57 | public static CompletableFuture obtenerClima() { 58 | return CompletableFuture.supplyAsync(() -> { 59 | System.out.println("🌦️ Consultando clima..."); 60 | dormir(3); // Latencia simulada 61 | return "Soleado, 25°C"; 62 | }); 63 | } 64 | 65 | // 🚗 Simula consulta a un servicio de tráfico 66 | public static CompletableFuture obtenerTrafico() { 67 | return CompletableFuture.supplyAsync(() -> { 68 | System.out.println("🚗 Consultando tráfico..."); 69 | dormir(2); // Latencia simulada 70 | return "Moderado en el centro"; 71 | }); 72 | } 73 | 74 | // 💤 Método auxiliar para simular latencia 75 | public static void dormir(int segundos) { 76 | try { 77 | TimeUnit.SECONDS.sleep(segundos); 78 | } catch (InterruptedException e) { 79 | Thread.currentThread().interrupt(); 80 | } 81 | } 82 | } 83 | ``` 84 | 85 | --- 86 | 87 | ## 🧪 Resultado esperado 88 | 89 | ``` 90 | 🌐 Iniciando consultas a servicios externos... 91 | 92 | 🌦️ Consultando clima... 93 | 🚗 Consultando tráfico... 94 | 📊 Reporte del día: 95 | - Clima: Soleado, 25°C 96 | - Tráfico: Moderado en el centro 97 | ``` 98 | 99 | > ⚠️ **Nota:** El orden de las **consultas puede variar** debido a la **naturaleza asincrónica** de las operaciones. 100 | 101 | --- 102 | 103 | ## 🔍 Conceptos clave utilizados 104 | 105 | | Concepto | Descripción | 106 | |------------------------|-------------| 107 | | `CompletableFuture` | Permite consultas asincrónicas y composición de resultados. | 108 | | `thenCombine()` | Combina resultados de **dos tareas independientes**. | 109 | | `thenAccept()` | Ejecuta una acción final sobre el resultado combinado. | 110 | | `exceptionally()` | Maneja errores en el flujo asincrónico. | 111 | | `supplyAsync()` | Ejecuta una tarea que devuelve un valor de forma asincrónica. | 112 | 113 | --- 114 | 115 | ## 🚀 Conexión con APIs reales (opcional) 116 | 117 | Si quisieras **llevar este ejemplo a un entorno real**, conectándote a **APIs externas** en lugar de simular servicios, podrías utilizar herramientas como: 118 | 119 | ### 🔧 Librerías útiles en Java: 120 | 121 | | Librería | Uso principal | 122 | |------------------|---------------| 123 | | **Spring WebClient** | Cliente reactivo para consumir APIs REST (no bloqueante). | 124 | | **OkHttp** | Cliente HTTP moderno y eficiente para Java/Kotlin. | 125 | | **Apache HttpClient** | Cliente HTTP tradicional, ampliamente usado en Java. | 126 | 127 | --- 128 | 129 | ### 🌐 Ejemplos de APIs públicas: 130 | 131 | | API | Descripción | URL | 132 | |-----------------------------|----------------------------------------------|-----| 133 | | **OpenWeatherMap** | Datos de clima en tiempo real y pronósticos. | [https://openweathermap.org/api](https://openweathermap.org/api) | 134 | | **HERE Traffic API** | Datos de tráfico en tiempo real. | [https://developer.here.com](https://developer.here.com) | 135 | | **API-Ninjas** | Diversas APIs (clima, tráfico, noticias, etc.). | [https://api-ninjas.com](https://api-ninjas.com) | 136 | 137 | --- 138 | 139 | ### 💡 ¿Cómo integrarlas? 140 | 141 | - Usar **WebClient** de Spring para realizar solicitudes **no bloqueantes**: 142 | ```java 143 | WebClient webClient = WebClient.create("https://api.openweathermap.org"); 144 | Mono response = webClient.get() 145 | .uri("/data/2.5/weather?q=Monterrey&appid=YOUR_API_KEY") 146 | .retrieve() 147 | .bodyToMono(String.class); 148 | ``` 149 | 150 | - O con **OkHttp** (más simple): 151 | ```java 152 | OkHttpClient client = new OkHttpClient(); 153 | Request request = new Request.Builder() 154 | .url("https://api.openweathermap.org/data/2.5/weather?q=Monterrey&appid=YOUR_API_KEY") 155 | .build(); 156 | ``` 157 | 158 | > 📌 **Nota:** Usar APIs reales requiere manejar **claves API** y puede tener **limitaciones de uso gratuito**. 159 | 160 | --- 161 | 162 | ## 📝 En resumen 163 | 164 | - **`CompletableFuture`** permite **consultar múltiples servicios externos en paralelo**, optimizando tiempos de espera. 165 | - **`thenCombine`** es ideal para **unir resultados de dos consultas independientes** (ej. clima y tráfico). 166 | - **Asincronía** en operaciones I/O ayuda a **mejorar la eficiencia** en aplicaciones que dependen de **servicios externos o bases de datos**. 167 | 168 | > 🔍 Ejemplos de uso: 169 | > - Aplicaciones móviles consultando **API de clima y tráfico**. 170 | > - Sistemas de monitoreo que integran **múltiples fuentes de datos**. 171 | 172 | --- 173 | 174 | 📘 Recursos adicionales: 175 | 176 | - 🔗 [CompletableFuture en Java – Baeldung](https://www.baeldung.com/java-completablefuture) 177 | - 🔗 [Asynchronous I/O – Oracle Docs](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) 178 | 179 | --- 180 | 181 | ⬅️ [**Anterior**](../Reto-01/Readme.md) | [**Siguiente**](../Reto-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-04/Imagenes/S04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-04/Imagenes/S04.jpg -------------------------------------------------------------------------------- /Sesion-04/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../Readme.md) ➡️ / 📖 `Sesión 04` 2 | 3 |
4 | Sesion_04 5 |
6 | 7 | # 🎯 Objetivo 8 | 9 | ⚒️ Comprender las diferencias entre **concurrencia** y **asincronía** en Java, aprender a utilizar `CompletableFuture` para la ejecución de tareas asincrónicas, y aplicar estas técnicas en casos prácticos como operaciones I/O o simulación de servicios externos. 10 | 11 | --- 12 | 13 | 📘 Material del prework: 14 | Antes de comenzar con los ejercicios de esta sesión, recordemos que en el material de prework hemos cubierto los fundamentos teóricos que aplicaremos hoy. A lo largo de esta sesión, pondremos en práctica estos conceptos mediante una serie de ejercicios y retos diseñados para reforzar y validar nuestro entendimiento. 15 | 🔥¡Vamos a comenzar!🔥 16 | 17 | --- 18 | 19 | ## 📂 Temas de la sesión... 20 | 21 | ### 📖 Asincronía vs concurrencia 22 | Diferenciar los conceptos de **concurrencia** y **asincronía**, entendiendo cómo y cuándo aplicarlos en sistemas modernos. 23 | 24 | 🔹 **Diferencias clave y casos de uso** 25 | 🔹 **Latencia y tareas no bloqueantes** 26 | 🔹 **Uso combinado con hilos** 27 | 28 | 📜 **[Ejemplo 01: Diferencias entre concurrencia y asincronía](Ejemplo-01/Readme.md)** 29 | 30 | --- 31 | 32 | ### 📖 Uso de `CompletableFuture` 33 | Explorar cómo usar `CompletableFuture` para gestionar **procesos asincrónicos** de manera fluida y encadenada. 34 | 35 | 🔹 **Creación y ejecución de tareas asincrónicas** 36 | 🔹 **Encadenamiento con `thenApply`, `thenAccept`** 37 | 🔹 **Manejo de errores con `exceptionally`** 38 | 39 | 📜 **[Ejemplo 02: Uso de CompletableFuture en operaciones asincrónicas](Ejemplo-02/Readme.md)** 40 | 🔥 **[Reto 01: Simulación asincrónica en una app de movilidad](Reto-01/Readme.md)** 41 | 42 | --- 43 | 44 | ### 📖 Aplicaciones prácticas de asincronía 45 | Aplicar los conocimientos adquiridos en escenarios reales. 46 | 47 | 🔹 **Operaciones I/O asincrónicas** 48 | 🔹 **Simulación de llamadas a servicios externos** 49 | 🔹 **Consideraciones de diseño en aplicaciones asincrónicas** 50 | 51 | 📜 **[Ejemplo 03: Caso práctico de asincronía con operaciones I/O](Ejemplo-03/Readme.md)** 52 | 🔥 **[Reto 02: Gestión asincrónica de vuelos en un aeropuerto internacional](Reto-02/Readme.md)** 53 | 54 | --- 55 | 56 | ⬅️ [**Anterior**](../Sesion-03/Readme.md) | [**Siguiente**](../Sesion-05/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-04/Reto-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 04**](../Readme.md) ➡️ / ⚡ `Reto 01: Simulación asincrónica en una app de movilidad` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Aplicar `CompletableFuture` para simular **procesos asincrónicos** en una **app de movilidad** (tipo Uber o DiDi), realizando tareas en paralelo como **calcular la ruta** y **estimar la tarifa**, y enviando una **notificación** al usuario una vez finalizadas. 6 | 7 | --- 8 | 9 | ## 🧠 Contexto del reto 10 | 11 | En una app de movilidad, al solicitar un viaje: 12 | 13 | 1. Se calcula la **ruta óptima** entre origen y destino. 14 | 2. Se estima la **tarifa** considerando distancia y demanda. 15 | 3. Una vez ambas operaciones terminan, se envía una **confirmación al usuario** con la información. 16 | 17 | Todo este flujo debe ser **asincrónico y no bloqueante**, permitiendo procesar otras solicitudes mientras estas tareas se completan. 18 | 19 | --- 20 | 21 | ## 📝 Instrucciones 22 | 23 | 1. Crea una clase **`MovilidadApp`** con los siguientes métodos: 24 | 25 | - `CompletableFuture calcularRuta()`: 26 | - Simula calcular la **ruta óptima** (latencia de 2-3 segundos). 27 | - Retorna un mensaje como `"Ruta: Centro -> Norte"`. 28 | 29 | - `CompletableFuture estimarTarifa()`: 30 | - Simula la **estimación de la tarifa** (latencia de 1-2 segundos). 31 | - Retorna un valor numérico como `75.50`. 32 | 33 | - Un método para **combinar ambas operaciones**: 34 | - Muestra un mensaje final como: 35 | 36 | ``` 37 | 🚗 Ruta calculada: Centro -> Norte | Tarifa estimada: $75.50 38 | ``` 39 | 40 | 2. Encadena las operaciones usando **`thenCombine`** y maneja **errores** con `exceptionally` para casos donde alguna operación pueda fallar (simula con una excepción si quieres). 41 | 42 | 3. Muestra los resultados en consola. 43 | 44 | --- 45 | 46 | ## 💡 Ejemplo de salida esperada 47 | 48 | ``` 49 | 🚦 Calculando ruta... 50 | 💰 Estimando tarifa... 51 | ✅ 🚗 Ruta calculada: Centro -> Norte | Tarifa estimada: $75.50 52 | ``` 53 | 54 | --- 55 | 56 | 📘 Recursos útiles: 57 | 58 | - 🔗 [CompletableFuture – Oracle Docs](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) 59 | - 🔗 [Asynchronous Programming in Java – Baeldung](https://www.baeldung.com/java-completablefuture) 60 | 61 | --- 62 | 63 | 🏆 Si logras **combinar correctamente los resultados asincrónicos** y mostrar el mensaje final, ¡reto completado con éxito! 64 | 65 | --- 66 | 67 | ⬅️ [**Anterior**](../Ejemplo-02/Readme.md) | [**Siguiente**](../Ejemplo-03/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-04/Reto-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 04**](../Readme.md) ➡️ / ⚡ `Reto 02: Gestión asincrónica de vuelos en un aeropuerto internacional` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Simular un flujo **asincrónico y no bloqueante** para la **gestión de aterrizajes en un aeropuerto internacional**, integrando **varias consultas en paralelo** con `CompletableFuture`, combinando sus resultados y manejando errores potenciales. 6 | 7 | --- 8 | 9 | ## 🧠 Contexto del reto 10 | 11 | En un aeropuerto internacional, al aproximarse un **vuelo para aterrizaje**, el sistema debe realizar **consultas asincrónicas** para validar las condiciones del aterrizaje: 12 | 13 | 1. **Disponibilidad de la pista** (puede haber ocupación o mantenimiento). 14 | 2. **Estado meteorológico** (ej. tormentas, niebla, vientos fuertes). 15 | 3. **Estado del tráfico aéreo cercano** (otros vuelos aproximándose). 16 | 4. **Disponibilidad de personal en tierra** (personal de guía y seguridad). 17 | 18 | El aterrizaje solo se **autoriza si todas las condiciones son óptimas**. 19 | Este proceso debe ser **no bloqueante** y robusto, manejando errores si alguno de los servicios falla. 20 | 21 | --- 22 | 23 | ## 📝 Instrucciones 24 | 25 | 1. Crea una clase **`AeropuertoControl`** con los siguientes métodos: 26 | 27 | - `CompletableFuture verificarPista()` 28 | - `CompletableFuture verificarClima()` 29 | - `CompletableFuture verificarTraficoAereo()` 30 | - `CompletableFuture verificarPersonalTierra()` 31 | 32 | Cada método debe simular **latencia** (2-3 segundos) y devolver `true` si el servicio es favorable. 33 | 34 | 2. **Ejecuta todas las verificaciones en paralelo** usando `CompletableFuture`. 35 | 3. **Combina los resultados** usando `thenCombine`, `thenCombineAsync` o `allOf` para **decidir si se autoriza el aterrizaje**. 36 | 4. Si alguna consulta **falla o regresa `false`**, muestra: 37 | 38 | ``` 39 | 🚫 Aterrizaje denegado: condiciones no óptimas. 40 | ``` 41 | 42 | Si todo es exitoso: 43 | 44 | ``` 45 | 🛬 Aterrizaje autorizado: todas las condiciones óptimas. 46 | ``` 47 | 48 | 5. Usa **`exceptionally`** para manejar cualquier **error en el proceso**. 49 | 50 | --- 51 | 52 | ## 💪 Desafío adicional (opcional) 53 | 54 | - **Agrega fallas aleatorias** en cada servicio: 55 | - Cada verificación puede devolver **`true` o `false` aleatoriamente** (simulando incertidumbre). 56 | - Define probabilidades diferentes para cada servicio (ej. pista 80%, clima 85%, tráfico 90%, personal 95%). 57 | 58 | Esto hará que **cada ejecución sea distinta**, reflejando escenarios **más realistas** en la gestión de aterrizajes. 59 | 60 | --- 61 | 62 | ## 💡 Ejemplo de salida esperada 63 | 64 | ``` 65 | 🛫 Verificando condiciones para aterrizaje... 66 | 67 | 🛣️ Pista disponible: true 68 | 🌦️ Clima favorable: false 69 | 🚦 Tráfico aéreo despejado: true 70 | 👷‍♂️ Personal disponible: true 71 | 72 | 🚫 Aterrizaje denegado: condiciones no óptimas. 73 | ``` 74 | 75 | O: 76 | 77 | ``` 78 | 🛣️ Pista disponible: true 79 | 🌦️ Clima favorable: true 80 | 🚦 Tráfico aéreo despejado: true 81 | 👷‍♂️ Personal disponible: true 82 | 83 | 🛬 Aterrizaje autorizado: todas las condiciones óptimas. 84 | ``` 85 | 86 | --- 87 | 88 | 📘 Recursos útiles: 89 | 90 | - 🔗 [Asynchronous Programming with CompletableFuture – GeeksforGeeks](https://www.geeksforgeeks.org/completablefuture-in-java/) 91 | - 🔗 [Random in Java – GeeksforGeeks](https://www.geeksforgeeks.org/generating-random-numbers-in-java/) 92 | 93 | --- 94 | 95 | 🏆 Si logras combinar correctamente los **cuatro servicios asincrónicos** y agregar **fallas aleatorias**, ¡reto completado con éxito! 96 | 97 | --- 98 | 99 | ⬅️ [**Anterior**](../Ejemplo-03/Readme.md) | [**Siguiente**](../../Sesion-05/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-05/Ejemplo-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 05**](../Readme.md) ➡️ / 📝 `Ejemplo 01: Comparativa entre Stream API y programación reactiva` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Comparar el comportamiento de **Stream API tradicional** y **programación reactiva** con **Flux** en Java, mostrando cómo cada enfoque gestiona **notificaciones** a usuarios en una **plataforma de mensajería** y cómo manejan la **latencia** de entrega. 6 | 7 | --- 8 | 9 | ## 🧠 Contexto del ejemplo 10 | 11 | Supongamos que administras un sistema de **notificaciones de usuarios**. Queremos enviar **mensajes personalizados** a cada usuario: 12 | 13 | - Con **Stream API tradicional**, los mensajes se procesan **en bloque** (uno por uno hasta completar todos). 14 | - Con **Flux** (reactivo), los mensajes se **emiten a medida que están disponibles**, permitiendo una entrega **más fluida y no bloqueante**. 15 | 16 | --- 17 | 18 | ## 📦 Configuración de dependencias (Maven) 19 | 20 | Agrega esta dependencia en tu archivo `pom.xml`: 21 | 22 | ```xml 23 | 24 | 25 | io.projectreactor 26 | reactor-core 27 | 3.6.11 28 | 29 | 30 | ``` 31 | 32 | --- 33 | 34 | ## 🧱 Paso 1: Notificaciones con **Stream API** 35 | 36 | ```java 37 | import java.util.List; 38 | import java.util.concurrent.TimeUnit; 39 | 40 | public class NotificacionesStreamTradicional { 41 | public static void main(String[] args) throws InterruptedException { 42 | List usuarios = List.of("Ana", "Luis", "Marta", "Carlos"); 43 | 44 | System.out.println("📢 Enviando notificaciones con Stream API:"); 45 | 46 | usuarios.forEach(NotificacionesStreamTradicional::enviarNotificacion); 47 | } 48 | 49 | private static void enviarNotificacion(String usuario) { 50 | try { 51 | TimeUnit.SECONDS.sleep(1); // Simula retraso en enviar la notificación 52 | } catch (InterruptedException e) { 53 | Thread.currentThread().interrupt(); 54 | } 55 | System.out.println("✅ Notificación enviada a: " + usuario); 56 | } 57 | } 58 | ``` 59 | 60 | --- 61 | 62 | ## 🧱 Paso 2: Notificaciones con **Flux** (Programación reactiva) 63 | 64 | ```java 65 | import reactor.core.publisher.Flux; 66 | import java.time.Duration; 67 | 68 | public class NotificacionesFluxReactivo { 69 | public static void main(String[] args) throws InterruptedException { 70 | System.out.println("\n⚡ Enviando notificaciones con Flux:"); 71 | 72 | Flux usuariosFlux = Flux.just("Ana", "Luis", "Marta", "Carlos") 73 | .delayElements(Duration.ofSeconds(1)); // Simula llegada gradual 74 | 75 | usuariosFlux.subscribe(usuario -> 76 | System.out.println("✅ Notificación enviada a: " + usuario) 77 | ); 78 | 79 | // Esperar que terminen las notificaciones reactivas 80 | Thread.sleep(5000); 81 | } 82 | } 83 | ``` 84 | 85 | --- 86 | 87 | ## 🧪 Resultado esperado 88 | 89 | ``` 90 | 📢 Enviando notificaciones con Stream API: 91 | ✅ Notificación enviada a: Ana 92 | ✅ Notificación enviada a: Luis 93 | ✅ Notificación enviada a: Marta 94 | ✅ Notificación enviada a: Carlos 95 | 96 | ⚡ Enviando notificaciones con Flux: 97 | ✅ Notificación enviada a: Ana 98 | ✅ Notificación enviada a: Luis 99 | ✅ Notificación enviada a: Marta 100 | ✅ Notificación enviada a: Carlos 101 | ``` 102 | 103 | > 🔍 **Diferencia clave**: 104 | > - En **Stream API**, el proceso es **secuencial** y **bloquea** el hilo principal (`main`) hasta enviar todas las notificaciones. 105 | > - Con **Flux**, cada notificación **fluye de forma independiente**, **sin bloquear** el hilo, ideal para **sistemas en tiempo real**. 106 | 107 | --- 108 | 109 | ## 🔍 Conceptos clave utilizados 110 | 111 | | Concepto | Descripción | 112 | |--------------|-------------| 113 | | `Stream API` | Procesa colecciones de datos en **bloque**, de forma secuencial y **bloqueante**. | 114 | | `Flux` | Publicador reactivo que emite **múltiples elementos** en un flujo **no bloqueante**. | 115 | | `delayElements()` | Introduce latencia entre elementos emitidos por Flux, simulando llegada gradual. | 116 | | `subscribe()` | Activa el flujo y define cómo se procesan los elementos emitidos. | 117 | 118 | --- 119 | 120 | ## 📝 En resumen 121 | 122 | - **Stream API tradicional** es **bloqueante** y procesa **colecciones completas** de datos. 123 | - **Flux (reactivo)** permite **procesar elementos conforme llegan**, sin bloquear hilos, ideal para **flujos continuos o en tiempo real**. 124 | 125 | > 🚀 **¿Cuándo usar cada uno?** 126 | > - Usa **Stream API** para procesar listas ya completas (ej. colecciones en memoria). 127 | > - Usa **Flux** para **eventos en tiempo real**, **mensajería**, **sensores**, o cuando los datos **se generan gradualmente**. 128 | 129 | --- 130 | 131 | 📘 Recursos útiles: 132 | 133 | - 🔗 [Project Reactor – Guía oficial](https://projectreactor.io/docs/core/release/reference/) 134 | - 🔗 [Programación reactiva vs tradicional](https://www.pragma.co/es/blog/guia-de-programacion-reactiva-en-java) 135 | 136 | --- 137 | 138 | ⬅️ [**Anterior**](../Readme.md) | [**Siguiente**](../Ejemplo-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-05/Ejemplo-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 05**](../Readme.md) ➡️ / 📝 `Ejemplo 02: Uso de operadores en Flux` 2 | 3 | 4 | ## 🎯 Objetivo 5 | 6 | 🔍 Aplicar **operadores clave** de **Flux** (`map`, `flatMap`, `filter`) para transformar y procesar flujos reactivos, con varios ejemplos del mundo real. Entender cómo funciona la **subscripción** y cómo manejar flujos **concurrentes y asíncronos**. 7 | 8 | --- 9 | 10 | ## ⚙️ Requisitos 11 | 12 | - JDK 17 o superior 13 | - IntelliJ IDEA o cualquier editor compatible con Java 14 | - Dependencias de **Project Reactor** (`reactor-core`) 15 | - Conocimientos previos de **Stream API** y **Flux** 16 | 17 | --- 18 | 19 | ## 📦 Configuración de dependencias (Maven) 20 | 21 | Agrega esta dependencia en tu archivo `pom.xml`: 22 | 23 | ```xml 24 | 25 | 26 | io.projectreactor 27 | reactor-core 28 | 3.6.11 29 | 30 | 31 | ``` 32 | 33 | --- 34 | 35 | ## 🧠 Contexto del ejemplo 36 | 37 | Imagina diferentes **plataformas** que requieren enviar **notificaciones o tareas concurrentes**: 38 | 39 | 1. **Universidad**: Enviar **correos a estudiantes**. 40 | 2. **Empresa de logística**: Confirmar **entregas a clientes**. 41 | 3. **Sistema de monitoreo**: Notificar **alertas de sensores**. 42 | 43 | Con estos **tres flujos (Flux)** verás cómo aplicar **operadores reactivos** en situaciones distintas, explorando: 44 | 45 | - Cómo **transformar** elementos (`map`). 46 | - Cómo **filtrar** elementos (`filter`). 47 | - Cómo **expandir** un flujo (`flatMap`). 48 | 49 | > 🧪 **¿Por qué usar varios Flux?** 50 | > - **Mejora la comprensión** del modelo reactivo. 51 | > - Simula **escenarios reales complejos**. 52 | > - Permite **comparar comportamientos** entre flujos. 53 | 54 | --- 55 | 56 | ## 📄 Código base: `OperadoresReactorDemo.java` 57 | 58 | ```java 59 | import reactor.core.publisher.Flux; 60 | import java.time.Duration; 61 | import java.util.List; 62 | 63 | public class OperadoresReactorDemo { 64 | 65 | public static void main(String[] args) throws InterruptedException { 66 | 67 | // Ejemplo 1️⃣: Notificaciones en Universidad 68 | List usuarios = List.of( 69 | new Usuario("Ana", "Estudiante", "ana@uni.edu"), 70 | new Usuario("Carlos", "Profesor", "carlos@uni.edu"), 71 | new Usuario("Luisa", "Estudiante", "luisa@uni.edu"), 72 | new Usuario("Sofía", "Administrativo", "sofia@uni.edu") 73 | ); 74 | 75 | Flux.fromIterable(usuarios) 76 | .filter(u -> u.getRol().equalsIgnoreCase("Estudiante")) // 🔍 Filtra estudiantes 77 | .map(u -> "📢 Notificación para: " + u.getNombre()) // 🔄 Transforma en mensaje 78 | .delayElements(Duration.ofMillis(500)) 79 | .subscribe(m -> System.out.println("✅ Universidad: " + m)); 80 | 81 | // Ejemplo 2️⃣: Confirmación de entregas (logística) 82 | Flux.just("Pedido 1", "Pedido 2", "Pedido 3") 83 | .flatMap(pedido -> simularEntrega(pedido)) // 🔁 Cada pedido genera otro flujo (simula proceso asíncrono) 84 | .subscribe(m -> System.out.println("🚚 Logística: " + m)); 85 | 86 | // Ejemplo 3️⃣: Alertas de sensores 87 | Flux.just(18, 22, 35, 45) // 🔢 Temperaturas de sensores 88 | .filter(temp -> temp > 30) // 🔍 Filtra temperaturas altas 89 | .map(temp -> "⚠️ Alerta: Temperatura alta: " + temp + "°C") 90 | .subscribe(System.out::println); 91 | 92 | Thread.sleep(4000); // Esperar finalización 93 | } 94 | 95 | // 🔧 Simula proceso asíncrono para confirmación de entrega 96 | private static Flux simularEntrega(String pedido) { 97 | return Flux.just(pedido + " confirmado") 98 | .delayElements(Duration.ofMillis(700)); 99 | } 100 | 101 | // Clase auxiliar 102 | static class Usuario { 103 | private final String nombre; 104 | private final String rol; 105 | private final String correo; 106 | 107 | public Usuario(String nombre, String rol, String correo) { 108 | this.nombre = nombre; 109 | this.rol = rol; 110 | this.correo = correo; 111 | } 112 | 113 | public String getNombre() { return nombre; } 114 | public String getRol() { return rol; } 115 | } 116 | } 117 | ``` 118 | 119 | --- 120 | 121 | ## 🧪 Resultado esperado 122 | 123 | ``` 124 | ⚠️ Alerta: Temperatura alta: 35°C 125 | ⚠️ Alerta: Temperatura alta: 45°C 126 | ✅ Universidad: 📢 Notificación para: Ana 127 | 🚚 Logística: Pedido 2 confirmado 128 | 🚚 Logística: Pedido 1 confirmado 129 | 🚚 Logística: Pedido 3 confirmado 130 | ✅ Universidad: 📢 Notificación para: Luisa 131 | ``` 132 | 133 | > ⚠️ El **orden de ejecución puede variar** entre los flujos por la **naturaleza asíncrona de Flux**. 134 | 135 | --- 136 | 137 | ## 🔍 Operadores utilizados en Flux 138 | 139 | | Operador | Propósito | 140 | |-----------------|-----------| 141 | | `fromIterable()`| Crea un **Flux** a partir de una colección. | 142 | | `just()` | Crea un **Flux** con uno o más elementos. | 143 | | `filter()` | Filtra los elementos que cumplen una condición. | 144 | | `map()` | Transforma cada elemento. | 145 | | `flatMap()` | Expande cada elemento en otro **Flux** (flujo dentro de flujo). | 146 | | `delayElements()`| Introduce una latencia entre cada elemento emitido. | 147 | | `subscribe()` | Inicia el flujo reactivo. | 148 | 149 | --- 150 | 151 | ## 📝 En resumen 152 | 153 | - Usar **varios Flux** ayuda a **comparar casos de uso reales**: 154 | - **Notificaciones de universidad** (map + filter). 155 | - **Procesos logísticos** (flatMap + asincronía). 156 | - **Alertas de sensores** (filter + map). 157 | 158 | - **`flatMap`** permite lanzar **subprocesos reactivos** dentro de un flujo. 159 | - La **latencia simulada** (`delayElements`) muestra **procesos asíncronos sin bloquear hilos**. 160 | 161 | --- 162 | 163 | ⬅️ [**Anterior**](../Ejemplo-01/Readme.md) | [**Siguiente**](../Reto-01/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-05/Ejemplo-03/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 05**](../Readme.md) ➡️ / 📝 `Ejemplo 03: Manejo avanzado de flujos y backpressure` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Comprender cómo manejar **backpressure** (control de velocidad de emisión de datos) y cómo **encadenar operaciones reactivas** usando **Flux** y **Project Reactor**, explorando diferentes **escenarios del mundo real**. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - JDK 17 o superior 12 | - IntelliJ IDEA o cualquier editor compatible con Java 13 | - Dependencias de **Project Reactor** (`reactor-core`) 14 | - Conocimientos previos de `Flux`, `map`, `flatMap`, `filter` 15 | 16 | --- 17 | 18 | ## 🧠 Contexto del ejemplo 19 | 20 | En esta sesión verás **tres escenarios distintos** donde **backpressure** y **Flux** son útiles: 21 | 22 | 1️⃣ **Sistema de pedidos en línea** (prioritarios vs normales). 23 | 2️⃣ **Alertas de sensores** (sólo altas temperaturas deben procesarse lentamente). 24 | 3️⃣ **Mensajes en redes sociales** (alta demanda, procesar solo mensajes importantes). 25 | 26 | Estos ejemplos muestran cómo: 27 | 28 | - **Controlar la velocidad** del flujo (backpressure). 29 | - **Filtrar**, **transformar** y **procesar flujos reactivos** de forma flexible. 30 | 31 | --- 32 | 33 | ## 📦 Configuración de dependencias (Maven) 34 | 35 | Agrega esta dependencia en tu archivo `pom.xml`: 36 | 37 | ```xml 38 | 39 | 40 | io.projectreactor 41 | reactor-core 42 | 3.6.11 43 | 44 | 45 | ``` 46 | 47 | --- 48 | 49 | ## 📄 Código base: `ManejoAvanzadoReactor.java` 50 | 51 | ```java 52 | import reactor.core.publisher.Flux; 53 | import java.time.Duration; 54 | 55 | public class ManejoAvanzadoReactor { 56 | 57 | public static void main(String[] args) throws InterruptedException { 58 | 59 | // Ejemplo 1️⃣: Sistema de pedidos con backpressure 60 | Flux.interval(Duration.ofMillis(500)) 61 | .onBackpressureBuffer() // 👈 Añadido para evitar overflow 62 | .map(i -> new Pedido(i + 1, i % 3 == 0 ? "Prioritario" : "Normal")) 63 | .take(9) 64 | .filter(p -> p.getTipo().equals("Prioritario")) 65 | .doOnNext(p -> System.out.println("📥 Pedido recibido: " + p)) 66 | .delayElements(Duration.ofSeconds(1)) 67 | .subscribe(p -> System.out.println("✅ Pedido procesado: " + p)); 68 | 69 | // Ejemplo 2️⃣: Alertas de sensores 70 | Flux.interval(Duration.ofMillis(400)) 71 | .onBackpressureBuffer() // 👈 Añadido para evitar overflow 72 | .map(i -> (int) (Math.random() * 50)) 73 | .take(7) 74 | .filter(temp -> temp > 30) 75 | .delayElements(Duration.ofMillis(800)) 76 | .subscribe(temp -> System.out.println("⚠️ Alerta: Temperatura alta - " + temp + "°C")); 77 | 78 | // Ejemplo 3️⃣: Mensajes en redes sociales 79 | Flux.interval(Duration.ofMillis(300)) 80 | .onBackpressureBuffer() // 👈 Añadido para evitar overflow 81 | .map(i -> "Mensaje #" + (i + 1)) 82 | .take(5) 83 | .doOnNext(m -> System.out.println("💬 Mensaje recibido: " + m)) 84 | .delayElements(Duration.ofMillis(1000)) 85 | .subscribe(m -> System.out.println("📢 Procesado: " + m)); 86 | 87 | Thread.sleep(10000); // Esperar todos los flujos 88 | } 89 | 90 | // Clase auxiliar Pedido 91 | static class Pedido { 92 | private final long id; 93 | private final String tipo; 94 | 95 | public Pedido(long id, String tipo) { 96 | this.id = id; 97 | this.tipo = tipo; 98 | } 99 | 100 | public String getTipo() { return tipo; } 101 | 102 | @Override 103 | public String toString() { 104 | return "Pedido#" + id + " [" + tipo + "]"; 105 | } 106 | } 107 | } 108 | ``` 109 | 110 | --- 111 | 112 | ## 🧪 Resultado esperado (fragmento) 113 | 114 | ``` 115 | 📥 Pedido recibido: Pedido#3 [Prioritario] 116 | ✅ Pedido procesado: Pedido#3 [Prioritario] 117 | ⚠️ Alerta: Temperatura alta - 42°C 118 | 💬 Mensaje recibido: Mensaje #1 119 | 📢 Procesado: Mensaje #1 120 | 📥 Pedido recibido: Pedido#6 [Prioritario] 121 | ✅ Pedido procesado: Pedido#6 [Prioritario] 122 | ⚠️ Alerta: Temperatura alta - 39°C 123 | 💬 Mensaje recibido: Mensaje #2 124 | 📢 Procesado: Mensaje #2 125 | ... 126 | ``` 127 | 128 | > ⚠️ El **orden puede variar** debido a la **naturaleza asíncrona** de cada **Flux**. 129 | 130 | --- 131 | 132 | ## 🔍 Conceptos clave utilizados 133 | 134 | | Concepto | Descripción | 135 | |----------------------|-------------| 136 | | `Flux.interval()` | Genera un flujo de datos a intervalos regulares (pedidos, temperaturas, mensajes). | 137 | | `onBackpressureBuffer()` | Controla el **backpressure** almacenando temporalmente los elementos si el consumidor procesa más lento que el productor. | 138 | | `filter()` | Filtra elementos relevantes (ej. solo prioritarios o temperaturas altas). | 139 | | `map()` | Transforma elementos del flujo (ej. índice → objeto o temperatura). | 140 | | `flatMap()` | (No usado aquí, pero útil para expandir un flujo en múltiples subflujos). | 141 | | `doOnNext()` | Ejecuta acciones secundarias (logs, monitoreo) sin modificar el flujo. | 142 | | `delayElements()` | Introduce latencia entre cada elemento emitido, controlando la velocidad (**simula backpressure**). | 143 | | `subscribe()` | Activa el flujo reactivo (sin suscripción no sucede nada). | 144 | 145 | --- 146 | 147 | ## 📝 En resumen 148 | 149 | - **Backpressure** es una técnica fundamental para **evitar que el consumidor se vea saturado** cuando el productor emite datos más rápido de lo que pueden procesarse. 150 | - Herramientas como **`onBackpressureBuffer()`** permiten **almacenar temporalmente los elementos excedentes** para mantener un flujo estable. 151 | 152 | - **Combinar varios Flux** permite modelar sistemas **concurrentes y reactivos**, adaptados a diferentes ritmos de producción y consumo (pedidos, sensores, mensajes en tiempo real). 153 | 154 | - Los operadores **`filter`**, **`map`**, **`delayElements`**, **`doOnNext`** y **`onBackpressureBuffer()`** son esenciales para construir **flujos robustos, adaptables y seguros** en entornos de alta demanda. 155 | 156 | - **El control del ritmo del flujo** garantiza que el sistema **no colapse ante cargas variables**, ofreciendo **resiliencia y flexibilidad**. 157 | 158 | 159 | > 💡 **Tip:** En casos más complejos, puedes combinar **Flux.merge()** o **Flux.concat()** para fusionar varios flujos. 160 | 161 | --- 162 | 163 | ⬅️ [**Anterior**](../Reto-01/Readme.md) | [**Siguiente**](../Reto-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-05/Imagenes/S05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-05/Imagenes/S05.jpg -------------------------------------------------------------------------------- /Sesion-05/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../Readme.md) ➡️ / 📖 `Sesión 05` 2 | 3 |
4 | Sesion_05 5 |
6 | 7 | # 🎯 Objetivo 8 | 9 | ⚒️ Comprender los fundamentos de la **programación reactiva** en Java, conocer las diferencias con el manejo tradicional de streams, e implementar flujos reactivos utilizando **Project Reactor** o **RxJava**, abordando conceptos clave como **Mono**, **Flux** y **control de backpressure**. 10 | 11 | --- 12 | 13 | 📘 Material del prework: 14 | Antes de comenzar con los ejercicios de esta sesión, recordemos que en el material de prework hemos cubierto los fundamentos teóricos de la **programación reactiva**, los cuales aplicaremos hoy mediante una serie de ejercicios prácticos diseñados para fortalecer nuestro entendimiento. 15 | 🔥¡Vamos a comenzar!🔥 16 | 17 | --- 18 | 19 | ## 📂 Temas de la sesión... 20 | 21 | ### 📖 Introducción a programación reactiva 22 | Exploraremos qué es la programación reactiva, cuándo conviene usarla, y cómo se diferencia del **Stream API tradicional**. 23 | 24 | 🔹 **¿Qué es y por qué usarla?** 25 | 🔹 **Diferencia con Stream API tradicional** 26 | 🔹 **Patrones reactivos comunes** 27 | 28 | 📜 **[Ejemplo 01: Comparativa entre Stream API y programación reactiva](Ejemplo-01/Readme.md)** 29 | 30 | --- 31 | 32 | ### 📖 Project Reactor / RxJava 33 | Implementaremos flujos reactivos usando **Project Reactor** o **RxJava**, aprendiendo los conceptos clave y operadores básicos. 34 | 35 | 🔹 **Conceptos clave: Mono, Flux** 36 | 🔹 **Operadores: map, flatMap, filter** 37 | 🔹 **Subscripción y ejecución** 38 | 39 | 📜 **[Ejemplo 02: Flujo reactivo básico con Flux y Mono](Ejemplo-02/Readme.md)** 40 | 🔥 **[Reto 01: Gestión reactiva de sistemas críticos en Meridian Prime](Reto-01/Readme.md)** 41 | 42 | --- 43 | 44 | ### 📖 Manejo avanzado de flujos 45 | Abordaremos técnicas para el **manejo de backpressure**, el encadenamiento de operaciones, y casos comunes de uso en sistemas modernos. 46 | 47 | 🔹 **Control de backpressure** 48 | 🔹 **Encadenamiento de operaciones** 49 | 🔹 **Casos de uso comunes (servicios, flujos de eventos)** 50 | 51 | 📜 **[Ejemplo 03: Control de backpressure en flujos reactivos](Ejemplo-03/Readme.md)** 52 | 🔥 **[Reto 02: Procesamiento de eventos en una cadena de producción reactiva](Reto-02/Readme.md)** 53 | 54 | --- 55 | 56 | ⬅️ [**Anterior**](../Sesion-04/Readme.md) | [**Siguiente**](../Sesion-06/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-05/Reto-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 05**](../Readme.md) ➡️ / ⚡ `Reto 01: Gestión reactiva de sistemas críticos en Meridian Prime` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Simular el **flujo reactivo y no bloqueante** para la **gestión en tiempo real de los sistemas críticos** de **Meridian Prime** (una ciudad inteligente inspirada en *Horizon Zero Dawn*), usando **Project Reactor** y **Flux** para procesar **múltiples flujos concurrentes**. 6 | 7 | --- 8 | 9 | ## 🧠 Contexto del reto 10 | 11 | Como **chief engineer de Meridian Prime**, debes gestionar **cinco sistemas críticos** en **tiempo real**: 12 | 13 | 1. 🚗 **Sensores de tráfico** → Detectan congestión en las principales avenidas. 14 | 2. 🌫️ **Contaminación del aire** → Generan alertas si superan los límites saludables. 15 | 3. 🚑 **Accidentes viales** → Prioriza emergencias según la gravedad del accidente. 16 | 4. 🚝 **Trenes maglev** → Controla la frecuencia y detecta retrasos. 17 | 5. 🚦 **Semáforos inteligentes** → Ajusta tiempos según la repetición de señales rojas. 18 | 19 | El sistema debe **procesar estos flujos en paralelo**, **filtrar eventos críticos** y **responder de forma fluida** sin bloquear el sistema. 20 | 21 | --- 22 | 23 | ## 📝 Instrucciones 24 | 25 | 1. **Crea cinco flujos `Flux` independientes**, cada uno simulando **un sistema crítico**: 26 | 27 | | Sistema | Dato simulado | Frecuencia sugerida | 28 | |--------------------------|---------------------------------------|----------------------| 29 | | Sensores de tráfico | Nivel de congestión (0-100%) | Cada 500 ms | 30 | | Contaminación del aire | Nivel de partículas PM2.5 (ug/m3) | Cada 600 ms | 31 | | Accidentes viales | Evento con prioridad (Baja, Media, Alta) | Cada 800 ms | 32 | | Trenes maglev | Retraso en minutos (0-10 min) | Cada 700 ms | 33 | | Semáforos inteligentes | Estado (Verde, Amarillo, Rojo) por cruce | Cada 400 ms | 34 | 35 | 2. **Filtra eventos críticos** en cada flujo: 36 | 37 | - 🚗 **Congestión mayor al 70%**. 38 | - 🌫️ **Contaminación superior a 50 ug/m3**. 39 | - 🚑 **Accidentes con prioridad Alta**. 40 | - 🚝 **Retrasos mayores a 5 minutos**. 41 | - 🚦 **Semáforo en rojo más de 3 veces seguidas**. 42 | 43 | 3. **Emite en consola los eventos críticos detectados** con mensajes descriptivos. 44 | 45 | 4. **Simula backpressure** en al menos un flujo (ej. sensores de tráfico o trenes maglev) usando **`delayElements`** o **`onBackpressureBuffer`**. 46 | 47 | 5. Implementa **subscripciones separadas** para cada flujo (puedes usar `merge()` si quieres combinarlos). 48 | 49 | --- 50 | 51 | ## 💪 Desafío adicional (opcional) 52 | 53 | - Simula **eventos aleatorios críticos simultáneos** (ej. accidente múltiple + congestión severa + contaminación alta) para poner a prueba la **capacidad reactiva** de tu sistema. 54 | - Muestra en consola una **alerta global** si **tres o más eventos críticos** ocurren al mismo tiempo. 55 | 56 | --- 57 | 58 | ## 💡 Ejemplo de salida esperada 59 | 60 | ``` 61 | 🚗 Alerta: Congestión del 85% en Avenida Solar 62 | 🌫️ Alerta: Contaminación alta (PM2.5: 67 ug/m3) 63 | 🚑 Emergencia vial: Accidente con prioridad Alta 64 | 🚝 Tren maglev con retraso crítico: 8 minutos 65 | 🚦 Semáforo en Rojo detectado 3 veces seguidas en cruce Norte 66 | 67 | 🚨 Alerta global: Múltiples eventos críticos detectados en Meridian Prime 68 | ``` 69 | 70 | --- 71 | 72 | 🏆 **Reto completado con éxito** si logras: 73 | 74 | - **Controlar los cinco sistemas en paralelo**. 75 | - **Filtrar y procesar eventos críticos de forma reactiva**. 76 | - Simular **backpressure** y **alertas globales** correctamente. 77 | 78 | --- 79 | 80 | ⬅️ [**Anterior**](../Ejemplo-02/Readme.md) | [**Siguiente**](../Ejemplo-03/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-05/Reto-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 05**](../Readme.md) ➡️ / ⚡ `Reto 02: Monitoreo reactivo de signos vitales en una UCI` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Simular un sistema **reactivo** que **monitorea signos vitales** de **pacientes críticos** en tiempo real en una **Unidad de Cuidados Intensivos (UCI)**, aplicando **backpressure** para controlar el flujo de datos y **encadenar operaciones reactivas** que filtren eventos anómalos. 6 | 7 | --- 8 | 9 | ## 🧠 Contexto del reto 10 | 11 | En una UCI moderna, los **sensores** de pacientes generan constantemente datos como: 12 | 13 | - **Frecuencia cardíaca (FC)** 14 | - **Presión arterial (PA)** 15 | - **Nivel de oxígeno (SpO2)** 16 | 17 | Estos datos **se generan rápidamente**, pero **el sistema médico no puede procesar todo al mismo ritmo**. Debes construir un **flujo reactivo** que: 18 | 19 | 1. **Filtre valores críticos** (ej. FC muy alta/baja, PA fuera de rango, SpO2 bajo). 20 | 2. Aplique **backpressure** para **controlar la velocidad de procesamiento**, evitando saturar al personal médico. 21 | 3. Muestre **alertas en consola** sólo para **eventos críticos**. 22 | 23 | --- 24 | 25 | ## 📝 Instrucciones 26 | 27 | 1. **Simula generación continua de datos** usando `Flux.interval()` para **3 pacientes**. 28 | - Cada paciente debe emitir eventos **cada 300 ms** con **valores aleatorios** para **FC**, **PA** y **SpO2**. 29 | 30 | 2. **Filtra los eventos críticos** con estas condiciones: 31 | 32 | | Parámetro | Rango crítico | 33 | |----------------------|---------------------------------| 34 | | Frecuencia cardíaca | < 50 o > 120 bpm | 35 | | Presión arterial | < 90/60 mmHg o > 140/90 mmHg | 36 | | Oxígeno en sangre | < 90% | 37 | 38 | 3. Implementa **backpressure** con `delayElements()` para **procesar eventos críticos** a un ritmo **más lento (1 seg)**. 39 | 4. Muestra **alertas** en consola, por ejemplo: 40 | 41 | ``` 42 | ⚠️ Paciente 2 - FC crítica: 130 bpm 43 | ⚠️ Paciente 1 - SpO2 baja: 85% 44 | ``` 45 | 46 | --- 47 | 48 | ## 💪 Desafío adicional (opcional) 49 | 50 | - **Fusiona los flujos de los 3 pacientes** usando `Flux.merge()` o `Flux.concat()` para **gestionar todos los eventos en un solo flujo**. 51 | - Prioriza los eventos de **FC** sobre **PA** o **SpO2** si hay múltiples eventos simultáneos. 52 | 53 | --- 54 | 55 | ## 💡 Ejemplo de salida esperada 56 | 57 | ``` 58 | ⚠️ Paciente 1 - FC crítica: 130 bpm 59 | ⚠️ Paciente 2 - SpO2 baja: 85% 60 | ⚠️ Paciente 3 - PA crítica: 150/95 mmHg 61 | ``` 62 | 63 | > ⚠️ **El orden puede variar** debido al procesamiento reactivo y la fusión de flujos. 64 | 65 | --- 66 | 67 | 🏆 Si logras **filtrar, fusionar flujos** y aplicar **backpressure** en el monitoreo de signos vitales, ¡reto completado con éxito! 🎉 68 | 69 | --- 70 | 71 | ⬅️ [**Anterior**](../Ejemplo-03/Readme.md) | [**Siguiente**](../../Sesion-06/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-06/Ejemplo-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 06**](../Readme.md) ➡️ / 📝 `Ejemplo 01: JPA - Creación de entidades y repositorios` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Aprender a crear una entidad Java utilizando JPA y definir un repositorio para gestionar operaciones básicas con una base de datos, simulando un sistema de inventario. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - IntelliJ IDEA Community Edition 12 | - Apache Maven 3.8.4 o superior 13 | - JDK 17 o superior (se recomienda Java 17+) 14 | - Spring Boot 3.x 15 | - Navegador web para Spring Initializr 16 | 17 | --- 18 | 19 | ## 📦 Creación del proyecto con Spring Initializr 20 | 21 | 1. Ingresa a [https://start.spring.io](https://start.spring.io/) 22 | 2. Configura lo siguiente: 23 | - **Project**: Maven 24 | - **Language**: Java 25 | - **Spring Boot**: 3.2.x (última versión estable) 26 | - **Group**: `com.bedu` 27 | - **Artifact**: `inventario` 28 | - **Java**: 17 29 | 3. Agrega estas dependencias: 30 | - Spring Web 31 | - Spring Data JPA 32 | - H2 Database (para pruebas rápidas sin instalar nada) 33 | 34 | 4. Haz clic en "Generate" y abre el proyecto en IntelliJ IDEA. 35 | 36 | --- 37 | 38 | ## 🧱 Creación de la entidad `Producto` 39 | 40 | Creamos una clase `Producto` con anotaciones JPA: 41 | 42 | ```java 43 | package com.bedu.inventario; 44 | 45 | import jakarta.persistence.*; 46 | 47 | @Entity 48 | public class Producto { 49 | 50 | @Id // Campo que funcionará como clave primaria de la tabla 51 | @GeneratedValue(strategy = GenerationType.IDENTITY) // El ID se generará automáticamente (autoincremental) 52 | private Long id; 53 | 54 | // Campos que serán columnas en la tabla 'producto' 55 | private String nombre; 56 | private String descripcion; 57 | private double precio; 58 | 59 | protected Producto() {} // Constructor por defecto requerido por JPA 60 | 61 | // Constructor público para crear objetos de tipo Producto con los campos necesarios 62 | public Producto(String nombre, String descripcion, double precio) { 63 | this.nombre = nombre; 64 | this.descripcion = descripcion; 65 | this.precio = precio; 66 | } 67 | 68 | // Getters para acceder a los atributos (necesarios para el funcionamiento de JPA y buenas prácticas) 69 | public Long getId() { return id; } 70 | public String getNombre() { return nombre; } 71 | public String getDescripcion() { return descripcion; } 72 | public double getPrecio() { return precio; } 73 | 74 | // Método que permite imprimir el objeto de forma legible (útil para logs o consola) 75 | @Override 76 | public String toString() { 77 | return String.format("Producto[id=%d, nombre='%s', precio=%.2f]", 78 | id, nombre, precio); 79 | } 80 | } 81 | ``` 82 | 83 | --- 84 | 85 | ## 📁 Creación del repositorio `ProductoRepository` 86 | 87 | ```java 88 | package com.bedu.inventario; 89 | 90 | import org.springframework.data.jpa.repository.JpaRepository; 91 | 92 | import java.util.List; 93 | 94 | // Esta interfaz extiende JpaRepository para gestionar operaciones CRUD sobre la entidad Producto 95 | public interface ProductoRepository extends JpaRepository { 96 | 97 | // Método personalizado que busca productos cuyo nombre contenga un texto específico (no sensible a mayúsculas) 98 | List findByNombreContaining(String nombre); 99 | } 100 | ``` 101 | 102 | --- 103 | 104 | ## 🚀 Probar el repositorio desde la clase principal 105 | 106 | Edita tu clase `InventarioApplication.java` para incluir un `CommandLineRunner` que pruebe operaciones básicas: 107 | 108 | ```java 109 | package com.bedu.inventario; 110 | 111 | import org.springframework.boot.CommandLineRunner; 112 | import org.springframework.boot.SpringApplication; 113 | import org.springframework.boot.autoconfigure.SpringBootApplication; 114 | import org.springframework.context.annotation.Bean; 115 | 116 | @SpringBootApplication 117 | public class InventarioApplication { 118 | 119 | public static void main(String[] args) { 120 | SpringApplication.run(InventarioApplication.class, args); 121 | } 122 | 123 | @Bean 124 | public CommandLineRunner demo(ProductoRepository repository) { 125 | return (args) -> { 126 | // Guardar algunos productos 127 | repository.save(new Producto("Laptop", "Portátil de 16 pulgadas", 1200.00)); 128 | repository.save(new Producto("Teclado mecánico", "Switch azul", 800.00)); 129 | repository.save(new Producto("Mouse gamer", "Alta precisión", 600.00)); 130 | 131 | // Mostrar todos los productos 132 | System.out.println("📂 Productos disponibles:"); 133 | repository.findAll().forEach(System.out::println); 134 | 135 | // Buscar por nombre parcial 136 | System.out.println("\n🔍 Productos que contienen 'Lap':"); 137 | repository.findByNombreContaining("Lap").forEach(System.out::println); 138 | }; 139 | } 140 | } 141 | ``` 142 | 143 | --- 144 | 145 | ## 🧪 Resultado esperado en consola 146 | 147 | Al ejecutar el programa verás una salida similar a: 148 | 149 | ``` 150 | 📂 Productos disponibles: 151 | Producto[id=1, nombre='Laptop', precio=1200.00] 152 | Producto[id=2, nombre='Teclado mecánico', precio=800.00] 153 | Producto[id=3, nombre='Mouse gamer', precio=600.00] 154 | 155 | 🔍 Productos que contienen 'Lap': 156 | Producto[id=1, nombre='Laptop', precio=1200.00] 157 | ``` 158 | 159 | --- 160 | 161 | ## 🔍 Conceptos clave utilizados 162 | 163 | | Anotación / Clase | Propósito | 164 | |-------------------|-----------| 165 | | `@Entity` | Marca una clase como entidad persistente | 166 | | `@Id` | Indica el atributo que será la clave primaria | 167 | | `@GeneratedValue` | Especifica cómo se genera automáticamente el ID | 168 | | `JpaRepository` | Permite usar métodos como `save()`, `findAll()`, `deleteById()` sin código adicional | 169 | | `CommandLineRunner` | Ejecuta código al iniciar la app (útil para pruebas sin frontend) | 170 | 171 | --- 172 | 173 | ## 📝 En resumen 174 | 175 | - Con **JPA (Java Persistence API)** y **Spring Data JPA** creamos fácilmente **entidades** (como `Producto`) que se **mapean a tablas** en una base de datos. 176 | - **`JpaRepository`** nos proporciona **operaciones CRUD básicas** (guardar, buscar, eliminar) sin necesidad de escribir SQL. 177 | - Con **consultas derivadas** como `findByNombreContaining`, generamos búsquedas personalizadas de manera **automática y declarativa**. 178 | - Integramos un **repositorio** en la clase principal con `CommandLineRunner` para **insertar datos** y **consultarlos** al iniciar la aplicación. 179 | - Esta es la **base de cualquier aplicación backend** que interactúe con bases de datos relacionales usando **Spring Boot y JPA**. 180 | 181 | --- 182 | 183 | ### 💡 ¿Sabías que...? 184 | 185 | - **JPA** no es una implementación en sí, sino una **especificación**. Las implementaciones más conocidas son **Hibernate**, **EclipseLink** y **TopLink**. 186 | - Spring Boot **detecta automáticamente** las entidades y configura el datasource si encuentra dependencias como `spring-boot-starter-data-jpa` y una base de datos embebida como H2. 187 | - Puedes **cambiar la base de datos** de desarrollo (por ejemplo, de H2 a MySQL) simplemente modificando tu archivo `application.properties` sin tocar el código de tus entidades. 188 | - Gracias a la convención de nombres, Spring Data JPA puede **generar automáticamente consultas** como `findByNombreContaining` o `findByPrecioGreaterThan`. 189 | - **JPA con Spring Boot** permite crear operaciones CRUD completas sin escribir SQL manualmente, gracias al uso de repositorios. 190 | - `@Bean CommandLineRunner` ejecuta código automáticamente al iniciar la app, útil para probar sin interfaz gráfica. 191 | 192 | --- 193 | 194 | ### 📘 Recursos adicionales 195 | 196 | - 🔗 [Spring Data JPA – Query Methods](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods) 197 | - 🔗 [POO en Java – W3Schools](https://www.w3schools.com/java/java_oop.asp) 198 | - 🔗 [Spring Boot: Guía oficial](https://spring.io/projects/spring-boot) 199 | 200 | --- 201 | 202 | ⬅️ [**Anterior**](../Readme.md) | [**Siguiente**](../Reto-01/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-06/Ejemplo-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 06**](../Readme.md) ➡️ / 📝 `Ejemplo 02: Configuración de conexión y creación de tablas` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Aprender a configurar correctamente la conexión a base de datos en un proyecto Spring Boot con JPA, y modelar una relación simple entre entidades para representar un caso de uso real como una tienda en línea. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - Proyecto base generado en [Spring Initializr](https://start.spring.io) (puedes reutilizar el proyecto del Ejemplo 01) 12 | - IntelliJ IDEA Community Edition 13 | - Apache Maven 3.8.4 o superior 14 | - JDK 17 o superior 15 | - Base de datos H2 (embebida) 16 | - Archivo `application.properties` correctamente configurado 17 | 18 | --- 19 | 20 | ## 🧰 ¿Qué veremos en este ejemplo? 21 | 22 | - Cómo configurar la conexión a la base de datos en `application.properties` 23 | - Cómo crear una entidad nueva (`Categoria`) 24 | - Cómo relacionarla con `Producto` usando anotaciones JPA (`@ManyToOne`, `@JoinColumn`) 25 | - Cómo verificar que las tablas se crean correctamente 26 | 27 | --- 28 | 29 | ## 📁 Estructura de entidades 30 | 31 | ### 🧱 `Categoria.java` 32 | 33 | ```java 34 | package com.bedu.inventario; 35 | 36 | import jakarta.persistence.*; 37 | 38 | @Entity 39 | public class Categoria { 40 | 41 | @Id // Esta anotación indica que el campo 'id' será la clave primaria de la tabla 42 | @GeneratedValue(strategy = GenerationType.IDENTITY) // El valor del ID se generará automáticamente (autoincremental) por la base de datos 43 | private Long id; 44 | 45 | private String nombre; 46 | 47 | protected Categoria() {} 48 | 49 | public Categoria(String nombre) { 50 | this.nombre = nombre; 51 | } 52 | 53 | public Long getId() { return id; } 54 | public String getNombre() { return nombre; } 55 | } 56 | ``` 57 | 58 | --- 59 | 60 | ### 🧱 Ajustes en `Producto.java` 61 | 62 | Además de agregar el campo `categoria` como una relación, es importante complementar la clase con los siguientes elementos: 63 | 64 | ```java 65 | // Justo después de los demás atributos 66 | @ManyToOne 67 | @JoinColumn(name = "categoria_id") // nombre de la columna FK en la tabla productos 68 | private Categoria categoria; 69 | ``` 70 | 71 | ```java 72 | // Dentro del constructor público (debajo de los otros parámetros) 73 | public Producto(String nombre, String descripcion, double precio, Categoria categoria) { 74 | this.nombre = nombre; 75 | this.descripcion = descripcion; 76 | this.precio = precio; 77 | this.categoria = categoria; 78 | } 79 | ``` 80 | 81 | ```java 82 | // Agregar un getter al final de los métodos de acceso 83 | public Categoria getCategoria() { 84 | return categoria; 85 | } 86 | ``` 87 | 88 | ```java 89 | // Dentro del método toString(), agrega la categoría de forma opcional 90 | @Override 91 | public String toString() { 92 | return String.format( 93 | "Producto[id=%d, nombre='%s', precio=%.2f, categoria='%s']", 94 | id, nombre, precio, categoria != null ? categoria.getNombre() : "Sin categoría" 95 | ); 96 | } 97 | ``` 98 | 99 | > 📌 Recuerda que estas modificaciones deben integrarse dentro de la clase `Producto`, en su ubicación correspondiente (atributos arriba, métodos abajo). No deben duplicarse fuera de la clase. 100 | 101 | --- 102 | 103 | ## ⚙️ Configuración de `application.properties` 104 | 105 | Asegúrate de tener este archivo dentro de `src/main/resources`: 106 | 107 | ```properties 108 | spring.datasource.url=jdbc:h2:mem:inventariodb 109 | spring.datasource.driver-class-name=org.h2.Driver 110 | spring.datasource.username=sa 111 | spring.datasource.password= 112 | 113 | spring.jpa.hibernate.ddl-auto=update 114 | spring.h2.console.enabled=true 115 | spring.h2.console.path=/h2-console 116 | ``` 117 | 118 | --- 119 | 120 | ## 🚀 Prueba rápida en `CommandLineRunner` 121 | 122 | Agrega este código en `InventarioApplication.java` para cargar una categoría y asociarla a productos: 123 | 124 | ```java 125 | @Bean 126 | public CommandLineRunner demo(ProductoRepository productoRepo, CategoriaRepository categoriaRepo) { 127 | return (args) -> { 128 | Categoria tecnologia = new Categoria("Tecnología"); 129 | categoriaRepo.save(tecnologia); 130 | 131 | productoRepo.save(new Producto("Laptop ASUS ROG Strix SCAR 18", "Intel Core i9, RTX 5090", 90000.00, tecnologia)); 132 | productoRepo.save(new Producto("Laptop MSI Titan 18 HX", "Intel Core i9, RTX 4090", 140000.00, tecnologia)); 133 | productoRepo.save(new Producto("Tablet Lenovo", "Pantalla 10 pulgadas", 7800.00, tecnologia)); 134 | 135 | System.out.println("📂 Productos registrados:"); 136 | productoRepo.findAll().forEach(p -> System.out.println(p.getNombre() + " - " + p.getCategoria().getNombre())); 137 | }; 138 | } 139 | ``` 140 | 141 | --- 142 | 143 | ## 🧪 Resultado esperado en consola 144 | 145 | Al ejecutar el proyecto, deberías ver una salida similar a esta: 146 | 147 | ``` 148 | 📂 Productos registrados: 149 | Laptop ASUS ROG Strix SCAR 18 - Tecnología 150 | Laptop MSI Titan 18 HX - Tecnología 151 | Tablet Lenovo - Tecnología 152 | ``` 153 | 154 | Esto confirma que: 155 | - Las entidades fueron creadas correctamente 156 | - La relación `@ManyToOne` entre `Producto` y `Categoria` funciona 157 | - Los datos se están consultando correctamente desde la base de datos embebida 158 | 159 | > 💡 Si no ves el nombre de la categoría o aparece como `null`, asegúrate de haber guardado la categoría antes de asociarla a los productos y de haber actualizado el método `toString()` y el getter. 160 | 161 | 162 | --- 163 | 164 | ## 🧪 Verificación en consola H2 165 | 166 | 1. Ejecuta la aplicación 167 | 2. Abre el navegador: [http://localhost:8080/h2-console](http://localhost:8080/h2-console) 168 | 3. Usa la URL: `jdbc:h2:mem:inventariodb` 169 | 4. Tabla `producto` debe tener una columna `categoria_id` 170 | 5. Puedes hacer consultas como: 171 | 172 | ```sql 173 | SELECT * FROM producto; 174 | SELECT * FROM categoria; 175 | ``` 176 | 177 | --- 178 | 179 | ## 🧠 Notas 180 | 181 | - `@ManyToOne` se usa cuando varios productos pertenecen a una misma categoría. 182 | - `@JoinColumn` permite personalizar el nombre de la columna de la clave foránea. 183 | - Spring Boot se encarga de crear automáticamente las tablas si `ddl-auto=update`. 184 | 185 | --- 186 | 187 | 188 | ## 💡 **¿Y si quiero usar MySQL o MariaDB?** 189 | > Aunque este ejemplo usa H2 por simplicidad, puedes cambiar fácilmente a MySQL o MariaDB modificando el archivo `application.properties` y agregando el conector correspondiente al `pom.xml`. Esto se recomienda como actividad adicional fuera de clase. 190 | 191 | 192 | Podríamos incluir un fragmento de configuración para MySQL como referencia: 193 | 194 | ```properties 195 | spring.datasource.url=jdbc:mysql://localhost:3306/inventario_db 196 | spring.datasource.username=root 197 | spring.datasource.password=123456 198 | spring.jpa.hibernate.ddl-auto=update 199 | ``` 200 | 201 | Y el conector en el `pom.xml`: 202 | 203 | ```xml 204 | 205 | com.mysql 206 | mysql-connector-j 207 | runtime 208 | 209 | ``` 210 | 211 | --- 212 | 213 | ## 📝 En resumen 214 | 215 | - Configuramos la **conexión a una base de datos** embebida (H2) en un proyecto **Spring Boot** usando **`application.properties`**. 216 | - Creamos una relación **`@ManyToOne`** entre **`Producto`** y **`Categoria`**, modelando un escenario típico de **tienda en línea** donde varios productos comparten la misma categoría. 217 | - Probamos la conexión y las entidades usando **`CommandLineRunner`**, cargando datos y verificando la **persistencia**. 218 | - También validamos el esquema en la **consola H2**, asegurándonos que las tablas y las relaciones se crearon correctamente. 219 | - Esta estructura facilita la **escalabilidad** del sistema, permitiendo agregar nuevas entidades o cambiar la base de datos (por ejemplo, de H2 a **MySQL** o **MariaDB**) sin modificar el código fuente de las entidades. 220 | 221 | --- 222 | 223 | 📘 Recursos útiles: 224 | 🔗 [Relaciones en JPA – Baeldung](https://www.baeldung.com/jpa-joincolumn-vs-mappedby) 225 | 🔗 [JPA Relationships – Official Guide](https://jakarta.ee/specifications/persistence/) 226 | 227 | --- 228 | 229 | ⬅️ [**Anterior**](../Reto-01/Readme.md) | [**Siguiente**](../Reto-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-06/Ejemplo-03/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 06**](../Readme.md) ➡️ / 📝 `Ejemplo 03: Aplicación web con CRUD y JPA` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Construir una aplicación Java web utilizando Spring Boot y JPA que permita exponer operaciones CRUD mediante un API REST, integrando base de datos, servicios y controladores. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - Proyecto Spring Boot con la entidad `Producto` 12 | - IntelliJ IDEA o editor compatible con Spring Boot 13 | - [Postman](https://www.postman.com/downloads/) o cualquier cliente REST 14 | - Dependencias: Spring Web, Spring Data JPA, H2 Database 15 | - Archivo `application.properties` correctamente configurado 16 | 17 | --- 18 | 19 | ## 🧱 Arquitectura y organización del proyecto 20 | 21 | ¿Has notado que cada vez hay más archivos en nuestro proyecto? 22 | Para mantener todo ordenado y escalable, este ejemplo sigue una arquitectura en capas, separando controladores, servicios, repositorios y modelos, con el fin de facilitar la escalabilidad y mantenibilidad del proyecto. 23 | 24 | | Capa | Ubicación en el proyecto | Rol | 25 | |--------------|-------------------------------------|-----| 26 | | **Modelo** | `models/` | Representa la estructura de datos (`Producto`) | 27 | | **Controlador** | `controllers/` | Expone endpoints que reciben peticiones HTTP | 28 | | **Servicio** | `services/` | Contiene la lógica de negocio entre el controlador y la base de datos | 29 | | **Repositorio** | `repositories/` | Interactúa con la base de datos usando JPA | 30 | 31 | > Esta separación **mejora la mantenibilidad, pruebas y escalabilidad** del proyecto. 32 | 33 | --- 34 | 35 | ## 📁 Estructura general 36 | 37 | ``` 38 | src/main/java/com/bedu/inventario/ 39 | ├── controllers/ 40 | │ └── ProductoController.java 41 | ├── services/ 42 | │ └── ProductoService.java 43 | ├── repositories/ 44 | │ └── ProductoRepository.java 45 | ├── models/ 46 | │ └── Producto.java 47 | └── InventarioApplication.java 48 | ``` 49 | 50 | --- 51 | 52 | ## 🧱 Paso 1: Crear el servicio `ProductoService` 53 | 54 | ```java 55 | import com.bedu.inventario.models.Producto; 56 | import com.bedu.inventario.repositories.ProductoRepository; 57 | import org.springframework.stereotype.Service; 58 | 59 | import java.util.List; 60 | 61 | @Service 62 | public class ProductoService { 63 | 64 | private final ProductoRepository repository; 65 | 66 | public ProductoService(ProductoRepository repository) { 67 | this.repository = repository; 68 | } 69 | 70 | public List obtenerTodos() { 71 | return repository.findAll(); 72 | } 73 | 74 | public Producto guardar(Producto producto) { 75 | return repository.save(producto); 76 | } 77 | } 78 | ``` 79 | 80 | --- 81 | 82 | ## 🌐 Paso 2: Crear el controlador `ProductoController` 83 | 84 | ```java 85 | import com.bedu.inventario.models.Producto; 86 | import com.bedu.inventario.services.ProductoService; 87 | import org.springframework.web.bind.annotation.*; 88 | 89 | import java.util.List; 90 | 91 | @RestController 92 | @RequestMapping("/api/productos") 93 | public class ProductoController { 94 | 95 | private final ProductoService servicio; 96 | 97 | public ProductoController(ProductoService servicio) { 98 | this.servicio = servicio; 99 | } 100 | 101 | @GetMapping 102 | public List obtenerProductos() { 103 | return servicio.obtenerTodos(); 104 | } 105 | 106 | @PostMapping 107 | public Producto crearProducto(@RequestBody Producto producto) { 108 | return servicio.guardar(producto); 109 | } 110 | } 111 | ``` 112 | 113 | --- 114 | 115 | ## ✅ Paso 3: implementación para `InventarioApplication.java` 116 | 117 | ```java 118 | import com.bedu.inventario.models.Producto; 119 | import com.bedu.inventario.repositories.ProductoRepository; 120 | import org.springframework.boot.*; 121 | import org.springframework.boot.autoconfigure.SpringBootApplication; 122 | import org.springframework.context.annotation.Bean; 123 | 124 | @SpringBootApplication 125 | public class InventarioApplication { 126 | 127 | public static void main(String[] args) { 128 | SpringApplication.run(InventarioApplication.class, args); 129 | } 130 | 131 | @Bean 132 | public CommandLineRunner initData(ProductoRepository productoRepo) { 133 | return args -> { 134 | productoRepo.save(new Producto("Laptop Lenovo", "AMD Ryzen 7, 16GB RAM", 18500.0)); 135 | productoRepo.save(new Producto("Mouse inalámbrico", "Marca Logitech, sensor óptico", 350.0)); 136 | productoRepo.save(new Producto("Monitor LG", "27 pulgadas, Full HD", 4300.0)); 137 | 138 | System.out.println("📦 Productos cargados:"); 139 | productoRepo.findAll().forEach(System.out::println); 140 | }; 141 | } 142 | } 143 | ``` 144 | 145 | --- 146 | 147 | ## 🧪 Pruebas con Postman (paso a paso) 148 | 149 | ### 🔹 1. Iniciar la aplicación 150 | 151 | Asegúrate de que la app esté corriendo en `http://localhost:8080`. 152 | 153 | --- 154 | 155 | ### 🔹 2. Obtener productos (GET) 156 | 157 | - **Método:** `GET` 158 | - **URL:** `http://localhost:8080/api/productos` 159 | - **Resultado esperado:** Lista de productos 160 | 161 | --- 162 | 163 | ### 🔹 3. Crear un producto (POST) 164 | 165 | - **Método:** `POST` 166 | - **URL:** `http://localhost:8080/api/productos` 167 | - **Headers:** 168 | - `Content-Type`: `application/json` 169 | - **Body (raw, JSON):** 170 | 171 | ```json 172 | { 173 | "nombre": "Smartwatch Xiaomi", 174 | "descripcion": "Pantalla AMOLED, resistente al agua", 175 | "precio": 1800.0 176 | } 177 | ``` 178 | 179 | --- 180 | 181 | ## 🧠 Notas 182 | 183 | - Este ejemplo marca el inicio del desarrollo de una **API REST completa** con Spring Boot. 184 | - El uso de `@RestController` permite exponer endpoints sin configuración adicional. 185 | - Esta versión está intencionadamente simplificada para enfocarse en CRUD sin relaciones. 186 | - Para versiones más complejas (con relaciones o lógica adicional), puedes usar DTOs, validaciones y controladores separados por recurso. 187 | 188 | --- 189 | 190 | ## 📝 En resumen 191 | 192 | - Implementamos una **API REST básica** en Java usando **Spring Boot** y **JPA**, siguiendo una **arquitectura en capas** (controladores, servicios, repositorios, modelos). 193 | - **`ProductoController`** expone los **endpoints HTTP** (`GET`, `POST`) para interactuar con los datos. 194 | - **`ProductoService`** encapsula la **lógica de negocio**, mientras que **`ProductoRepository`** maneja la **persistencia** mediante **Spring Data JPA**. 195 | - Probamos la API con **Postman**, simulando la creación y consulta de productos en la base de datos **H2 embebida**. 196 | - Esta estructura permite **extender fácilmente la aplicación**, añadiendo nuevas funcionalidades, relaciones entre entidades o integraciones externas. 197 | 198 | --- 199 | 200 | 📘 Recursos útiles: 201 | 🔗 [Spring Boot REST API](https://spring.io/guides/gs/rest-service) 202 | 🔗 [Postman – Guía oficial](https://learning.postman.com/docs/getting-started/introduction/) 203 | 204 | --- 205 | 206 | ⬅️ [**Anterior**](../Reto-02/Readme.md) | [**Siguiente**](../../Sesion-07/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-06/Imagenes/S06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-06/Imagenes/S06.jpg -------------------------------------------------------------------------------- /Sesion-06/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../Readme.md) ➡️ / 📖 `Sesión 06` 2 | 3 |
4 | Sesion_06 5 |
6 | 7 | # 🎯 Objetivo 8 | 9 | ⚒️ Conocer las distintas interfaces que Java proporciona para interactuar con bases de datos, así como los fundamentos del uso de JPA y la creación de entidades para implementar operaciones CRUD. 10 | 11 | --- 12 | 13 | 14 | 📘 Material del prework: 15 | Antes de comenzar con los ejercicios de esta sesión, recordemos que en el material de prework hemos cubierto los fundamentos teóricos que aplicaremos hoy. A lo largo de esta sesión, pondremos en práctica estos conceptos mediante una serie de ejercicios y retos diseñados para reforzar y validar nuestro entendimiento. 16 | 🔥¡Vamos a comenzar!🔥 17 | 18 | --- 19 | 20 | 21 | ## 📂 Temas de la sesión... 22 | 23 | ### 📖 Introducción a la conexión de bases de datos en Java 24 | Java permite conectarse con diversas bases de datos utilizando JDBC o mediante herramientas más modernas como JPA. 25 | 26 | 🔹 **Modelo de conexión en Java (JDBC)** 27 | 🔹 **Ventajas del uso de ORM** 28 | 🔹 **Conceptos de entidades, repositorios y persistencia** 29 | 30 | 📜 **[Ejemplo 01: JPA - Creación de entidades y repositorios](Ejemplo-01/Readme.md)** 31 | 🔥 **[Reto 01: Registro de productos para inventario](Reto-01/Readme.md)** 32 | 33 | --- 34 | 35 | ### 📖 Configuración de base de datos y entorno de desarrollo 36 | Para comenzar a trabajar con bases de datos en Java, se requiere configurar el entorno y definir las entidades que representarán las tablas. 37 | 38 | 🔹 **Configuración de conexión a base de datos embebida (H2)** 39 | 🔹 **Uso de Maven y dependencias para JPA/Hibernate** 40 | 🔹 **Configuración del archivo `application.properties`** 41 | 42 | 📜 **[Ejemplo 02: Configuración de conexión y creación de tablas](Ejemplo-02/Readme.md)** 43 | 🔥 **[Reto 02: Productos por marca en una tienda en línea](Reto-02/Readme.md)** 44 | 45 | --- 46 | 47 | ### 📖 Conexión a una base de datos desde una aplicación web 48 | Integra una base de datos a una aplicación Java web usando JPA con Spring Boot para implementar operaciones CRUD. 49 | 50 | 🔹 **Integración de JPA con Spring Boot** 51 | 🔹 **Controladores y servicios conectados a base de datos** 52 | 🔹 **Pruebas básicas con endpoints y Postman** 53 | 54 | 📜 **[Ejemplo 03: Aplicación web con CRUD y JPA](Ejemplo-03/Readme.md)** 55 | 56 | --- 57 | 58 | ⬅️ [**Anterior**](../Sesion-05/Readme.md) | [**Siguiente**](../Sesion-07/Readme.md)➡️ 59 | -------------------------------------------------------------------------------- /Sesion-06/Reto-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 06**](../Readme.md) ➡️ / ⚡ `Reto 01: Registro de productos para inventario` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Reforzar la creación de entidades con **JPA**, usando el mismo proyecto del ejemplo anterior para agregar validaciones y consultas específicas en la clase `Producto`, simulando un sistema de inventario más realista. 6 | 7 | --- 8 | 9 | ## 📝 Instrucciones 10 | 11 | 📌 **Importante:** 12 | **No es necesario crear un nuevo proyecto**. Este reto debe resolverse **utilizando el mismo proyecto creado durante el _Ejemplo 01_** (`inventario`), extendiendo la lógica de la clase `Producto` y del `ProductoRepository`. 13 | 14 | --- 15 | 16 | ### 🛠️ Tareas a realizar: 17 | 18 | 1. ✍️ Asegúrate de tener tu clase `Producto` creada con los siguientes atributos: 19 | - `id` (tipo `Long`, llave primaria generada automáticamente) 20 | - `nombre` (tipo `String`) 21 | - `descripcion` (tipo `String`) 22 | - `precio` (tipo `double`) 23 | 24 | 2. 🔒 Implementa las siguientes **validaciones en la entidad**: 25 | - `@NotBlank` en `nombre` y `descripcion` 26 | - `@Min(1)` en `precio` 27 | > Puedes usar `jakarta.validation.constraints` y asegurarte de tener la dependencia `spring-boot-starter-validation`. 28 | 29 | 3. 🔍 En `ProductoRepository`, agrega los siguientes métodos personalizados: 30 | ```java 31 | List findByPrecioGreaterThan(double precio); 32 | List findByNombreContainingIgnoreCase(String nombre); 33 | List findByPrecioBetween(double min, double max); 34 | List findByNombreStartingWithIgnoreCase(String prefijo); 35 | ``` 36 | 37 | 4. 🧪 Prueba estas consultas y validaciones desde `CommandLineRunner`: 38 | - Guarda al menos **4 productos** 39 | - Imprime todos los productos con precio mayor a `500` 40 | - Imprime todos los productos que contengan `"lap"` en su nombre 41 | - Imprime productos con precio entre `400` y `1000` 42 | - Imprime productos cuyo nombre comience con `"m"` o `"M"` 43 | 44 | 5. 🧾 Muestra la salida en consola con `System.out.println()` 45 | 46 | 47 | Al ejecutar el programa verás una salida similar a: 48 | 49 | ``` 50 | 📦 Productos con precio mayor a 500: 51 | Producto[id=1, nombre='Laptop Lenovo', precio=12500.00] 52 | Producto[id=3, nombre='Teclado Mecánico', precio=950.00] 53 | Producto[id=4, nombre='Monitor', precio=3200.00] 54 | 55 | 🔍 Productos que contienen 'lap': 56 | Producto[id=1, nombre='Laptop Lenovo', precio=12500.00] 57 | 58 | 🎯 Productos con precio entre 400 y 1000: 59 | Producto[id=3, nombre='Teclado Mecánico', precio=950.00] 60 | 61 | 📘 Productos cuyo nombre empieza con 'm': 62 | Producto[id=2, nombre='Mouse Logitech', precio=350.00] 63 | Producto[id=4, nombre='Monitor', precio=3200.00] 64 | ``` 65 | 66 | --- 67 | 68 | 📘 Recursos útiles: 69 | 🔗 [Spring Data JPA: Derived Query Methods](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.details) 70 | 🔗 [Jakarta Bean Validation (JSR 380)](https://jakarta.ee/specifications/bean-validation/) 71 | 72 | --- 73 | 74 | 🧠 **Nota:** 75 | Este reto busca que apliques prácticas reales de validación y consultas personalizadas. Piensa cómo esto puede escalar a un backend completo, y cómo estas consultas permiten construir endpoints en una API REST. 76 | 77 | --- 78 | 79 | 🏆 Si logras ver en consola los resultados filtrados correctamente según los criterios establecidos, ¡vas por muy buen camino! 80 | 81 | --- 82 | 83 | ⬅️ [**Anterior**](../Ejemplo-01/Readme.md) | [**Siguiente**](../Ejemplo-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-06/Reto-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 06**](../Readme.md) ➡️ / ⚡ `Reto 02: Productos por marca en una tienda en línea` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Reforzar el uso de relaciones en JPA mediante una entidad nueva llamada `Marca`, relacionada con `Producto`, simulando un modelo básico de una tienda en línea. Se trabajará con relaciones `@ManyToOne`, ideal para representar que varios productos pertenecen a una marca. 6 | 7 | --- 8 | 9 | ## 📝 Instrucciones 10 | 11 | 📌 **Importante:** 12 | Este reto se realiza **en el mismo proyecto del Ejemplo 02**, reutilizando la entidad `Producto` y agregando una nueva entidad `Marca`. 13 | 14 | --- 15 | 16 | ### 🛠️ Tareas a realizar: 17 | 18 | 1. ✍️ Crea una nueva clase `Marca` con los siguientes atributos: 19 | - `id` (clave primaria, autogenerada) 20 | - `nombre` (nombre de la marca) 21 | 22 | 2. 🔁 Relaciona `Producto` con `Marca` usando `@ManyToOne`: 23 | 24 | ```java 25 | @ManyToOne 26 | @JoinColumn(name = "marca_id") 27 | private Marca marca; 28 | ``` 29 | 30 | 3. 🔄 Agrega en `Producto`: 31 | - Constructor con parámetro `Marca` 32 | - Getter para `getMarca()` 33 | 34 | 4. 🧪 Desde `CommandLineRunner`, realiza lo siguiente: 35 | - Crea al menos **2 marcas** 36 | - Asocia al menos **2 productos a cada marca** 37 | - Muestra los productos agrupados por marca: 38 | 39 | ```java 40 | System.out.println("📚 Productos por marca:"); 41 | marcaRepo.findAll().forEach(marca -> { 42 | System.out.println("🏷️ " + marca.getNombre() + ":"); 43 | productoRepo.findAll().stream() 44 | .filter(p -> p.getMarca().getId().equals(marca.getId())) 45 | .forEach(p -> System.out.println(" - " + p.getNombre())); 46 | }); 47 | ``` 48 | 49 | 5. 🧾 Asegúrate de crear un `MarcaRepository` que extienda `JpaRepository`. 50 | 51 | 6. 🧾 Muestra la salida en consola con `System.out.println()` 52 | 53 | 54 | Al ejecutar el programa verás una salida similar a: 55 | 56 | ``` 57 | 📚 Productos por marca: 58 | 🏷️ Apple: 59 | - iPhone 15 60 | - iPad Pro 61 | 🏷️ Samsung: 62 | - Galaxy S23 63 | - Smart TV 64 | ``` 65 | 66 | --- 67 | 68 | 📘 Recursos útiles: 69 | 🔗 [Spring Data JPA – Query Methods](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods) 70 | 🔗 [Relaciones JPA – Baeldung](https://www.baeldung.com/jpa-joincolumn-vs-mappedby) 71 | 72 | --- 73 | 74 | 🏆 Si logras ver productos agrupados correctamente por marca en la consola, ¡reto completado con éxito! 75 | 76 | --- 77 | 78 | ⬅️ [**Anterior**](../Ejemplo-02/Readme.md) | [**Siguiente**](../Ejemplo-03/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-07/Ejemplo-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 07**](../Readme.md) ➡️ / 📝 `Ejemplo 01: Comparativa entre monolito y microservicios` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔍 Entender las **diferencias clave** entre una **arquitectura monolítica** y una **arquitectura de microservicios**, explorando cómo se organizan, despliegan y escalan las aplicaciones en cada enfoque. 6 | 7 | --- 8 | 9 | ## ⚙️ Requisitos 10 | 11 | - Conocimientos básicos de desarrollo de software 12 | - Familiaridad con conceptos de arquitectura de aplicaciones 13 | 14 | --- 15 | 16 | ## 🧠 Contexto del ejemplo 17 | 18 | Imagina que desarrollas una **plataforma de experiencias en videojuegos de realidad virtual (VR)**. Esta plataforma permite a los usuarios **explorar un catálogo de juegos VR**, **registrar sus sesiones**, **gestionar perfiles de jugadores** y **analizar estadísticas de desempeño** (como tiempo de juego, calorías quemadas, etc.). 19 | 20 | Exploraremos cómo se podría diseñar esta plataforma en dos arquitecturas: 21 | 22 | 1. **Monolito**: Toda la lógica (usuarios, catálogo, sesiones, estadísticas) en una sola aplicación. 23 | 2. **Microservicios**: Cada módulo (usuarios, catálogo, sesiones, estadísticas) se gestiona como un servicio independiente. 24 | 25 | Este ejemplo nos ayudará a visualizar cómo cambia la **escalabilidad**, **mantenimiento** y **flexibilidad** dependiendo de la arquitectura. 26 | 27 | --- 28 | 29 | ## 📂 Estructura del proyecto 30 | 31 | ``` 32 | Ejemplo-01/ 33 | │ 34 | ├── Monolito/ 35 | │ └── PlataformaVRMonolito.java 36 | │ 37 | └── Microservicios/ 38 | ├── ServicioUsuarios.java 39 | ├── ServicioCatalogo.java 40 | ├── ServicioSesiones.java 41 | └── ServicioEstadisticas.java 42 | ``` 43 | 44 | --- 45 | 46 | ## 💻 Código 47 | 48 | ### 🧩 Monolito 49 | 50 | ```java 51 | // PlataformaVRMonolito.java 52 | public class PlataformaVRMonolito { 53 | public void gestionarExperienciaVR() { 54 | System.out.println("👤 Gestionando perfil de usuario..."); 55 | System.out.println("🎮 Mostrando catálogo de juegos VR..."); 56 | System.out.println("🕶️ Registrando nueva sesión de juego..."); 57 | System.out.println("📊 Actualizando estadísticas de rendimiento..."); 58 | } 59 | 60 | public static void main(String[] args) { 61 | PlataformaVRMonolito app = new PlataformaVRMonolito(); 62 | app.gestionarExperienciaVR(); 63 | } 64 | } 65 | ``` 66 | 67 | ### 🧩 Microservicios 68 | 69 | ```java 70 | // ServicioUsuarios.java 71 | public class ServicioUsuarios { 72 | // Servicio encargado de gestionar los perfiles de usuario 🎮 73 | public void gestionarUsuario() { 74 | System.out.println("👤 Gestionando perfil de usuario..."); 75 | } 76 | } 77 | 78 | // ServicioCatalogo.java 79 | public class ServicioCatalogo { 80 | // Servicio que muestra el catálogo de juegos VR disponibles 🕹️ 81 | public void mostrarCatalogo() { 82 | System.out.println("🎮 Mostrando catálogo de juegos VR..."); 83 | } 84 | } 85 | 86 | // ServicioSesiones.java 87 | public class ServicioSesiones { 88 | // Servicio que registra las sesiones de juego de los usuarios 🕶️ 89 | public void registrarSesion() { 90 | System.out.println("🕶️ Registrando nueva sesión de juego..."); 91 | } 92 | } 93 | 94 | // ServicioEstadisticas.java 95 | public class ServicioEstadisticas { 96 | // Servicio que actualiza las estadísticas de los jugadores 📊 97 | public void actualizarEstadisticas() { 98 | System.out.println("📊 Actualizando estadísticas de rendimiento..."); 99 | } 100 | } 101 | ``` 102 | 103 | ```java 104 | // Main.java 105 | public class Main { 106 | public static void main(String[] args) { 107 | ServicioUsuarios usuarios = new ServicioUsuarios(); 108 | ServicioCatalogo catalogo = new ServicioCatalogo(); 109 | ServicioSesiones sesiones = new ServicioSesiones(); 110 | ServicioEstadisticas estadisticas = new ServicioEstadisticas(); 111 | 112 | usuarios.gestionarUsuario(); 113 | catalogo.mostrarCatalogo(); 114 | sesiones.registrarSesion(); 115 | estadisticas.actualizarEstadisticas(); 116 | } 117 | } 118 | ``` 119 | 120 | --- 121 | 122 | ## 🧪 Resultado esperado 123 | 124 | ``` 125 | 👤 Gestionando perfil de usuario... 126 | 🎮 Mostrando catálogo de juegos VR... 127 | 🕶️ Registrando nueva sesión de juego... 128 | 📊 Actualizando estadísticas de rendimiento... 129 | ``` 130 | 131 | 132 | --- 133 | 134 | ## 📝 Conceptos clave utilizados 135 | 136 | - **Monolito**: Toda la lógica (usuarios, catálogo, sesiones, estadísticas) se encuentra en una sola aplicación. 137 | - **Microservicios**: Servicios separados que se comunican entre sí, cada uno enfocado en una funcionalidad específica. 138 | - **Escalabilidad horizontal**: Capacidad para escalar servicios individuales (por ejemplo, estadísticas de sesiones VR que requieren procesamiento en tiempo real). 139 | - **Despliegue independiente**: Cada servicio puede ser desplegado, actualizado o escalado por separado. 140 | 141 | --- 142 | 143 | ## 🔍 Comparativa rápida 144 | 145 | | Característica | Monolito | Microservicios | 146 | |--------------------|---------------------------------------|---------------------------------------| 147 | | **Despliegue** | Único artefacto | Múltiples servicios independientes | 148 | | **Escalabilidad** | Escalado completo | Escalado por servicio específico | 149 | | **Mantenimiento** | Difícil en sistemas grandes | Más sencillo al estar modularizado | 150 | | **Tecnologías** | Una sola tecnología | Variedad por servicio (si se desea) | 151 | | **Comunicación** | Interna | Red (HTTP, colas de mensajes, etc.) | 152 | 153 | --- 154 | 155 | ## 📚 Recursos adicionales 156 | 157 | - [Microservices vs Monolith – GeeksforGeeks](https://www.geeksforgeeks.org/monolithic-vs-microservices-architecture/) 158 | - [12 Factor App](https://12factor.net/) 159 | - [Microservices – A Practical Guide](https://dev.to/brilworks/building-microservices-in-java-a-practical-guide-49h8) 160 | 161 | --- 162 | 163 | ## 🚀 Resumen 164 | 165 | Este ejemplo mostró cómo una **plataforma de experiencias VR** puede estructurarse como un **monolito** o como una **arquitectura de microservicios**. Mientras el **monolito** puede ser más simple para aplicaciones pequeñas o con poca demanda, los **microservicios** permiten escalar y mantener partes específicas (como **estadísticas en tiempo real**) sin afectar el resto del sistema. 166 | 167 | > **Tip final:** En sistemas que involucran **realidad virtual**, donde algunas partes requieren **procesamiento intensivo** (como estadísticas o sesiones), **los microservicios** permiten escalar esos componentes sin sobrecargar la plataforma completa. 168 | 169 | --- 170 | 171 | ⬅️ [**Anterior**](../Readme.md) | [**Siguiente**](../Ejemplo-02/Readme.md) ➡️ -------------------------------------------------------------------------------- /Sesion-07/Ejemplo-03/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 07**](../Readme.md) ➡️ / 📝 `Ejemplo 03: Comunicación entre microservicios` 2 | 3 | ## 🎯 Objetivo 4 | 5 | 🔗 Comprender cómo se realiza la **comunicación entre microservicios** utilizando **REST** y **JSON** como mecanismos de intercambio de datos. 6 | Además, conocerás el concepto de **API Gateway** y utilizarás **Postman** o **Swagger** para probar las interacciones entre servicios. 7 | 8 | --- 9 | 10 | ## ⚙️ Requisitos 11 | 12 | - JDK 17 o superior 13 | - IDE compatible con Java (IntelliJ IDEA, Eclipse, VSCode) 14 | - Maven o Gradle instalado 15 | - **Postman** o **Swagger** (opcional para pruebas) 16 | 17 | --- 18 | 19 | ## 🧠 Contexto del ejemplo 20 | 21 | En un sistema basado en **microservicios**, los módulos no se comunican directamente a través de métodos internos, sino mediante **solicitudes HTTP REST** que intercambian datos en **formato JSON**. 22 | 23 | En este ejemplo tendrás **dos microservicios independientes**: 24 | 25 | 1. **Servicio de Empleados**: expone información de empleados (nombre, ID, idDepartamento). 26 | 2. **Servicio de Departamentos**: devuelve el nombre del departamento dado un **idDepartamento**. 27 | 28 | El **Servicio de Empleados** se comunica vía **HTTP REST** con el **Servicio de Departamentos** para enriquecer su información y devolver a los clientes un **JSON combinado**. 29 | 30 | --- 31 | 32 | ## 📂 Estructura del proyecto 33 | 34 | ``` 35 | Ejemplo-03/ 36 | │ 37 | ├── departamento_service/ <-- Proyecto Maven independiente 38 | │ └── src/main/java/com/bedu/departamento_service/controller/ 39 | │ └── DepartamentoController.java 40 | │ 41 | └── empleado_service/ <-- Proyecto Maven independiente 42 | └── src/main/java/com/bedu/empleado_service/controller/ 43 | └── EmpleadoController.java 44 | ``` 45 | 46 | 🔔 **Importante:** 47 | Cada microservicio es un **proyecto Maven independiente** con su propio `pom.xml` y estructura de carpetas. 48 | No se deben crear como paquetes dentro del mismo proyecto. **Cada uno se ejecuta por separado** y se comunica mediante HTTP. 49 | 50 | --- 51 | 52 | ## ⚙️ ¿Cómo crear cada microservicio? 53 | 54 | ### Opción 1: Usando [https://start.spring.io](https://start.spring.io) (Recomendado) 55 | 56 | 1. Abre [https://start.spring.io](https://start.spring.io). 57 | 2. Configura: 58 | - **Group:** `com.bedu` 59 | - **Artifact:** `departamento_service` (o `empleado_service`) 60 | - **Dependencies:** `Spring Web` 61 | 3. Descarga el proyecto y descomprímelo en la carpeta correspondiente (`departamento_service/` o `empleado_service/`). 62 | 4. Abre cada proyecto en tu IDE. 63 | 64 | ### Opción 2: Crear proyecto Maven desde tu IDE 65 | 66 | 1. En IntelliJ IDEA: 67 | - File → New → Project → Maven 68 | - Configura el **GroupId** y **ArtifactId** como corresponde. 69 | 2. Agrega el siguiente bloque en el `pom.xml`: 70 | 71 | ```xml 72 | 73 | org.springframework.boot 74 | spring-boot-starter-parent 75 | 3.4.5 76 | 77 | 78 | ``` 79 | 80 | Y las dependencias necesarias: 81 | 82 | ```xml 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-starter-web 87 | 88 | 89 | org.springframework.boot 90 | spring-boot-starter-test 91 | test 92 | 93 | 94 | ``` 95 | 96 | --- 97 | 98 | ## 💻 Código 99 | 100 | ### 🧩 Microservicio: **departamento_service** 101 | 102 | ```java 103 | // DepartamentoController.java 104 | package com.bedu.departamento_service.controller; 105 | 106 | import org.springframework.web.bind.annotation.*; 107 | 108 | import java.util.Map; 109 | 110 | @RestController 111 | @RequestMapping("/api/departamentos/") // Nota: Incluye slash final para evitar confusión 112 | public class DepartamentoController { 113 | 114 | // Simula una base de datos con un Map inmutable de departamentos (ID -> Nombre). 115 | private static final Map DEPARTAMENTOS = Map.of( 116 | 1L, "Marketing", 117 | 2L, "IT", 118 | 3L, "Finanzas" 119 | ); 120 | 121 | // Endpoint que devuelve el nombre del departamento según el ID proporcionado. 122 | @GetMapping("/{id}") 123 | public String obtenerDepartamento(@PathVariable Long id) { 124 | return DEPARTAMENTOS.getOrDefault(id, "Desconocido"); 125 | } 126 | 127 | // Endpoint que devuelve todos los departamentos en formato JSON. 128 | @GetMapping 129 | public Map obtenerTodosLosDepartamentos() { 130 | return DEPARTAMENTOS; 131 | } 132 | } 133 | ``` 134 | 135 | --- 136 | 137 | ### 🧩 Microservicio: **empleado_service** 138 | 139 | ```java 140 | // EmpleadoController.java 141 | package com.bedu.empleado_service.controller; 142 | 143 | import org.springframework.web.bind.annotation.*; 144 | import org.springframework.web.client.RestTemplate; 145 | 146 | import java.util.List; 147 | import java.util.Map; 148 | 149 | @RestController 150 | @RequestMapping("/api/empleados") 151 | public class EmpleadoController { 152 | 153 | // Simula una base de datos de empleados como una lista inmutable de mapas (cada mapa representa un empleado). 154 | private static final List> EMPLEADOS = List.of( 155 | Map.of("id", 1, "nombre", "Ana Gómez", "idDepartamento", 1L), 156 | Map.of("id", 2, "nombre", "Carlos Pérez", "idDepartamento", 2L) 157 | ); 158 | 159 | // RestTemplate es un cliente HTTP que permite consumir otros servicios REST. 160 | private final RestTemplate restTemplate = new RestTemplate(); 161 | 162 | // Endpoint que devuelve la lista de empleados, agregando cada empleado con el nombre del departamento. 163 | @GetMapping 164 | public List> obtenerEmpleados() { 165 | // Recorre cada empleado de la lista original 166 | return EMPLEADOS.stream().map(empleado -> { 167 | // Obtiene el ID del departamento del empleado actual 168 | Long idDepto = (Long) empleado.get("idDepartamento"); 169 | 170 | // Realiza una llamada HTTP GET al microservicio departamento_service para obtener el nombre del departamento 171 | String departamento = restTemplate.getForObject( 172 | "http://localhost:8081/api/departamentos/" + idDepto, // URL construida dinámicamente con el idDepartamento 173 | String.class // Se espera una respuesta en formato String (nombre del departamento) 174 | ); 175 | 176 | // Crea una copia mutable del mapa de empleado original (porque List.of() y Map.of() generan colecciones inmutables) 177 | Map empleadoModificado = new java.util.HashMap<>(empleado); 178 | 179 | // Agrega el nombre del departamento al mapa del empleado 180 | empleadoModificado.put("departamento", departamento); 181 | 182 | // Devuelve el empleado enriquecido con la información del departamento 183 | return empleadoModificado; 184 | }).toList(); // Recolecta todos los empleados modificados en una nueva lista 185 | } 186 | } 187 | ``` 188 | 189 | --- 190 | 191 | ### ⚙️ Configuración de puertos 192 | 193 | Cada microservicio debe correr en **puertos distintos**. Agrega en el `application.properties` de cada uno: 194 | 195 | #### departamento_service (puerto 8081): 196 | 197 | ```properties 198 | server.port=8081 199 | ``` 200 | 201 | #### empleado_service (puerto 8080): 202 | 203 | ```properties 204 | server.port=8080 205 | ``` 206 | 207 | --- 208 | 209 | ## 🧪 Cómo probar la comunicación 210 | 211 | 1. Arranca **departamento_service** (escuchando en `localhost:8081`). 212 | 2. Arranca **empleado_service** (escuchando en `localhost:8080`). 213 | 3. Abre **Postman** o tu navegador en: 214 | 215 | ``` 216 | http://localhost:8080/api/empleados 217 | ``` 218 | 219 | 4. Deberías ver un **JSON combinado**: 220 | 221 | ```json 222 | [ 223 | { "id": 1, "nombre": "Ana Gómez", "idDepartamento": 1, "departamento": "Marketing" }, 224 | { "id": 2, "nombre": "Carlos Pérez", "idDepartamento": 2, "departamento": "IT" } 225 | ] 226 | ``` 227 | 228 | --- 229 | 230 | ## 📝 Conceptos clave utilizados 231 | 232 | - **REST**: Patrón para comunicación entre servicios usando HTTP. 233 | - **JSON**: Formato estándar para intercambiar datos entre microservicios. 234 | - **RestTemplate**: Cliente HTTP en Spring para consumir otros servicios REST. 235 | - **API Gateway**: Punto central de entrada que coordina las llamadas a múltiples microservicios (no implementado, solo explicado). 236 | 237 | --- 238 | 239 | ## 🚀 Resumen 240 | 241 | Este ejemplo mostró cómo dos **microservicios independientes** se comunican mediante **HTTP REST** y **JSON**. Aprendiste a usar **RestTemplate** para realizar solicitudes entre servicios y comprendiste el rol que juega un **API Gateway** como punto central para coordinar las comunicaciones en arquitecturas basadas en microservicios. 242 | 243 | > **Tip final:** Aunque en este ejemplo se usa **RestTemplate**, en proyectos más robustos puedes utilizar **Feign Client** o **Spring Cloud Gateway** para facilitar la comunicación entre microservicios. 244 | 245 | --- 246 | 247 | ⬅️ [**Anterior**](../Ejemplo-02/Readme.md) | [**Siguiente**](../../Sesion-08/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-07/Imagenes/S07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-07/Imagenes/S07.jpg -------------------------------------------------------------------------------- /Sesion-07/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../Readme.md) ➡️ / 📖 `Sesión 07` 2 | 3 |
4 | Sesion_07 5 |
6 | 7 | # 🎯 Objetivo 8 | 9 | ⚒️ Introducir los fundamentos de la **arquitectura de microservicios**, comprender su estructura básica, las ventajas frente a monolitos, y crear una **API REST simple** usando **Spring Boot**. 10 | 11 | --- 12 | 13 | 📘 Material del prework: 14 | Hemos explorado **diferencias entre monolitos y microservicios**, entendiendo los principios básicos de **desacoplamiento** y **escalabilidad**. Hoy pondremos en práctica estos conceptos creando una **API REST básica**. 15 | 16 | 🔥 ¡Vamos a comenzar! 🔥 17 | 18 | --- 19 | 20 | ## 📂 Temas de la sesión... 21 | 22 | ### 📖 Arquitectura de microservicios 23 | 24 | 🔹 Principios básicos y diferencias con monolitos 25 | 🔹 Ventajas y desventajas 26 | 🔹 Casos reales de uso 27 | 28 | 📜 **[Ejemplo 01: Comparativa entre monolito y microservicios](Ejemplo-01/Readme.md)** 29 | 30 | --- 31 | 32 | ### 📖 Introducción a Spring Boot – Creación de una API REST simple 33 | 34 | 🔹 Estructura básica de un proyecto 35 | 🔹 Controladores y servicios 36 | 37 | 📜 **[Ejemplo 02: API REST básica con Spring Boot](Ejemplo-02/Readme.md)** 38 | 🔥 **[Reto 01: Expandiendo la API REST de empleados en RRHH](Reto-01/Readme.md)** 39 | 40 | --- 41 | 42 | ### 📖 Comunicación entre servicios 43 | 44 | 🔹 REST y JSON 45 | 🔹 Concepto de API Gateway 46 | 🔹 Uso de Postman o Swagger para pruebas 47 | 48 | 📜 **[Ejemplo 03: Comunicación entre microservicios](Ejemplo-03/Readme.md)** 49 | 50 | --- 51 | 52 | ⬅️ [**Anterior**](../Sesion-06/Readme.md) | [**Siguiente**](../Sesion-08/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-07/Reto-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 07**](../Readme.md) ➡️ / ⚡ `Reto 01: Expandiendo la API REST de empleados en RRHH` 2 | 3 | ## 🎯 Objetivo 4 | 5 | ⚒️ Extender una **API REST** con **Spring Boot** para incluir nuevas funcionalidades en la **gestión de empleados**, como **buscar empleados por puesto** o **eliminar registros**. 6 | 7 | Esto te permitirá practicar la creación de **nuevos endpoints REST** y aplicar buenas prácticas en el diseño de **APIs escalables**. 8 | 9 | --- 10 | 11 | ## 🧠 Contexto del reto 12 | 13 | Imagina que el área de **Recursos Humanos (RRHH)** de tu empresa necesita **ampliar la API REST** desarrollada en el ejemplo anterior para ofrecer más funcionalidades: 14 | 15 | 1. 🔍 **Buscar empleados por puesto** (ejemplo: "Desarrollador Backend"). 16 | 2. 🗑️ **Eliminar empleados** por su **ID**. 17 | 18 | Estas mejoras harán la API más completa y flexible, facilitando la **gestión de empleados** desde cualquier sistema. 19 | 20 | --- 21 | 22 | ## 📝 Instrucciones 23 | 24 | 1. **Agrega un nuevo endpoint GET** en `EmpleadoController`: 25 | 26 | - **Ruta**: `/api/empleados/puesto/{puesto}` 27 | - **Funcionalidad**: Devuelve una **lista de empleados** que coincidan con el **puesto** especificado. 28 | 29 | 2. **Agrega un endpoint DELETE** en `EmpleadoController`: 30 | 31 | - **Ruta**: `/api/empleados/{id}` 32 | - **Funcionalidad**: **Elimina** el empleado con el **ID** proporcionado. 33 | 34 | 3. **Modifica `EmpleadoService`** para incluir los métodos: 35 | 36 | - `List buscarPorPuesto(String puesto)` 37 | - `void eliminarEmpleado(Long id)` 38 | 39 | 4. **Prueba los endpoints** utilizando **Postman** o **curl**: 40 | 41 | - Buscar por puesto (ejemplo): 42 | 43 | ``` 44 | GET http://localhost:8080/api/empleados/puesto/Desarrollador Backend 45 | ``` 46 | 47 | - Eliminar empleado por ID (ejemplo): 48 | 49 | ``` 50 | DELETE http://localhost:8080/api/empleados/2 51 | ``` 52 | 53 | --- 54 | 55 | ## 💪 Desafío adicional (opcional) 56 | 57 | - Implementa una **validación** para que el endpoint DELETE devuelva un **mensaje de error** si el **ID** no existe. 58 | 59 | --- 60 | 61 | ## 💡 Ejemplo de salida esperada 62 | 63 | ``` 64 | 🔍 Buscar empleados por puesto: "Desarrollador Backend" 65 | [ 66 | { "id": 2, "nombre": "Carlos Pérez", "puesto": "Desarrollador Backend", "salario": 45000 } 67 | ] 68 | 69 | 🗑️ Eliminar empleado con ID: 2 70 | ✅ Empleado con ID 2 eliminado correctamente. 71 | 72 | 📋 Lista actualizada de empleados: 73 | [ 74 | { "id": 1, "nombre": "Ana Gómez", "puesto": "Gerente de Marketing", "salario": 55000 } 75 | ] 76 | ``` 77 | 78 | --- 79 | 80 | 🏆 Si logras **buscar y eliminar empleados** correctamente a través de la **API REST**, ¡reto completado! 81 | 82 | --- 83 | 84 | ⬅️ [**Anterior**](../Ejemplo-02/Readme.md) | [**Siguiente**](../Ejemplo-03/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-08/Ejemplo-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 08**](../Readme.md) ➡️ / 📝 `Ejemplo 01: Aplicación de principios SOLID` 2 | 3 | --- 4 | 5 | ## 🎯 Objetivo 6 | 7 | ⚒️ Aplicar **principios SOLID** para mejorar el diseño de un sistema de **notificaciones** en Java. Identificarás un ejemplo con **malas prácticas** (violando SRP y DIP) y lo **refactorizarás** para lograr un código **modular, extensible y fácil de mantener**. 8 | 9 | --- 10 | 11 | ## ⚙️ Requisitos 12 | 13 | - JDK 17 o superior 14 | - IDE compatible con Java (IntelliJ IDEA, Eclipse, VSCode) 15 | - Maven instalado (opcional, si deseas estructurar el proyecto como Maven) 16 | 17 | --- 18 | 19 | ## 🔤 ¿Qué es SOLID? 20 | 21 | **SOLID** es un acrónimo que agrupa **cinco principios fundamentales del diseño orientado a objetos**, pensados para crear sistemas **flexibles, mantenibles y escalables**. 22 | 23 | | Letra | Principio | ¿Qué significa? | 24 | |--------|------------------------------------------------|---------------------------------------------------------------------------| 25 | | **S** | **Single Responsibility Principle (SRP)** | Una clase debe tener **una única responsabilidad** o razón para cambiar. | 26 | | **O** | **Open/Closed Principle (OCP)** | El código debe estar **abierto a extensión, pero cerrado a modificación**.| 27 | | **L** | **Liskov Substitution Principle (LSP)** | Las clases hijas deben poder **sustituir a las clases padres** sin romper el programa.| 28 | | **I** | **Interface Segregation Principle (ISP)** | Evitar interfaces **grandes y generales**, mejor **interfaces pequeñas y específicas**.| 29 | | **D** | **Dependency Inversion Principle (DIP)** | Depender de **abstracciones** (interfaces), no de implementaciones concretas.| 30 | 31 | En este ejemplo, aplicaremos **SRP** y **DIP**, que son dos de los pilares más importantes para mantener el código **modular y desacoplado**. 32 | 33 | --- 34 | 35 | ## 🧠 Contexto del ejemplo 36 | 37 | En el desarrollo diario es común encontrar clases que **hacen demasiadas cosas** (violando SRP) o que **dependen directamente de implementaciones concretas** (violando DIP). 38 | Esto **dificulta las pruebas, el mantenimiento y la extensión del código**. 39 | 40 | En este ejemplo: 41 | 42 | 1. Verás un **NotificationService** que gestiona **varios tipos de notificaciones** de forma incorrecta (mezclando responsabilidades). 43 | 2. Refactorizaremos ese código **aplicando SRP (Single Responsibility Principle)** y **DIP (Dependency Inversion Principle)**. 44 | 45 | --- 46 | 47 | ## 📂 Estructura del proyecto 48 | 49 | ``` 50 | Ejemplo-01/ 51 | ├── sin_solid/ <-- Código sin aplicar SOLID (mal diseño) 52 | │ └── NotificationService.java 53 | └── con_solid/ <-- Código refactorizado aplicando SRP y DIP 54 | ├── NotificationSender.java (Interfaz) 55 | ├── EmailSender.java (Implementación de correo) 56 | ├── SmsSender.java (Implementación de SMS) 57 | └── NotificationService.java (Servicio que usa la abstracción) 58 | ``` 59 | 60 | --- 61 | 62 | ## 💻 Código 63 | 64 | ### 🔴 **Código inicial (sin SOLID)** 65 | 66 | ```java 67 | // sin_solid/NotificationService.java 68 | public class NotificationService { 69 | 70 | public void sendNotification(String message, String type) { 71 | if ("email".equals(type)) { 72 | System.out.println("Enviando correo: " + message); 73 | // Lógica específica de envío de email 74 | } else if ("sms".equals(type)) { 75 | System.out.println("Enviando SMS: " + message); 76 | // Lógica específica de envío de SMS 77 | } else { 78 | System.out.println("Tipo de notificación desconocido"); 79 | } 80 | } 81 | } 82 | ``` 83 | 84 | ### 🚨 Problemas del diseño: 85 | 86 | - ❌ **Viola SRP**: La clase gestiona múltiples tipos de notificaciones (correo y SMS). 87 | - ❌ **Viola DIP**: Depende de detalles concretos (lógica interna de email y SMS). 88 | - ❌ **Difícil de extender**: Si quieres agregar WhatsApp, Push, etc., tendrías que **modificar esta clase**. 89 | 90 | --- 91 | 92 | ### ✅ **Código refactorizado (aplicando SRP y DIP)** 93 | 94 | #### **1. Interfaz NotificationSender** 95 | 96 | ```java 97 | // con_solid/NotificationSender.java 98 | public interface NotificationSender { 99 | void send(String message); 100 | } 101 | ``` 102 | 103 | --- 104 | 105 | #### **2. Implementación de correo (EmailSender)** 106 | 107 | ```java 108 | // con_solid/EmailSender.java 109 | public class EmailSender implements NotificationSender { 110 | 111 | @Override 112 | public void send(String message) { 113 | System.out.println("Enviando correo: " + message); 114 | // Lógica específica de email 115 | } 116 | } 117 | ``` 118 | 119 | --- 120 | 121 | #### **3. Implementación de SMS (SmsSender)** 122 | 123 | ```java 124 | // con_solid/SmsSender.java 125 | public class SmsSender implements NotificationSender { 126 | 127 | @Override 128 | public void send(String message) { 129 | System.out.println("Enviando SMS: " + message); 130 | // Lógica específica de SMS 131 | } 132 | } 133 | ``` 134 | 135 | --- 136 | 137 | #### **4. Servicio de notificaciones (NotificationService)** 138 | 139 | ```java 140 | // con_solid/NotificationService.java 141 | public class NotificationService { 142 | 143 | private final NotificationSender sender; 144 | 145 | // Inyección de la abstracción (no depende de implementaciones concretas) 146 | public NotificationService(NotificationSender sender) { 147 | this.sender = sender; 148 | } 149 | 150 | public void sendNotification(String message) { 151 | sender.send(message); 152 | } 153 | } 154 | ``` 155 | 156 | --- 157 | 158 | #### **5. Clase principal (Main)** 159 | 160 | ```java 161 | // con_solid/Main.java 162 | public class Main { 163 | public static void main(String[] args) { 164 | // Cambia la implementación sin modificar NotificationService 165 | NotificationSender emailSender = new EmailSender(); 166 | NotificationService emailService = new NotificationService(emailSender); 167 | emailService.sendNotification("Bienvenido al sistema!"); 168 | 169 | NotificationSender smsSender = new SmsSender(); 170 | NotificationService smsService = new NotificationService(smsSender); 171 | smsService.sendNotification("Tu código es 1234."); 172 | } 173 | } 174 | ``` 175 | 176 | --- 177 | 178 | ## 📝 Comparación antes y después 179 | 180 | | Aspecto | Sin SOLID | Con SOLID | 181 | |---------------------------|---------------------------------------------|--------------------------------------------| 182 | | **Responsabilidad** | Mezcla lógica de varios tipos de envío | Cada clase tiene una única responsabilidad | 183 | | **Dependencias** | Depende de detalles concretos (email, SMS) | Depende de abstracciones (interfaces) | 184 | | **Extensibilidad** | Difícil (se debe modificar la clase) | Fácil (solo agregas nuevas implementaciones)| 185 | | **Mantenibilidad** | Baja (todo está acoplado) | Alta (código modular y flexible) | 186 | 187 | --- 188 | 189 | ## 🚀 Resumen 190 | 191 | Este ejemplo mostró cómo **aplicar SRP y DIP** permite crear un sistema de notificaciones **flexible y extensible**. 192 | Al depender de **abstracciones** (interfaces) y separar las responsabilidades, el código es más **modular** y fácil de mantener. 193 | 194 | > **Tip final:** En proyectos reales, estos principios son esenciales para manejar la **complejidad** y facilitar la **evolución del software**. 195 | 196 | --- 197 | 198 | ⬅️ [**Anterior**](../Readme.md) | [**Siguiente**](../Reto-01/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-08/Ejemplo-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 08**](../Readme.md) ➡️ / 📝 `Ejemplo 02: Pruebas unitarias con JUnit y Mockito` 2 | 3 | --- 4 | 5 | ## 🎯 Objetivo 6 | 7 | ⚒️ Aplicar **pruebas unitarias** en Java usando **JUnit 5** y **Mockito** para simular dependencias externas, integrando **logs con SLF4J** para depurar y monitorear el comportamiento de un **servicio de cálculo de descuentos**. 8 | 9 | --- 10 | 11 | ## ⚙️ Requisitos 12 | 13 | - JDK 17 o superior 14 | - IDE compatible con Java (IntelliJ IDEA, Eclipse, VSCode) 15 | - Maven instalado 16 | 17 | --- 18 | 19 | ## 🔧 Dependencias necesarias (`pom.xml`) 20 | 21 | ```xml 22 | 23 | 24 | 25 | org.junit.jupiter 26 | junit-jupiter-api 27 | 5.12.2 28 | test 29 | 30 | 31 | org.junit.jupiter 32 | junit-jupiter-engine 33 | 5.12.2 34 | test 35 | 36 | 37 | 38 | 39 | org.mockito 40 | mockito-core 41 | 5.17.0 42 | test 43 | 44 | 45 | 46 | 47 | org.slf4j 48 | slf4j-api 49 | 2.0.17 50 | 51 | 52 | org.slf4j 53 | slf4j-simple 54 | 2.0.17 55 | 56 | 57 | ``` 58 | 59 | --- 60 | 61 | ## 🧠 Contexto del ejemplo 62 | 63 | Este ejemplo simula un **servicio de cálculo de descuentos** para un sistema de ventas. El descuento **depende de un servicio externo (`PromocionService`)**, que determina el **porcentaje de descuento** aplicado según el tipo de cliente o promoción. 64 | 65 | Aquí aprenderás a: 66 | 67 | - **Simular dependencias** con **Mockito**. 68 | - Validar la lógica con **JUnit 5**. 69 | - **Registrar logs en contexto** con **SLF4J**. 70 | 71 | --- 72 | 73 | ## 📂 Estructura del proyecto 74 | 75 | ``` 76 | Ejemplo-02/ 77 | ├── src/ 78 | │ ├── main/ 79 | │ │ └── java/ 80 | │ │ ├── service/ 81 | │ │ │ ├── CalculadoraDescuentos.java 82 | │ │ │ └── PromocionService.java 83 | │ │ └── Main.java 84 | │ └── test/ 85 | │ └── java/ 86 | │ └── service/ 87 | │ └── CalculadoraDescuentosTest.java 88 | ``` 89 | 90 | --- 91 | 92 | ## 💻 Código completo 93 | 94 | ### 🧩 Servicio de promociones (interfaz) 95 | 96 | ```java 97 | // src/main/java/service/PromocionService.java 98 | package service; 99 | 100 | public interface PromocionService { 101 | double obtenerPorcentajeDescuento(); 102 | } 103 | ``` 104 | 105 | --- 106 | 107 | ### 🧩 Servicio de cálculo de descuentos 108 | 109 | ```java 110 | // src/main/java/service/CalculadoraDescuentos.java 111 | package service; 112 | 113 | import org.slf4j.Logger; 114 | import org.slf4j.LoggerFactory; 115 | 116 | public class CalculadoraDescuentos { 117 | 118 | private static final Logger logger = LoggerFactory.getLogger(CalculadoraDescuentos.class); 119 | 120 | private final PromocionService promocionService; 121 | 122 | public CalculadoraDescuentos(PromocionService promocionService) { 123 | this.promocionService = promocionService; 124 | } 125 | 126 | public double calcularDescuento(double precio) { 127 | double porcentaje = promocionService.obtenerPorcentajeDescuento(); 128 | double descuento = precio * porcentaje; 129 | 130 | logger.info("🏷️ Aplicando descuento del {}% sobre ${}, descuento calculado: ${}", 131 | porcentaje * 100, 132 | String.format("%,.2f", precio), 133 | String.format("%,.2f", descuento)); 134 | 135 | return descuento; 136 | } 137 | } 138 | ``` 139 | 140 | --- 141 | 142 | ### 🧩 Clase principal (Main.java) 143 | 144 | ```java 145 | // src/main/java/Main.java 146 | import service.CalculadoraDescuentos; 147 | import service.PromocionService; 148 | 149 | public class Main { 150 | public static void main(String[] args) { 151 | // Promoción fija del 10% para esta ejecución manual 152 | PromocionService promocionFija = () -> 0.10; 153 | 154 | CalculadoraDescuentos calculadora = new CalculadoraDescuentos(promocionFija); 155 | double descuento = calculadora.calcularDescuento(500.0); 156 | 157 | System.out.println("Descuento aplicado: $" + String.format("%,.2f", descuento)); 158 | } 159 | } 160 | ``` 161 | 162 | --- 163 | 164 | ### 🧩 Pruebas unitarias con Mockito 165 | 166 | ```java 167 | // src/test/java/service/CalculadoraDescuentosTest.java 168 | package service; 169 | 170 | import org.junit.jupiter.api.Test; 171 | import static org.junit.jupiter.api.Assertions.*; 172 | import static org.mockito.Mockito.*; 173 | 174 | class CalculadoraDescuentosTest { 175 | 176 | @Test 177 | void pruebaDescuentoDel10PorCiento() { 178 | PromocionService promocionMock = mock(PromocionService.class); 179 | when(promocionMock.obtenerPorcentajeDescuento()).thenReturn(0.10); 180 | 181 | CalculadoraDescuentos calculadora = new CalculadoraDescuentos(promocionMock); 182 | double descuento = calculadora.calcularDescuento(500.0); 183 | 184 | assertEquals(50.0, descuento, 0.01); 185 | verify(promocionMock).obtenerPorcentajeDescuento(); 186 | } 187 | 188 | @Test 189 | void pruebaSinDescuento() { 190 | PromocionService promocionMock = mock(PromocionService.class); 191 | when(promocionMock.obtenerPorcentajeDescuento()).thenReturn(0.0); 192 | 193 | CalculadoraDescuentos calculadora = new CalculadoraDescuentos(promocionMock); 194 | double descuento = calculadora.calcularDescuento(500.0); 195 | 196 | assertEquals(0.0, descuento, 0.01); 197 | verify(promocionMock).obtenerPorcentajeDescuento(); 198 | } 199 | 200 | @Test 201 | void pruebaDescuentoVip() { 202 | PromocionService promocionMock = mock(PromocionService.class); 203 | when(promocionMock.obtenerPorcentajeDescuento()).thenReturn(0.20); 204 | 205 | CalculadoraDescuentos calculadora = new CalculadoraDescuentos(promocionMock); 206 | double descuento = calculadora.calcularDescuento(500.0); 207 | 208 | assertEquals(100.0, descuento, 0.01); 209 | verify(promocionMock).obtenerPorcentajeDescuento(); 210 | } 211 | } 212 | ``` 213 | 214 | --- 215 | 216 | 217 | ## 🧪 ¿Cómo ejecutar el ejemplo? 218 | 219 | #### Para correr el `Main`: 220 | 221 | 1. Abre `Main.java`. 222 | 2. Haz clic en el ícono verde ▶️ para ejecutarlo. 223 | 224 | --- 225 | 226 | ### 📝 Salida esperada (`Main`): 227 | 228 | ``` 229 | Descuento aplicado: $50.00 230 | ``` 231 | 232 | **Logs esperados en consola:** 233 | 234 | ``` 235 | INFO service.CalculadoraDescuentos - 🏷️ Aplicando descuento del 10.0% sobre $500.00, descuento calculado: $50.00 236 | ``` 237 | 238 | --- 239 | 240 | #### Para correr las pruebas unitarias: 241 | 242 | Desde **IntelliJ IDEA**: 243 | 244 | 1. Abre `CalculadoraDescuentosTest.java`. 245 | 2. Haz clic en el ícono verde ▶️. 246 | 247 | Desde la terminal: 248 | 249 | ```bash 250 | mvn test 251 | ``` 252 | 253 | 🔔 **Nota:** 254 | Si ves un error como: 255 | 256 | ``` 257 | mvn : El término 'mvn' no se reconoce... 258 | ``` 259 | 260 | Significa que **Maven no está instalado o no está en el PATH**. 261 | 262 | - Puedes instalar Maven desde: 263 | 👉 [https://maven.apache.org/download.cgi](https://maven.apache.org/download.cgi) 264 | 265 | - Asegúrate de agregar Maven al **PATH** del sistema para que el comando `mvn` funcione desde cualquier terminal. 266 | 267 | 268 | --- 269 | 270 | ### 📝 Salida esperada (pruebas unitarias): 271 | 272 | ``` 273 | [INFO] ------------------------------------------------------- 274 | [INFO] T E S T S 275 | [INFO] ------------------------------------------------------- 276 | [INFO] Running service.CalculadoraDescuentosTest 277 | [INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 278 | [INFO] ------------------------------------------------------- 279 | [INFO] BUILD SUCCESS 280 | ``` 281 | 282 | --- 283 | 284 | ## 🚀 Resumen 285 | 286 | Este ejemplo: 287 | 288 | - **Integró dependencias simuladas** usando **Mockito**. 289 | - **Validó la lógica de negocio** con **JUnit 5**. 290 | - **Registró logs en contexto** con **SLF4J**. 291 | 292 | > **Tip final:** Simular dependencias con **Mockito** permite **probar en aislamiento** y **controlar escenarios complejos** sin depender de componentes externos. 293 | 294 | --- 295 | 296 | ⬅️ [**Anterior**](../Ejemplo-01/Readme.md) | [**Siguiente**](../Reto-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-08/Ejemplo-03/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 08**](../Readme.md) ➡️ / 📝 `Ejemplo 03: Organización de proyecto y control de versiones` 2 | 3 | --- 4 | 5 | ## 🎯 Objetivo 6 | 7 | ⚒️ Aplicar buenas prácticas de **organización del código en Java** usando **estructura por capas** y configurar un **repositorio Git** con una **documentación mínima**, facilitando el trabajo en equipo y la mantenibilidad del proyecto. 8 | 9 | --- 10 | 11 | ## ⚙️ Requisitos 12 | 13 | - JDK 17 o superior 14 | - IDE compatible con Java (IntelliJ IDEA, Eclipse, VSCode) 15 | - Git instalado 16 | - GitHub (opcional para repositorio remoto) 17 | 18 | --- 19 | 20 | ## 🧠 Contexto del ejemplo 21 | 22 | En proyectos reales, mantener el código **ordenado y bien documentado** es esencial para: 23 | 24 | - Evitar duplicidad y caos. 25 | - Facilitar la colaboración entre desarrolladores. 26 | - Asegurar que cualquier persona pueda entender y contribuir al proyecto. 27 | 28 | Este ejemplo muestra cómo **organizar un proyecto Java por capas** (controladores, servicios y modelos), implementar un **control de versiones básico con Git**, y mantener una **documentación mínima pero efectiva**. 29 | 30 | --- 31 | 32 | ## 📂 Estructura del proyecto 33 | 34 | ``` 35 | Ejemplo-03/ 36 | ├── controller/ 📂 Controladores (gestión de interacción) 37 | │ └── ProductoController.java 38 | ├── service/ 🔧 Servicios (lógica de negocio) 39 | │ └── ProductoService.java 40 | ├── model/ 📦 Modelos (entidades de datos) 41 | │ └── Producto.java 42 | ├── Main.java ▶️ Punto de entrada del programa 43 | ├── .gitignore 🚫 Configuración para excluir archivos no deseados 44 | └── Readme.md 📝 Documentación mínima del proyecto 45 | ``` 46 | 47 | --- 48 | 49 | ## 💻 Código completo 50 | 51 | ### 🧩 Modelo: Producto 52 | 53 | ```java 54 | // model/Producto.java 55 | package model; 56 | 57 | public class Producto { 58 | private String nombre; 59 | private double precio; 60 | 61 | public Producto(String nombre, double precio) { 62 | this.nombre = nombre; 63 | this.precio = precio; 64 | } 65 | 66 | public String getNombre() { return nombre; } 67 | public double getPrecio() { return precio; } 68 | } 69 | ``` 70 | 71 | --- 72 | 73 | ### 🧩 Servicio: ProductoService 74 | 75 | ```java 76 | // service/ProductoService.java 77 | package service; 78 | 79 | import model.Producto; 80 | import java.util.List; 81 | 82 | public class ProductoService { 83 | 84 | public List obtenerProductos() { 85 | return List.of( 86 | new Producto("Laptop", 1500.0), 87 | new Producto("Mouse", 25.0) 88 | ); 89 | } 90 | } 91 | ``` 92 | 93 | --- 94 | 95 | ### 🧩 Controlador: ProductoController 96 | 97 | ```java 98 | // controller/ProductoController.java 99 | package controller; 100 | 101 | import model.Producto; 102 | import service.ProductoService; 103 | import java.util.List; 104 | 105 | public class ProductoController { 106 | 107 | private final ProductoService productoService = new ProductoService(); 108 | 109 | public void listarProductos() { 110 | List productos = productoService.obtenerProductos(); 111 | productos.forEach(producto -> 112 | System.out.println("📦 Producto: " + producto.getNombre() + " | Precio: $" + String.format("%,.2f", producto.getPrecio())) 113 | ); 114 | } 115 | } 116 | ``` 117 | 118 | --- 119 | 120 | ### 🧩 Clase principal (Main.java) 121 | 122 | ```java 123 | // Main.java 124 | import controller.ProductoController; 125 | 126 | public class Main { 127 | public static void main(String[] args) { 128 | ProductoController controller = new ProductoController(); 129 | controller.listarProductos(); 130 | } 131 | } 132 | ``` 133 | 134 | --- 135 | 136 | ## 🛠️ Instrucciones para compilar y ejecutar 137 | 138 | 1. **Compilar el proyecto:** 139 | 140 | ```bash 141 | javac model/*.java service/*.java controller/*.java Main.java 142 | ``` 143 | 144 | 2. **Ejecutar la clase principal:** 145 | 146 | ```bash 147 | java Main 148 | ``` 149 | 150 | ### 📝 Salida esperada: 151 | 152 | ``` 153 | 📦 Producto: Laptop | Precio: $1,500.00 154 | 📦 Producto: Mouse | Precio: $25.00 155 | ``` 156 | 157 | --- 158 | 159 | ## 📝 Documentación mínima recomendada 160 | 161 | Incluye estos elementos en el **README.md** del proyecto: 162 | 163 | - 🎯 **Nombre del proyecto:** Proyecto de Organización por Capas. 164 | - 📝 **Descripción:** Implementa una estructura por capas en Java para facilitar la organización del código. 165 | - 🛠️ **Instrucciones para compilar y ejecutar** (como las mostradas anteriormente). 166 | - 🗂️ **Estructura del proyecto** (como se mostró al inicio). 167 | 168 | --- 169 | 170 | ## 🚫 `.gitignore` básico sugerido 171 | 172 | ``` 173 | *.class 🚫 Archivos compilados 174 | /target/ 🚫 Carpeta de compilación (si existiera) 175 | /.idea/ 🚫 Configuración de IntelliJ IDEA 176 | *.iml 🚫 Archivos de proyecto de IntelliJ 177 | ``` 178 | 179 | --- 180 | 181 | ## 🛠️ Configuración básica de Git 182 | 183 | ### 🔃 Inicializa el repositorio local: 184 | 185 | ```bash 186 | git init 187 | ``` 188 | 189 | ### 💾 Realiza tu primer commit: 190 | 191 | ```bash 192 | git add . 193 | git commit -m "🚀 Inicializa proyecto con estructura por capas" 194 | ``` 195 | 196 | ### 🌐 Conecta tu repositorio a **GitHub** (opcional): 197 | 198 | ```bash 199 | git remote add origin https://github.com/usuario/organizacion-proyecto.git 200 | git push -u origin main 201 | ``` 202 | 203 | ### 🔥 Tip final: 204 | 205 | - Usa **commits pequeños y descriptivos**: 206 | 207 | - ❌ `Cambios varios` 208 | - ✅ `🎨 Refactoriza ProductoService para mejorar legibilidad` 209 | 210 | - Sincroniza frecuentemente con el repositorio remoto (`git pull`, `git push`). 🔄 211 | 212 | --- 213 | 214 | ## 📝 Conceptos clave utilizados 215 | 216 | - **Estructura por capas** (controlador, servicio, modelo). 217 | - **Control de versiones con Git**. 218 | - **Documentación mínima del proyecto**. 219 | 220 | --- 221 | 222 | ## 🚀 Resumen 223 | 224 | Este ejemplo mostró cómo **organizar un proyecto Java** por capas y configurar un **repositorio Git básico**, asegurando un proyecto limpio, mantenible y colaborativo. 225 | 226 | > **Tip final:** Mantén siempre una estructura coherente y un flujo de trabajo Git disciplinado para proyectos de cualquier tamaño. 227 | 228 | --- 229 | 230 | ⬅️ [**Anterior**](../Reto-02/Readme.md) | [**Siguiente**](../../Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-08/Imagenes/S08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-08/Imagenes/S08.jpg -------------------------------------------------------------------------------- /Sesion-08/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../Readme.md) ➡️ / 📖 `Sesión 08` 2 | 3 |
4 | Sesion_08 5 |
6 | 7 | # 🎯 Objetivo 8 | 9 | ⚒️ Adoptar **buenas prácticas de diseño en Java**, reforzar conceptos de **pruebas unitarias**, **depuración** y conocer las **herramientas esenciales** para mantener proyectos limpios y colaborativos (control de versiones, documentación, logs). 10 | 11 | --- 12 | 13 | 📘 Material del prework: 14 | Revisamos conceptos clave sobre **principios SOLID**, **refactorización básica**, y el uso de **Git** y **pruebas unitarias**. Hoy vamos a practicar con ejemplos concretos. 15 | 16 | 🔥 ¡Vamos a comenzar! 🔥 17 | 18 | --- 19 | 20 | ## 📂 Temas de la sesión... 21 | 22 | ### 📖 Principios de diseño en Java 23 | 24 | 🔹 Convenciones de código 25 | 🔹 Refactorización básica 26 | 🔹 Introducción a patrones de diseño (GoF) 27 | 28 | 📜 **[Ejemplo 01: Aplicación de principios SOLID](Ejemplo-01/Readme.md)** 29 | 🔥 **[Reto 01: Refactorizar un procesador de pagos aplicando SOLID](Reto-01/Readme.md)** 30 | 31 | --- 32 | 33 | ### 📖 Pruebas unitarias y depuración 34 | 35 | 🔹 JUnit 5 y aserciones básicas 36 | 🔹 Mocks y pruebas con Mockito 37 | 🔹 Logs con SLF4J 38 | 39 | 📜 **[Ejemplo 02: Pruebas unitarias con JUnit](Ejemplo-02/Readme.md)** 40 | 🔥 **[Reto 02: Simulación de pruebas unitarias en un microservicio](Reto-02/Readme.md)** 41 | 42 | --- 43 | 44 | ### 📖 Organización del código y control de versiones 45 | 46 | 🔹 Estructura por capas y paquetes 47 | 🔹 Buen uso de Git y GitHub 48 | 🔹 Comentarios útiles y documentación mínima 49 | 50 | 📜 **[Ejemplo 03: Organización de proyecto y control de versiones](Ejemplo-03/Readme.md)** 51 | 52 | --- 53 | 54 | ⬅️ [**Anterior**](../Sesion-07/Readme.md) | [**Siguiente**](../Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-08/Reto-01/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 08**](../Readme.md) ➡️ / ⚡ `Reto 01: Refactorizar un procesador de pagos aplicando SOLID` 2 | 3 | --- 4 | 5 | ## 🎯 Objetivo 6 | 7 | ⚒️ **Identificar malas prácticas** en un procesador de pagos y **refactorizarlo aplicando principios SOLID** (especialmente **SRP**, **OCP** y **DIP**), mejorando la **mantenibilidad** y **extensibilidad** del código. 8 | 9 | --- 10 | 11 | ## 🧠 Contexto del reto 12 | 13 | Tienes un sistema de **procesamiento de pagos** que permite realizar transacciones usando **tarjeta**, **PayPal** o **criptomonedas**. 14 | Sin embargo, **toda la lógica está mezclada** dentro de una sola clase, lo que **viola varios principios SOLID**. 15 | 16 | Este diseño **dificulta agregar nuevos métodos de pago** (por ejemplo, Apple Pay o transferencias), ya que tendrías que **modificar la clase existente**, afectando otras partes del sistema. 17 | 18 | --- 19 | 20 | ## 📋 Instrucciones 21 | 22 | 1. Analiza el siguiente código mal diseñado (**violando SRP, OCP y DIP**). 23 | 24 | 2. Refactorízalo aplicando **principios SOLID**: 25 | 26 | - **SRP**: Cada clase debe tener una **única responsabilidad**. 27 | - **OCP**: El código debe estar **abierto a extensión**, pero **cerrado a modificación**. 28 | - **DIP**: El servicio debe depender de **abstracciones** (interfaces), no de implementaciones concretas. 29 | 30 | 3. Crea al menos **tres métodos de pago** (**tarjeta**, **PayPal** y **cripto**), y asegúrate de que **puedan agregarse más en el futuro sin modificar las clases existentes**. 31 | 32 | 4. Implementa una **clase principal (`Main`)** para probar tu solución. 33 | 34 | --- 35 | 36 | ## 💻 Código inicial (mal diseño) 37 | 38 | ```java 39 | public class PaymentProcessor { 40 | 41 | public void processPayment(String method, double amount) { 42 | if ("card".equals(method)) { 43 | System.out.println("Procesando pago con tarjeta por $" + amount); 44 | // Lógica específica de tarjeta 45 | } else if ("paypal".equals(method)) { 46 | System.out.println("Procesando pago con PayPal por $" + amount); 47 | // Lógica específica de PayPal 48 | } else if ("crypto".equals(method)) { 49 | System.out.println("Procesando pago con criptomonedas por $" + amount); 50 | // Lógica específica de cripto 51 | } else { 52 | System.out.println("Método de pago no soportado"); 53 | } 54 | } 55 | } 56 | ``` 57 | 58 | --- 59 | 60 | ## 🧪 Prueba esperada 61 | 62 | Desde tu `Main`: 63 | 64 | ```java 65 | public class Main { 66 | public static void main(String[] args) { 67 | // Ejemplo para tarjeta 68 | // Ejemplo para PayPal 69 | // Ejemplo para cripto 70 | } 71 | } 72 | ``` 73 | 74 | ### Salida esperada: 75 | 76 | ```plaintext 77 | 💳 Procesando pago con tarjeta por $1,500.00 78 | 💻 Procesando pago con PayPal por $8,500.00 79 | 🪙 Procesando pago con criptomonedas por $10,557.00 80 | 🏦 Procesando pago con transferencia bancaria por $150,897.27 81 | ``` 82 | 83 | --- 84 | 85 | ## 🚀 Desafío adicional 86 | 87 | - Implementa una **nueva opción de pago** (por ejemplo, **Apple Pay** o **transferencia bancaria**) **sin modificar las clases existentes** (solo creando nuevas clases). 88 | 89 | --- 90 | 91 | ⬅️ [**Anterior**](../Ejemplo-01/Readme.md) | [**Siguiente**](../Ejemplo-02/Readme.md)➡️ -------------------------------------------------------------------------------- /Sesion-08/Reto-02/Readme.md: -------------------------------------------------------------------------------- 1 | 🏠 [**Inicio**](../../Readme.md) ➡️ / 📖 [**Sesión 08**](../Readme.md) ➡️ / ⚡ `Reto 02: Sistema de reservas en restaurante` 2 | 3 | --- 4 | 5 | ## 🎯 Objetivo 6 | 7 | ⚒️ Desarrollar un **microservicio simple de reservas para un restaurante**, integrando **pruebas unitarias con JUnit 5**, **mocks con Mockito** y **logs con SLF4J** para registrar el comportamiento del sistema. 8 | 9 | --- 10 | 11 | ## 🧠 Contexto del reto 12 | 13 | Imagina que eres parte del equipo de desarrollo de un **restaurante** que quiere automatizar las **reservas de mesas**. 14 | 15 | - Cuando un cliente solicita una **reserva** para una fecha y número de personas, el sistema debe: 16 | - Consultar un **servicio externo de disponibilidad de mesas**. 17 | - Confirmar o rechazar la reserva según la **disponibilidad**. 18 | 19 | El **equipo de operaciones** necesita que el sistema registre **logs detallados** sobre cada intento de reserva para dar seguimiento. 20 | 21 | --- 22 | 23 | ## 🛠️ Instrucciones 24 | 25 | 1. **Crea las siguientes clases:** 26 | 27 | - `ReservaService`: 28 | - Método `realizarReserva(String fecha, int personas)` → devuelve `true` si la reserva fue confirmada, `false` si fue rechazada. 29 | - Este método debe: 30 | - Consultar a un `DisponibilidadService` (una interfaz que simula un **servicio externo**). 31 | - Si hay disponibilidad, confirma la reserva; si no, la rechaza. 32 | - **Registrar logs** con SLF4J indicando el resultado (confirmada o rechazada). 33 | 34 | - `DisponibilidadService` (interfaz): 35 | - Método `boolean hayDisponibilidad(String fecha, int personas)`. 36 | 37 | --- 38 | 39 | 2. **Crea las pruebas unitarias con JUnit 5 y Mockito**: 40 | 41 | - Simula diferentes escenarios usando **Mockito**: 42 | - ✅ Cuando **hay disponibilidad**, la reserva debe ser confirmada. 43 | - ❌ Cuando **no hay disponibilidad**, la reserva debe ser rechazada. 44 | 45 | - Verifica: 46 | - Que **se llama correctamente** al método del `DisponibilidadService`. 47 | - Que el **resultado de la reserva** es el esperado. 48 | 49 | --- 50 | 51 | 3. **Agrega logs en contexto** con **SLF4J**: 52 | 53 | - Log de **nivel info** que indique: 54 | 55 | - Fecha, número de personas, y si la reserva fue **confirmada** o **rechazada**. 56 | 57 | Ejemplo: 58 | 59 | ``` 60 | INFO Reserva confirmada para 2 personas el 2025-05-01 61 | INFO Reserva rechazada para 5 personas el 2025-05-02 (sin disponibilidad) 62 | ``` 63 | 64 | --- 65 | 66 | ## 📂 Código base sugerido 67 | 68 | ⚠️ Solo te comparto la estructura y firmas, **debes implementar la lógica**: 69 | 70 | ```java 71 | // service/DisponibilidadService.java 72 | public interface DisponibilidadService { 73 | boolean hayDisponibilidad(String fecha, int personas); 74 | } 75 | ``` 76 | 77 | ```java 78 | // service/ReservaService.java 79 | public class ReservaService { 80 | private final DisponibilidadService disponibilidadService; 81 | 82 | public ReservaService(DisponibilidadService disponibilidadService) { 83 | this.disponibilidadService = disponibilidadService; 84 | } 85 | 86 | public boolean realizarReserva(String fecha, int personas) { 87 | // Implementar lógica aquí (consultar disponibilidad y registrar logs) 88 | return false; 89 | } 90 | } 91 | ``` 92 | 93 | --- 94 | 95 | ## 📝 Recomendaciones 96 | 97 | - Integra **SLF4J + slf4j-simple** para los logs. 98 | - Usa **JUnit 5** y **Mockito** para las pruebas unitarias y mocks. 99 | 100 | --- 101 | 102 | ## 🚀 Desafío adicional (opcional) 103 | 104 | 💡 **Extiende la lógica** para permitir: 105 | 106 | - Reservar solo si el número de personas está entre **1 y 10**. 107 | - Registrar un **log de advertencia (WARN)** si alguien intenta reservar para más de 10 personas. 108 | 109 | --- 110 | 111 | ## 🧪 ¿Cómo validar? 112 | 113 | - **Corre el `Main` (si lo creas)** y observa los logs. 114 | - Ejecuta las pruebas unitarias (`mvn test` o desde IntelliJ IDEA). 115 | 116 | --- 117 | 118 | ⬅️ [**Anterior**](../Ejemplo-02/Readme.md) | [**Siguiente**](../Ejemplo-03/Readme.md)➡️ 119 | -------------------------------------------------------------------------------- /Sesion-09/Imagenes/S09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-09/Imagenes/S09.jpg -------------------------------------------------------------------------------- /Sesion-10/Imagenes/S00.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-10/Imagenes/S00.jpg -------------------------------------------------------------------------------- /Sesion-10/Imagenes/S10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beduExpert/Java-Standar-Edition-2-2025/7e06dc38a6d05c489a402a46f40305b64715e6a2/Sesion-10/Imagenes/S10.jpg --------------------------------------------------------------------------------