├── 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 |

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 extends Componente> 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 extends Cuenta> 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 super CuentaCorriente> 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 extends T>` | Permite **leer** objetos de tipo `T` o subtipos (para mostrar cuentas). |
149 | | `List super T>` | 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 |

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 extends OrdenProduccion> lista)`
44 | (Debe **leer** cualquier tipo de orden y mostrar sus datos).
45 |
46 | 4. Implementa otro método:
47 |
48 | - `procesarPersonalizadas(List super OrdenPersonalizada> 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 extends MaterialCurso> lista)`
42 | (Muestra el detalle de todos los materiales).
43 |
44 | - `contarDuracionVideos(List extends Video> lista)`
45 | (Suma y muestra la duración total de los videos).
46 |
47 | - `marcarEjerciciosRevisados(List super Ejercicio> 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 |

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 |

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 |

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 |

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 |

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