Created
May 31, 2025 16:10
-
-
Save frobs/d6ffc22d8092a3693e195af7ad37dd29 to your computer and use it in GitHub Desktop.
Refactorizacion de código funcional en java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.time.Duration; | |
import java.util.ArrayList; | |
import java.util.Comparator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Optional; | |
import java.util.OptionalDouble; | |
import java.util.stream.Collector; | |
import java.util.stream.Collectors; | |
import java.util.stream.Stream; | |
public class Funcional { | |
public static void main(String[] args) { | |
Funcional funcional = new Funcional(); | |
System.out.println("=== TESTS PARA LA FUNCIÓN dolorFuncionalEnLosOjos ===\n"); | |
// Ejecutar todos los tests | |
testCasoNormal(funcional); | |
testListaVacia(funcional); | |
testListaNull(funcional); | |
testUnSoloTipo(funcional); | |
testUnSoloDestinoPorTipo(funcional); | |
testViajesConTipoNull(funcional); | |
testViajesConDestinoNull(funcional); | |
testViajesConDuracionNull(funcional); | |
testViajesConValoresMixtos(funcional); | |
} | |
// Test 1: Caso normal con múltiples tipos y destinos | |
private static void testCasoNormal(Funcional funcional) { | |
System.out.println("TEST 1: Caso normal con múltiples tipos y destinos"); | |
List<Viaje> viajesNormal = new ArrayList<>(); | |
// Viajes de tipo "Negocio" | |
viajesNormal.add(new Viaje("Negocio", "Madrid", Duration.ofHours(2))); | |
viajesNormal.add(new Viaje("Negocio", "Madrid", Duration.ofHours(3))); // Este no se considerará (solo el | |
// mínimo) | |
viajesNormal.add(new Viaje("Negocio", "Barcelona", Duration.ofMinutes(90))); | |
viajesNormal.add(new Viaje("Negocio", "Valencia", Duration.ofHours(3))); | |
// Viajes de tipo "Vacaciones" | |
viajesNormal.add(new Viaje("Vacaciones", "Paris", Duration.ofHours(4))); | |
viajesNormal.add(new Viaje("Vacaciones", "Paris", Duration.ofHours(5))); // Este no se considerará (solo el | |
// mínimo) | |
viajesNormal.add(new Viaje("Vacaciones", "Roma", Duration.ofHours(2))); | |
viajesNormal.add(new Viaje("Vacaciones", "Londres", Duration.ofHours(3))); | |
OptionalDouble resultadoNormal = funcional.dolorFuncionalEnLosOjos(viajesNormal); | |
mostrarResultado(resultadoNormal, "Caso normal"); | |
System.out.println("Resultado esperado: 155.0 minutos\n"); | |
} | |
// Test 2: Lista vacía | |
private static void testListaVacia(Funcional funcional) { | |
System.out.println("TEST 2: Lista vacía"); | |
List<Viaje> viajesVacios = new ArrayList<>(); | |
OptionalDouble resultadoVacio = funcional.dolorFuncionalEnLosOjos(viajesVacios); | |
mostrarResultado(resultadoVacio, "Lista vacía"); | |
System.out.println("Resultado esperado: OptionalDouble.empty\n"); | |
} | |
// Test 3: Lista null | |
private static void testListaNull(Funcional funcional) { | |
System.out.println("TEST 3: Lista null"); | |
try { | |
OptionalDouble resultadoNull = funcional.dolorFuncionalEnLosOjos(null); | |
mostrarResultado(resultadoNull, "Lista null"); | |
} catch (Exception e) { | |
System.out.println("Excepción capturada: " + e.getClass().getSimpleName() + " - " + e.getMessage()); | |
System.out.println("Comportamiento esperado: NullPointerException\n"); | |
} | |
} | |
// Test 4: Un solo tipo de viaje | |
private static void testUnSoloTipo(Funcional funcional) { | |
System.out.println("TEST 4: Un solo tipo de viaje"); | |
List<Viaje> viajesUnTipo = new ArrayList<>(); | |
viajesUnTipo.add(new Viaje("Negocio", "Madrid", Duration.ofHours(2))); | |
viajesUnTipo.add(new Viaje("Negocio", "Barcelona", Duration.ofMinutes(90))); | |
viajesUnTipo.add(new Viaje("Negocio", "Valencia", Duration.ofHours(3))); | |
OptionalDouble resultadoUnTipo = funcional.dolorFuncionalEnLosOjos(viajesUnTipo); | |
mostrarResultado(resultadoUnTipo, "Un solo tipo"); | |
System.out.println("Resultado esperado: 130.0 minutos\n"); | |
} | |
// Test 5: Un solo destino por tipo | |
private static void testUnSoloDestinoPorTipo(Funcional funcional) { | |
System.out.println("TEST 5: Un solo destino por tipo"); | |
List<Viaje> viajesUnDestino = new ArrayList<>(); | |
viajesUnDestino.add(new Viaje("Negocio", "Madrid", Duration.ofHours(2))); | |
viajesUnDestino.add(new Viaje("Vacaciones", "Paris", Duration.ofHours(4))); | |
OptionalDouble resultadoUnDestino = funcional.dolorFuncionalEnLosOjos(viajesUnDestino); | |
mostrarResultado(resultadoUnDestino, "Un destino por tipo"); | |
System.out.println("Resultado esperado: 180.0 minutos\n"); | |
} | |
// Test 6: Viajes con tipo null | |
private static void testViajesConTipoNull(Funcional funcional) { | |
System.out.println("TEST 6: Viajes con tipo null"); | |
List<Viaje> viajesTipoNull = new ArrayList<>(); | |
viajesTipoNull.add(new Viaje(null, "Madrid", Duration.ofHours(2))); | |
viajesTipoNull.add(new Viaje("Negocio", "Barcelona", Duration.ofMinutes(90))); | |
try { | |
OptionalDouble resultadoTipoNull = funcional.dolorFuncionalEnLosOjos(viajesTipoNull); | |
mostrarResultado(resultadoTipoNull, "Tipo null"); | |
} catch (Exception e) { | |
System.out.println("Excepción capturada: " + e.getClass().getSimpleName() + " - " + e.getMessage()); | |
System.out.println("Comportamiento actual: Excepción al agrupar por tipo null\n"); | |
} | |
} | |
// Test 7: Viajes con destino null | |
private static void testViajesConDestinoNull(Funcional funcional) { | |
System.out.println("TEST 7: Viajes con destino null"); | |
List<Viaje> viajesDestinoNull = new ArrayList<>(); | |
viajesDestinoNull.add(new Viaje("Negocio", null, Duration.ofHours(2))); | |
viajesDestinoNull.add(new Viaje("Negocio", "Barcelona", Duration.ofMinutes(90))); | |
try { | |
OptionalDouble resultadoDestinoNull = funcional.dolorFuncionalEnLosOjos(viajesDestinoNull); | |
mostrarResultado(resultadoDestinoNull, "Destino null"); | |
} catch (Exception e) { | |
System.out.println("Excepción capturada: " + e.getClass().getSimpleName() + " - " + e.getMessage()); | |
System.out.println("Comportamiento actual: Excepción al agrupar por destino null\n"); | |
} | |
} | |
// Test 8: Viajes con duración null | |
private static void testViajesConDuracionNull(Funcional funcional) { | |
System.out.println("TEST 8: Viajes con duración null"); | |
List<Viaje> viajesDuracionNull = new ArrayList<>(); | |
viajesDuracionNull.add(new Viaje("Negocio", "Madrid", null)); | |
viajesDuracionNull.add(new Viaje("Negocio", "Barcelona", Duration.ofMinutes(90))); | |
try { | |
OptionalDouble resultadoDuracionNull = funcional.dolorFuncionalEnLosOjos(viajesDuracionNull); | |
mostrarResultado(resultadoDuracionNull, "Duración null"); | |
} catch (Exception e) { | |
System.out.println("Excepción capturada: " + e.getClass().getSimpleName() + " - " + e.getMessage()); | |
System.out.println("Comportamiento actual: Excepción al comparar duraciones null\n"); | |
} | |
} | |
// Test 9: Viajes con valores mixtos (algunos null, algunos válidos) | |
private static void testViajesConValoresMixtos(Funcional funcional) { | |
System.out.println("TEST 9: Viajes con valores mixtos"); | |
List<Viaje> viajesMixtos = new ArrayList<>(); | |
viajesMixtos.add(new Viaje("Negocio", "Madrid", Duration.ofHours(2))); | |
viajesMixtos.add(new Viaje(null, "Paris", Duration.ofHours(3))); | |
viajesMixtos.add(new Viaje("Vacaciones", null, Duration.ofHours(4))); | |
viajesMixtos.add(new Viaje("Trabajo", "Londres", null)); | |
try { | |
OptionalDouble resultadoMixto = funcional.dolorFuncionalEnLosOjos(viajesMixtos); | |
mostrarResultado(resultadoMixto, "Valores mixtos"); | |
} catch (Exception e) { | |
System.out.println("Excepción capturada: " + e.getClass().getSimpleName() + " - " + e.getMessage()); | |
System.out.println("Comportamiento actual: Excepción con valores mixtos\n"); | |
} | |
} | |
// Método auxiliar para mostrar resultados de forma consistente | |
private static void mostrarResultado(OptionalDouble resultado, String casoTest) { | |
if (resultado.isPresent()) { | |
System.out.println("Resultado para " + casoTest + ": " + resultado.getAsDouble() + " minutos"); | |
} else { | |
System.out.println("Resultado para " + casoTest + ": OptionalDouble.empty"); | |
} | |
} | |
/** | |
* Calcula el promedio de duración de los viajes con menor duración por cada destino y tipo. | |
* | |
* @param viajes Lista de viajes a procesar | |
* @return OptionalDouble con el promedio de duración en minutos, o empty si no hay viajes | |
* @throws NullPointerException si la lista de viajes es null | |
*/ | |
public OptionalDouble dolorFuncionalEnLosOjos(List<Viaje> viajes) { | |
if (viajes == null) { | |
throw new NullPointerException("La lista de viajes no puede ser null"); | |
} | |
Map<String, Map<String, Optional<Viaje>>> viajesPorTipoYDestino = agruparViajesPorTipoYDestino(viajes); | |
Stream<Viaje> viajesConMenorDuracion = extraerViajesConMenorDuracion(viajesPorTipoYDestino); | |
return calcularPromedioDuracion(viajesConMenorDuracion); | |
} | |
/** | |
* Agrupa los viajes por tipo y luego por destino, seleccionando el de menor duración para cada combinación. | |
* | |
* @param viajes Lista de viajes a agrupar | |
* @return Mapa donde la clave es el tipo de viaje y el valor es otro mapa con destino como clave | |
* y el viaje de menor duración como valor | |
*/ | |
private Map<String, Map<String, Optional<Viaje>>> agruparViajesPorTipoYDestino(List<Viaje> viajes) { | |
return viajes.stream() | |
.collect(Collectors.groupingBy( | |
Viaje::getTipo, | |
Collectors.groupingBy( | |
Viaje::getDestino, | |
Collectors.minBy( | |
Comparator.comparing(Viaje::getDuracion) | |
) | |
) | |
)); | |
} | |
/** | |
* Extrae los viajes con menor duración de cada destino. | |
*/ | |
private Stream<Viaje> extraerViajesConMenorDuracion(Map<String, Map<String, Optional<Viaje>>> viajesPorTipoYDestino) { | |
return viajesPorTipoYDestino.values().stream() | |
.flatMap(destinoMap -> destinoMap.values().stream()) | |
.flatMap(Optional::stream); | |
} | |
/** | |
* Calcula el promedio de duración en minutos de una lista de viajes. | |
*/ | |
private OptionalDouble calcularPromedioDuracion(Stream<Viaje> viajes) { | |
return viajes | |
.mapToDouble(viaje -> viaje.getDuracion().toMinutes()) | |
.average(); | |
} | |
// Clase Viaje interna | |
static class Viaje { | |
private String tipo; | |
private String destino; | |
private Duration duracion; | |
public Viaje(String tipo, String destino, Duration duracion) { | |
this.tipo = tipo; | |
this.destino = destino; | |
this.duracion = duracion; | |
} | |
public String getTipo() { | |
return tipo; | |
} | |
public String getDestino() { | |
return destino; | |
} | |
public Duration getDuracion() { | |
return duracion; | |
} | |
@Override | |
public String toString() { | |
return "Viaje{tipo='" + tipo + "', destino='" + destino + "', duracion=" + duracion.toMinutes() + " min}"; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment