Programación funcional y reactiva - Computación
Introducción a la programación reactiva
Programación reactiva
Reactiva: que produce una reacción
Acciones sobre elementos de la GUI
Aproximaciones
Programación reactiva
Pero es posible reaccionar a otros elementos
Otras reacciones
Programación reactiva
Manejo de datos
Enfoques
Programación reactiva
Functional reactive programming
Enfoques
Patrón Observer
Patrón Observer
Patrón Observer
😔Problema
Cliente
Tienda
Patrón Observer
😊Solución
Patrón Observer
🏢Estructura
Patrón Observer
🏢Estructura
Patrón Observer en Java
Observable
Lenguajes de programación
Observable
Interfaz
public interface Observable {
void addObserver(Observer o);
void deleteObserver(Observer o);
void notifyObservers();
}
Observer
Interfaz
public interface Observer {
void update();
}
EjemploObservable
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class EjemploObservable implements Observable{
Set<Observer> observersSet = new HashSet<>();
@Override
public void addObserver(Observer o) {
observersSet.add(o);
}
@Override
public void deleteObserver(Observer o) {
observersSet.remove(o);
}
@Override
public void notifyObservers() {
for (Observer observer : observersSet) {
observer.update();
}
}
}
Ejemplo1Observer
public class Ejemplo1Observer implements Observer {
@Override
public void update() {
System.out.println("Se ha llamada al ejemplo 1");
}
}
Main
public class Main {
public static void main(String[] args) {
EjemploObservable observable = new EjemploObservable();
observable.addObserver(new Ejemplo1Observer());
observable.notifyObservers();
}
}
Reactive Streams
Reactive Streams
Definición
Iniciativa para proporcionar un estándar para el procesamiento de flujos de datos de una forma:
Reactive Streams
Flujo de datos
Reactive Streams
Flujo de datos
Reactive Streams
Síncrono
Reactive Streams
Asíncrono
Reactive Streams
Back pressure
Quién consume los datos está en la capacidad de señalar cuántos datos puede recibir.
Reactive Streams
Sin bloqueo
El procesamiento se hace sin bloqueos.
Ejemplos:
Reactive Streams
Lenguaje de programación
https://reactivex.io
ReactiveX
An API for asynchronous programming with observables streams
Reactive Streams
Lenguaje de programación
Algunos programas tienen sus propias implementaciones para manejar reactive streams.
Por ejemplo: Java
Reactive Streams
Lenguaje de programación
En Scala
libraryDependencies += "io.reactivex" %% "rxscala" % "0.27.0"
ReactiveX en Scala
RxScala
Trabajar con RxScala
Instalar la dependencia de RxScala: RxScala está basado en RxJava, por lo que necesitas agregar la dependencia a tu archivo build.sbt
:
libraryDependencies += "io.reactivex" %% "rxscala" % "0.27.0"
Características principales
Ejemplo Básico: Crear un observable
Observable que emite una secuencia de valores
import rx.lang.scala.Observable
object BasicExample extends App {
// Crear un Observable con una lista de valores
val observable = Observable.just(1, 2, 3, 4, 5)
// Suscribirse al Observable
observable.subscribe(
onNext = x => println(s"Received: $x"), // Procesar cada valor emitido
onError = e => println(s"Error: ${e.getMessage}"), // Manejar errores
onCompleted = () => println("Completed!") // Notificar cuando el flujo termine
)
}
Clase principal para manejar flujos de datos reactivos
Crea un Observable que emite una lista de valores
Activa el Observable y le indica qué hacer con los datos que emite
Transformar datos con map
Multiplicar cada valor emitido por 2
import rx.lang.scala.Observable
object MapExample extends App {
// Crear un Observable con valores
val observable = Observable.just(1, 2, 3, 4, 5)
// Aplicar transformación a cada valor
observable
.map(_ * 2) // Multiplica cada valor por 2
.subscribe(x => println(s"Transformed: $x"))
}
Filtrar valores con filter
Mostrar sólo los números mayores a 3.
import rx.lang.scala.Observable
object FilterExample extends App {
// Crear un Observable con valores
val observable = Observable.just(1, 2, 3, 4, 5)
// Filtrar valores mayores a 3
observable
.filter(_ > 3) // Filtrar números mayores a 3
.subscribe(x => println(s"Filtered: $x"))
}
Flujos Asíncronos con temporizadores
Emitir un valor cada segundo
import rx.lang.scala.Observable
// Proporciona utilidades para trabajar con intervalos de tiempo
import scala.concurrent.duration._
object IntervalExample extends App {
// Crear un Observable que emite valores cada segundo
val observable = Observable.interval(1.second).take(5) // Tomar 5 valores
// Suscribirse al flujo
observable.subscribe(
x => println(s"Tick: $x"), // Procesar cada emisión
)
// Mantener la aplicación en ejecución para observar los valores
Thread.sleep(6000)
}
Crea un Observable que emite valores (0, 1, 2, ...) con un intervalo de 1 segundo entre cada emisión
Combinar flujos con merge
Combinar dos flujos diferentes
import rx.lang.scala.Observable
object MergeExample extends App {
// Crear dos Observables
val observable1 = Observable.just("A", "B", "C")
val observable2 = Observable.just("1", "2", "3")
// Combinar los dos flujos
Observable.merge(observable1, observable2)
.subscribe(x => println(s"Received: $x"))
}
Manejo de errores
Continuar el flujo incluso si ocurre un error.
import rx.lang.scala.Observable
object ErrorHandlingExample extends App {
// Crear un Observable que puede generar un error
val observable = Observable.just(10, 5, 0, 4)
// Manejar errores en el flujo
observable
.map(x => 10 / x) // Esto generará un error al dividir por 0
.onErrorResumeNext(_ => Observable.just(-1)) // Continuar con un valor predeterminado
.subscribe(
x => println(s"Received: $x"),
e => println(s"Error: ${e.getMessage}"),
() => println("Stream completed")
)
}
Simulación de un sensor
Emitir valores aleatorios como si fueran datos de un sensor.
import rx.lang.scala.Observable
import scala.util.Random
import scala.concurrent.duration._
object SensorExample extends App {
// Crear un flujo de datos del sensor
val sensorStream = Observable.interval(1.second).map(_ => Random.between(20.0, 30.0))
// Procesar el flujo del sensor
sensorStream
.take(5) // Emitir solo 5 valores
.subscribe(
x => println(f"Sensor reading: $x%.2f°C"),
e => println(s"Error: ${e.getMessage}"),
() => println("Sensor stopped")
)
// Mantener la aplicación corriendo
Thread.sleep(6000)
}
Combinar datos de sensores con zip
Combinar lecturas de dos sensores en un flujo único
import rx.lang.scala.Observable
import scala.util.Random
import scala.concurrent.duration._
object ZipExample extends App {
// Crear flujos de dos sensores
val sensor1 = Observable.interval(1.second).map(_ => Random.between(20, 30))
val sensor2 = Observable.interval(1.second).map(_ => Random.between(30, 40))
// Combinar los flujos con zip
sensor1.zip(sensor2)
.take(5)
.subscribe(
onNext = { case (temp1, temp2) => println(s"Sensor1: $temp1°C, Sensor2: $temp2°C") },
onError = e => println(s"Error: ${e.getMessage}"),
onCompleted = () => println("Stream completed!")
)
// Mantener la aplicación corriendo
Thread.sleep(6000)
}
Combinar datos de sensores con zip
Combinar lecturas de dos sensores en un flujo único
import rx.lang.scala.Observable
import scala.util.Random
import scala.concurrent.duration._
object ZipExample extends App{
// Crear flujos de dos sensores
val sensor1 = Observable.interval(1.second).map(_ => Random.between(20, 30))
val sensor2 = Observable.interval(1.second).map(_ => Random.between(30, 40))
// Combinar los flujos con zip
sensor1.zipWith(sensor2) { (temp1, temp2) =>
println(s"Sensor1: $temp1°C, Sensor2: $temp2°C")
}.take(5).subscribe()
// Mantener la aplicación corriendo
Thread.sleep(6000)
}
Sistema de alertas
Generar una alerta si un valor supera un umbral.
import rx.lang.scala.Observable
import scala.util.Random
import scala.concurrent.duration._
object AlertExample extends App{
val observable = Observable.interval(1.second).map(_ => Random.between(20.0, 40.0)) // Datos aleatorios
observable
.take(10) // Emitir 10 valores
.subscribe(
temp => {
println(f"Temperature: $temp%.2f°C")
if (temp > 35) println(f"🚨 ALERT! High temperature detected: $temp%.2f°C")
},
e => println(s"Error: ${e.getMessage}"),
() => println("Monitoring stopped")
)
Thread.sleep(12000)
}