scala - para - patron de inyeccion
¿Cómo se hace la inyección de dependencia con el patrón Cake sin hardcoding? (5)
Acabo de leer y disfruté el artículo del patrón Cake . Sin embargo, en mi opinión, una de las razones clave para usar la inyección de dependencia es que puede variar los componentes que utiliza un archivo XML o los argumentos de la línea de comandos.
¿Cómo se maneja ese aspecto de DI con el patrón Cake? Los ejemplos que he visto implican mezclar rasgos en forma estática.
Dado que mezclar en rasgos se realiza de forma estática en Scala, si desea variar los rasgos mezclados en un objeto, cree objetos diferentes en función de alguna condición.
Tomemos un ejemplo de patrón de pastel canónico. Sus módulos se definen como rasgos, y su aplicación se construye como un Objeto simple con un conjunto de funcionalidades mezcladas
val application =
new Object
extends Communications
with Parsing
with Persistence
with Logging
with ProductionDataSource
application.startup
Ahora todos esos módulos tienen buenas declaraciones de auto-tipo que definen sus dependencias entre módulos, por lo que la línea solo se compila si todas sus dependencias entre módulos existen, son únicas y están bien tipadas. En particular, el módulo Persistence tiene un tipo propio que dice que cualquier cosa que implemente Persistence también debe implementar DataSource, un rasgo abstracto del módulo. Dado que ProductionDataSource hereda de DataSource, todo está muy bien, y esa línea de construcción de aplicaciones se compila.
Pero, ¿qué sucede si desea utilizar un DataSource diferente, apuntando a alguna base de datos local para fines de prueba? Supongamos además que no puede simplemente reutilizar ProductionDataSource con diferentes parámetros de configuración, cargados desde algún archivo de propiedades. Lo que harías en ese caso es definir un nuevo rasgo, TestDataSource, que extienda DataSource y mezclarlo en su lugar. Incluso podría hacerlo de forma dinámica en función de un indicador de línea de comando.
val application = if (test)
new Object
extends Communications
with Parsing
with Persistence
with Logging
with TestDataSource
else
new Object
extends Communications
with Parsing
with Persistence
with Logging
with ProductionDataSource
application.startup
Ahora que parece un poco más detallado de lo que nos gustaría, especialmente si su aplicación necesita variar su construcción en múltiples ejes. En el lado positivo, normalmente solo tienes un pedazo de lógica de construcción condicional como esa en una aplicación (o en el peor de los casos, por ciclo de vida identificable de los componentes), por lo que al menos el dolor se minimiza y se protege del resto de tu lógica.
El elevador tiene algo integrado en esas líneas. Se trata principalmente de código scala, pero tiene algún control de tiempo de ejecución. http://www.assembla.com/wiki/show/liftweb/Dependency_Injection
Hasta que el complemento AutoProxy esté disponible, una forma de lograr el efecto es usar la delegación:
trait Module {
def foo: Int
}
trait DelegatedModule extends Module {
var delegate: Module = _
def foo = delegate.foo
}
class Impl extends Module {
def foo = 1
}
// later
val composed: Module with ... with ... = new DelegatedModule with ... with ...
composed.delegate = choose() // choose is linear in the number of `Module` implementations
Pero ten cuidado, la desventaja de esto es que es más detallado, y debes tener cuidado con el orden de inicialización si usas var
s dentro de un rasgo. Otro inconveniente es que si hay tipos dependientes de la ruta dentro del Module
anterior, no podrá usar la delegación tan fácilmente.
Pero si hay una gran cantidad de implementaciones diferentes que pueden variarse, probablemente le cueste menos código que enumerar casos con todas las combinaciones posibles.
La respuesta corta es que Scala actualmente no tiene ningún soporte integrado para dynamic mixins.
Estoy trabajando en el complemento autoproxy para admitir esto, aunque actualmente está en espera hasta la versión 2.9, cuando el compilador tendrá nuevas características, lo que lo hará una tarea mucho más fácil.
Mientras tanto, la mejor manera de lograr casi la misma funcionalidad es implementar su comportamiento añadido dinámicamente como una clase contenedora, y luego agregar una conversión implícita al miembro envuelto.
Scala es también un lenguaje de script. Entonces su configuración XML puede ser un script de Scala. Es seguro para tipos y no es un lenguaje diferente.
Simplemente mira el inicio:
scala -cp first.jar:second.jar startupScript.scala
no es tan diferente que:
java -cp first.jar:second.jar com.example.MyMainClass context.xml
Siempre puedes usar DI, pero tienes una herramienta más.