2009-07-04 16 views
6

Dipendenze Le strutture di iniezione in Ruby sono state praticamente dichiarate non necessarie. Jamis Buck ha scritto su questo anno scorso nel suo post sul blog LEGOs, Play-Doh, and Programming.Sostituzione dell'implementazione di runtime con Ruby

L'alternativa generale accettata sembra utilizzare un certo grado di iniezione del costruttore, ma semplicemente la fornitura di valori predefiniti.

class A 
end 

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

    def new_client 
    @client_impl.new 
    end 
end 

Questo approccio è bene per me, ma sembra mancare una cosa da configurazioni più tradizionali: un modo di sostituire implementazioni in fase di esecuzione sulla base di alcuni switch esterno.

Ad esempio, con un quadro Dependency Injection avrei potuto fare qualcosa di simile (pesudo-C#):

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

Questo esempio registra solo un diverso IChatServer implementazione a seconda che il nostro server centeralized è disponibile.

Dato che stiamo ancora usando il costruttore in Ruby, non abbiamo controllo programmatico sulle dipendenze che vengono utilizzate (a meno che non specifichiamo ciascuna di esse). Gli esempi forniti da Jamis sembrano adatti a rendere le lezioni più testabili, ma sembrano mancare di possibilità di sostituzione.

La mia domanda è: come risolvi questa situazione in Ruby? Sono aperto a qualsiasi risposta, tra cui "non hai proprio bisogno di farlo". Voglio solo conoscere la prospettiva di Ruby su questi argomenti.

risposta

8

Oltre alla sostituzione del costruttore, è possibile memorizzare le informazioni in una variabile di istanza ("attributo"). Dal tuo esempio:

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 

Si potrebbe anche consentire la dipendenza da mettere a disposizione come opzione per il metodo:

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) 

tecniche Si potrebbe anche mix-and-match:

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 

Fondamentalmente, quando le persone parlano di Ruby e DI, ciò che intendono è che il linguaggio stesso è sufficientemente flessibile da consentire di implementare qualsiasi numero di stili di DI senza bisogno di un framework speciale.

Problemi correlati