2009-07-04 18 views
6

Los marcos de inyección de dependencias en Ruby se han declarado prácticamente innecesarios. Jamis Buck escribió sobre esto el año pasado en su publicación de blog LEGOs, Play-Doh, and Programming.Sustitución de implementación de tiempo de ejecución con Ruby

La alternativa general aceptada parece estar utilizando algún grado de inyección de constructor, pero simplemente proporcionando valores predeterminados.

class A 
end 

class B 
    def initialize(options={}) 
    @client_impl = options[:client] || A 
    end 

    def new_client 
    @client_impl.new 
    end 
end 

Este enfoque está bien para mí, pero parece que falta una cosa de las configuraciones más tradicionales: una manera de sustituir implementaciones en tiempo de ejecución basado en algunas interruptor externo.

Por ejemplo, con un marco de inyección de dependencias que podía hacer algo como esto (Pesudo-C#):

if (IsServerAvailable) 
    container.Register<IChatServer>(new CenteralizedChatServer()); 
else 
    container.Register<IChatServer>(new DistributedChatServer()); 

Este ejemplo simplemente registra una IChatServer implementación diferente dependiendo de si nuestro servidor centeralized está disponible.

Como todavía estamos utilizando el constructor en Ruby, no tenemos control de programación sobre las dependencias que se usan (a menos que especifiquemos cada una nosotros mismos). Los ejemplos que da Jamis parecen adecuados para hacer que las clases sean más comprobables, pero parecen carecer de las facilidades para la sustitución.

¿Cuál es mi pregunta? ¿Cómo resuelves esta situación en Ruby? Estoy abierto a cualquier respuesta, incluso "simplemente no necesitas hacer eso". Solo quiero saber la perspectiva de Ruby sobre estos asuntos.

Respuesta

8

Además de la sustitución del constructor, puede almacenar la información en una variable de instancia ("atributo"). De su ejemplo:

class A 
end 

class B 
    attr_accessor :client_impl 

    def connect 
    @connection = @client_impl.new.connect 
    end 
end 

b = B.new 
b.client_impl = Twitterbot 
b.connect 

También podría permitir la dependencia a estar disponible como una opción para el método:

class A 
end 

class B 
    def connect(impl = nil) 
    impl ||= Twitterbot 
    @connection = impl.new.connect 
    end 
end 

b = B.new 
b.connect 

b = B.new 
b.connect(Facebookbot) 

técnicas También podría mezclar y combinar:

class A 
end 

class B 
    attr_accessor :impl 

    def initialize(impl = nil) 
    @impl = impl || Twitterbot 
    end 

    def connect(impl = @impl) 
    @connection = impl.new.connect 
    end 
end 

b = B.new 
b.connect # Will use Twitterbot 

b = B.new(Facebookbot) 
b.connect # Will use Facebookbot 

b = B.new 
b.impl = Facebookbot 
b.connect # Will use Facebookbot 

b = B.new 
b.connect(Facebookbot) # Will use Facebookbot 

Básicamente, cuando las personas hablan sobre Ruby y DI, lo que quieren decir es que el lenguaje en sí mismo es lo suficientemente flexible como para posibilitar la implementación de cualquier cantidad de estilos de DI sin necesidad de un marco especial.

Cuestiones relacionadas