Pregunta

Tengo un rasgo sellado que se implementa mediante 3 objetos.

sealed trait MyTrait {
...
}
object A extends MyTrait { ... }
object B extends MyTrait { ... }
object C extends MyTrait { ... }

Estoy usando el mecanismo de validación de Scalaz en el que los métodos de aplicación de los objetos A, B y C devuelven un tipo validado.los Objetos A, B y C contienen algo de lógica y quiero aplicar esta lógica secuencialmente, es decir, primero quiero aplicar A y verificar cuál es el resultado de A y, en base a ello, decidir si quiero llamar a B o simplemente devolver el resultado validado.Quiero repetir esto hasta que presione C, después de lo cual simplemente devuelvo todo lo que obtenga como resultado de llamar a C.

Actualmente tengo un enfoque estático en el que primero llamo a A, paso el resultado de A a un método de utilidad, verifico el resultado y luego llamo a B.

  def apply(request: Request): Validated[Result] = {
    val vResultA = run(request, A)
    val vResultB = if (isResultOk(vResultA)) run(request, B) else vResultA
    if (isResultOk(vResultB)) run(request, C) else vResultB
  }

¿Hay una mejor manera de hacer esto?¿Alguna sugerencia o patrón que pueda aplicar?

¿Fue útil?

Solución

definiremos resultados exitosos = resultados que están bien, y resultados fallidos = resultados que no están bien.

Primero, A, B, y C ¿Todos los objetos se extienden? MyTrait.Por lo tanto, se pueden agrupar en un Array o una Lista de MyTrait.

val objects = Array(A, B, C) /* You can use List instead if you want. */

Entonces el tipo de objects es Array[MyTrait].

A continuación, tenemos que iterar sobre esta matriz.
Sin embargo, simplemente llamando map en este Array continúa mapeándose incluso si el anterior isResultOk() es false.
Por lo tanto, usaremos Stream en lugar de Array.

Veamos cómo usar Stream puede dejar de llamar map si se cumple alguna condición.

Array(1, 2, 3, 4, 5).map(i => {
    println(i)
    i + 100
  }).takeWhile(_ <= 103).foreach(println(_))

La salida del código anterior será:

1
2
3
4
5
101
102
103

Entonces, map() termina, y luego takeWhile() termina - takeWhile() no afecta las llamadas map().
Sin embargo, si hacemos las mismas operaciones en el Stream,

Array(1, 2, 3, 4, 5).toStream.map(i => {
    println(i)
    i + 100
  }).takeWhile(_ <= 103).foreach(println(_))

La salida será:

1
101
2
102
3
103
4

Entonces el llamado será map() -> takeWhile() -> foreach() -> map() -> takeWhile() -> ...
Al final, se imprime 4 y se cortará 4 + 100 = 104 > 103 takeWhile().
No se podrá acceder más a los siguientes elementos.

Entonces, ¿tenemos que usar takeWhile?

objects.toStream.map(run(request, _)).takeWhile(isResultOk(_))

Esto eliminará los resultados fallidos, aunque necesitamos el primer resultado fallido si se produjo una falla.
(es decir.Esto creará un problema si hay algún resultado que no sea correcto).

¿Qué tal la función opuesta? dropWhile()?

objects.toStream.map(run(request, _)).dropWhile(isResultOk(_))

Esto eliminará todos los resultados exitosos, aunque todos los resultados sean exitosos.
(es decir.Esto creará un problema si todos los resultados son correctos).

Entonces, usaremos span().
c.span(p) = (c.takeWhile(p), c.dropWhile(p))
Probaremos si hay resultados que no están bien.
Si hay un resultado que no es correcto, devolveremos el primer resultado.
De lo contrario, devolveremos el último resultado correcto.

val (succ, fail) = objects.toStream.map(run(request, _)).span(isResultOk(_))
fail.headOption.getOrElse(succ.last)

fail.headOption regresará Some(fail's first element) si fail no está vacío, de lo contrario None.

En resumen,

val objects = Array(A, B, C)

def apply(request: Request): Validated[Result] = {
  val (succ, fail) = objects.toStream.map(run(request, _)).span(isResultOk(_))
  fail.headOption.getOrElse(succ.last)
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top