Skip to content

Instantly share code, notes, and snippets.

@frobs
Created May 31, 2025 16:10
Show Gist options
  • Save frobs/d6ffc22d8092a3693e195af7ad37dd29 to your computer and use it in GitHub Desktop.
Save frobs/d6ffc22d8092a3693e195af7ad37dd29 to your computer and use it in GitHub Desktop.
Refactorizacion de código funcional en java
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