2012-07-04 11 views
8

で名前付きインスタンスをマップするために多分これは簡単ですが、インターネット上でそれを検索することは、すでにここに私城ウィンザー - どのようにコンストラクタ・インジェクション

を頭の痛みを与えることが問題である:

interface IValidator 
{ 
    void Validate(object obj); 
} 

public class ValidatorA : IValidator 
{ 
    public void Validate(object obj) { } 
} 

public class ValidatorB : IValidator 
{ 
    public void Validate(object obj) { } 
} 


interface IClassA { } 
interface IClassB { } 

public class MyBaseClass 
{ 
    protected IValidator validator; 

    public void Validate() 
    { 
     validator.Validate(this); 
    } 
} 

public class ClassA : MyBaseClass, IClassA 
{ 
    //problem: validator should ValidatorA 
    public ClassA(IValidator validator) { } 
} 

public class ClassB : MyBaseClass, IClassB 
{ 
    //problem: validator should ValidatorB 
    public ClassB(IValidator validator) { } 
} 

public class OtherClass 
{ 
    public OtherClass(IClassA a, IClassB b) { } 
} 


//on Main 
var oc = container.Resolve<OtherClass>(); 

任意のアイデア?私は、Namedと城ウィンザーはClassAClassBにそれらのバリデータを適切に注入することができるか、今の問題をValidatorAValidatorBを登録

EDIT、それを行う方法はありますか?それとももっと良い解決策がありますか?

私のクラスデザインが間違っていると思う人は、私に助言をしてください。これまでのところ私はそれが正しいと思います。はい、バリデーターは特定のクラスに対して特定の構成を持っています。

  1. Validatorは複雑なオブジェクトであり、データベースに接続する必要があります。したがって、ユニットテストの理由から、コンストラクタの代わりにインターフェイスを渡す必要があります。
  2. 私が使用する唯一の方法はValidate
  3. あるので、バリのいずれかに異なるインターフェースを使用する方法私はMyBaseClass.Validate()共通テンプレートメソッドパターンがそれではないと思いますか?
+2

問題の説明は要約です。 –

+2

ClassAがIValidatorの特定の実装を必要とする場合、インタフェースは有用な抽象化を提供しないので、設計は間違っています。 –

+0

@Jacek Grogon、いいえ、私は 'Validate'メソッドしか必要としません。実際には' Validate'メソッドを呼び出すClassAとClassBのベースクラスがあります。複雑さを隠すためにここに書きません。 – ktutnik

答えて

9

あなたが与えられたインタフェース(IValidator)に実装命名登録された複数のを持っている場合は、あなたが使用したいんどちらを指定することができますServiceOverridesを用いて消費者クラス(ClassAClassB)を登録するとき:

以下コンテナ設定プロバイダOtherClassClassA有するインスタンスをValidatorAClassBインスタンスと012と:

var container = new WindsorContainer(); 

container.Register(Component.For<IClassA>().ImplementedBy<ClassA>() 
    .DependsOn(ServiceOverride.ForKey<IValidator>().Eq("ValidatorA"))); 
container.Register(Component.For<IClassB>().ImplementedBy<ClassB>() 
    .DependsOn(ServiceOverride.ForKey<IValidator>().Eq("ValidatorB"))); 

container.Register(Component.For<IValidator>().ImplementedBy<ValidatorA>() 
    .Named("ValidatorA")); 
container.Register(Component.For<IValidator>().ImplementedBy<ValidatorB>() 
    .Named("ValidatorB")); 

container.Register(Component.For<OtherClass>().ImplementedBy<OtherClass>()); 

var oc = container.Resolve<OtherClass>(); 
+0

ありがとう、これは私が欲しいものです。ケースクローズ – ktutnik

1

あなたが、共通のコンテナに独立したタイプとして(ValidatorBClassAValidatorAClassBで)タイトなカップルを置くしようとしていることが表示されます。これは無意味です。このようなタイトなカップリングに頼らなければならないのであれば、この点に関して依存性注入を忘れて、型を厳密に参照してください。

これは、すべてのクラスに対して共通のバリデーターを実装できれば、より意味があります。たとえば、クラスにバリデーションルールの提供を担当させ、Validator はルールを適用するだけです。あるいは、クラス内にバリデーション全体を含めることもできます。これはおそらくここで最も合理的なシナリオです。

MyBaseClass.Validate()コントロールの反転のように見えますが、テンプレートメソッドのようには見えません。ちょうどウィンザーコンテナ設定に焦点を当て、あなたの選択したアーキテクチャの詳細に行くことなく

+0

本当にありがとうございました。はい、クラスは仲間でしたが、プロセスは繰り返します。実際にはMyBaseClass.Validate()にタスクがあります:1. 'validator.Validate(this)'からエラーを取得します。2. Dispatch 'DataErrorEvent' 3.' HasError'プロパティを変更します(なぜ、私はtempalteメソッドと呼ばれますか)。これらのタスクはすべて、assynchronで実行する必要があります。それはすべてのClassAとClassBの権利でそれを行うことは悪い考えであろうか? – ktutnik

+0

もしそうなら、 '' Validate() ''は実際にはテンプレートメソッドです。それでも、 '' ValidatorA''ではなく、 '' ClassA''に '' ValidateCore() ''などの成分メソッドを入れてみませんか?繰り返しの場合は、 '' MyBaseClass''に入れてください。 '' Validator''クラスを抽出すると複雑さが増し、明快さが失われます。 –

+0

もし私がmsitakenしていなければ、 'ClassA'の中の' ValidatorA'をインスタンス化し、新しいメソッド 'ValidateCore'を作成し、' MyBaseClass.Validate() 'から' ValidateCore'を呼び出してください。どのような場合でも 'ValidatorA'や' ValidatorB'がデータベースに接続されるべきユニットテストを行うことは不可能なので、私はそれができないと思います。 – ktutnik

関連する問題