2009-07-04 16 views
6

Rubyの依存性注入フレームワークは、ほとんど不要と宣言されています。 Jamis Buckはこの昨年、ブログ記事LEGOs, Play-Doh, and Programmingにこう書いています。Rubyによるランタイム実装の置換

一般的に受け入れられている代替案は、ある程度のコンストラクタインジェクションを使用しているようですが、デフォルトを提供するだけです。

class A 
end 

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

    def new_client 
    @client_impl.new 
    end 
end 

このアプローチは、私が細かいですが、より伝統的なセットアップから一つ欠けているようだ:一部の外部スイッチに基づいて実行時に実装を置換する方法を。私はこの(pesudo-C#の)のような何かができる依存性注入フレームワークと例えば

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

この例で私たちのcenteralizedサーバーが利用可能であるかどうかに応じて、異なるIChatServer実装を登録します。

Rubyでコンストラクタを使用しているだけなので、使用する依存関係をプログラムで制御することはできません(それぞれ独自に指定しない限り)。 Jamisが提供する例は、クラスをよりテスト可能にするのに適しているようですが、置換のための機能が不足しているようです。

私の質問は、どのようにRubyでこの状況を解決するのですか?私は "あなたはただそれをする必要はありません"を含め、どんな答えにもオープンしています。これらの問題についてRubyの視点を知りたいだけです。

答えて

8

コンストラクタの置換に加えて、情報をインスタンス変数(「属性」)に格納することもできます。あなたの例から:

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) 

あなたはまた、可能性がミックス・アンド・マッチ技術:

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 

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 

また、依存関係がメソッドにオプションとして利用できるようにする可能性があります

基本的に、RubyとDIについて人々が話すとき、特別なフレームワークを必要とせずにDIのスタイルをいくつでも実装できるように、言語そのものが柔軟であるということです。

関連する問題