Estoy buscando construir un patrón de tubería con Scala. Deseo después de escribir los objetos del canal, que podrían estar conectados entre sí de esta manera:Cómo implementar elegantemente el patrón de tubería usando Scala
Pipeline1 :: Pipeline2 :: Pipeline3 ...
he experimentado con algunas ideas hasta el momento. Algunos trabajan y otros no. Pero ninguno de ellos parece deshacerse por completo del código repetitivo. Lo siguiente es lo más cercano que tengo.
En primer lugar definir la clase abstracta de la tubería y Fuente:
// I is the input type and O is the output type of the pipeline
abstract class Pipeline[I, +O](p: Pipeline[_, _ <: I]) {
val source = p
val name: String
def produce(): O
def stats():String
}
abstract class Source[+T] extends Pipeline[AnyRef, T](null)
A continuación, he creado dos oleoductos y tratar de vincularlos
// this creates a random integer
class RandomInteger extends Source[Int] {
override val name = "randInt"
def produce() = {
scala.Math.round(scala.Math.random.asInstanceOf[Float] * 10)
}
def stats()="this pipeline is stateless"
}
// multiply it by ten
class TimesTen(p: Pipeline[_, Int]) extends Pipeline[Int, Int](p) {
private var count = 0 // this is a simple state of the pipeline
override val name = "Times"
def produce = {
val i = source.produce()
count += 1 // updating the state
i * 10
}
def stats() = "this pipeline has been called for " + count + " times"
}
object TimesTen {
// this code achieves the desired connection using ::
// but this has to be repeated in each pipeline subclass.
// how to remove or abstract away this boilerplate code?
def ::(that: Pipeline[_, Int]) = new TimesTen(that)
}
Ésta es la clase principal, donde dos tuberías están vinculados.
object Pipeline {
def main(args: Array[String]) {
val p = new RandomInteger() :: TimesTen
println(p.source)
for (i <- 0 to 10)
println(p.produce())
println(p.stats())
}
}
Este código funciona. Pero tendría que repetir el código en el objeto complementario TimesTen en cada clase de interconexión que escribo. Esto ciertamente no es deseable. ¿Hay alguna forma mejor de hacer esto? La reflexión podría funcionar, pero escuché cosas malas al respecto, como que todo lo que involucre la reflexión es un mal diseño. Tampoco estoy seguro del apoyo de Scala para la reflexión.
Gracias por su tiempo.
Actualización: Diseñé este problema de juguete para que sea fácil de entender. Como solución general, y según lo requiera mi aplicación, cada objeto de canalización tiene un estado, que está idealmente encapsulado dentro del propio objeto en lugar de estar expuesto a cualquier otra canalización. He modificado el código anterior para reflejar esto. Ojalá pudiera haber una solución basada en objetos. Todavía estoy experimentando y te dejaré saber si encuentro uno.
Actualización 2: Después de algunos pensamientos, creo que la idea de la tubería es en realidad una función generalizada que contiene algunos estados internos, así como la capacidad de componer una función Function0
con una función Function1
. En Scala, la clase Function0
no tiene el método compose()
o andThen()
.
objetos en mi humilde opinión todavía podrían ser útiles. Las tuberías pueden tener estados, que pueden caber cómodamente en un objeto. Creo que con la función de redacción, el problema puede ser resuelto. Una solución es crear un objeto auxiliar que contenga una lista de objetos de canalización y componerlos juntos. Veré si eso funciona mañana. –
Su código no se compila en 2.9.1 - dice que "randomInteger" no tiene el método "componer". (al igual que el método "andThen") – Rogach
'randomInteger compone timesTen' es el camino equivocado. No puede tomar 'Unit', multiplicarlo por diez, y luego alimentar a' randomInteger'. En su lugar, quiere 'randomInteger y Then timesTen' o' timesTen compose randomInteger'. Hacer uso de 'Function1' es definitivamente la forma más conveniente de hacer que esta idea de" pipeline "funcione. –