2009-07-04 13 views
6

Struktury wtrysku Dependency Injection w Ruby zostały uznane za niepotrzebne. Jamis Buck napisał o tym w zeszłym roku w swoim blogu na blogu LEGOs, Play-Doh, and Programming.Podstawienie implementacji języka wykonawczego z Ruby

Ogólnie przyjętą alternatywą wydaje się być użycie pewnego stopnia wtrysku konstruktora, ale po prostu dostarczanie wartości domyślnych.

class A 
end 

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

    def new_client 
    @client_impl.new 
    end 
end 

Podejście to jest w porządku przez mnie, ale wydaje się, że brakuje jednej rzeczy od bardziej tradycyjnych konfiguracjach: sposób na zastąpienie implementacje w czasie wykonywania na podstawie jakiegoś zewnętrznego przełącznika.

Na przykład z Dependency Injection ram mogę zrobić coś takiego (pesudo-C#):

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

Ten przykład tylko rejestruje inną IChatServer realizacji w zależności od tego, czy nasz serwer centeralized jest dostępna.

Ponieważ wciąż używamy konstruktora w Ruby, nie mamy programowej kontroli nad zależnościami, które są używane (chyba że sami określimy siebie). Przykłady, które podaje Jamis, wydają się dobrze dostosowane do tego, aby zajęcia były bardziej testowalne, ale wydaje się, że brakuje im możliwości zastępowania.

Moje pytanie brzmi: jak rozwiązać tę sytuację w Ruby? Jestem otwarty na wszelkie odpowiedzi, w tym "po prostu nie musisz tego robić". Chcę tylko poznać perspektywę Rubiego na te tematy.

Odpowiedz

8

Oprócz podstawiania konstruktora można przechowywać informacje w zmiennej instancji ("atrybut"). Z Twojego przykład:

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 

Można też pozwolić zależność być udostępniane jako opcja do metody:

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) 

technik Można także 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 

Zasadniczo, gdy ludzie mówią o Rubim i DI, chodzi o to, że sam język jest wystarczająco elastyczny, aby umożliwić implementację dowolnej liczby stylów DI bez potrzeby posiadania specjalnej struktury.

Powiązane problemy