Castle Windsor - How to map Named instance in constructor injection
Asked Answered
G

2

11

maybe this is easy, but searching it on the internet already give me a head ache

here is the problem:

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>();

Any idea?

EDIT

I registered ValidatorA and ValidatorB with Named, now the problem how Castle Windsor can inject those validator properly to the ClassA and ClassB, is there a way to do that? or is there any better solution?

if there is someone think my class design is wrong please, i open for any advice. So far i think it correct. Yes, validator have specific configuration for specific Class. but there are reasons it is abstracted:

  1. Validator is a complex object, sometime should connect to database, so I MUST pass interface instead of implementation to constructor for unit testing reason.
  2. No way to use different interface for any of Validator, because the only method that i used is Validate
  3. I think MyBaseClass.Validate() a common template method pattern isn't it?
Gantlet answered 4/7, 2012 at 8:56 Comment(6)
Your description of problem is to abstract.Ova
If ClassA requires specific implementation of IValidator, then the design is wrong, as the interface provides no useful abstraction.Bengaline
@Jacek Grogon, no i only need the Validate method, actually there is a baseclass of ClassA and ClassB which is call the Validate method. but i don't write it here just to hide complexity.Gantlet
updated the code, so the only think i really need is the Validate methodGantlet
Create an interface for ClassA and ClassB something like IClassAValidator that inherits from IValidator and adds nothing more. Then get the validators to implement the specific interface. ClassA can then have IClassAValidator injected into it and pass the IValidator to MyBaseClass.Cockroach
@JackHughes: sorry don't properly read your answer, yes that was my thought.. thank you, but still looking for the exact answer.. is it really impossible to do that?Gantlet
C
12

Without going into the details of your chosen architecture just focusing on the Windsor container configuration:

If you have registered multiple named implementation to a given interface (IValidator), you can specify which one do you want to use when registering the consumer classes (ClassA, ClassB) with using ServiceOverrides:

The following container configuration providers an OtherClass with ClassA instance with a ValidatorA and ClassB instance with a ValidatorB:

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>();
Cornie answered 4/7, 2012 at 15:10 Comment(0)
A
2

It appears that you're trying to put tight couples (ClassA with ValidatorA, ClassB with ValidatorB) as independent types into a common container. This is pointless. If you must rely on tight coupling like this, forget dependency injection in this regard and just hard-reference the types.

This would make more sense if you could implement a common validator for all classes. For instance, make the classes responsible for providing validation rules, and let Validator just enforce the rules. Or perhaps just include the whole validation inside your classes, which is probably the most sensible scenario here.

MyBaseClass.Validate() look like inversion of control, but not like a template method.

Amnion answered 4/7, 2012 at 12:4 Comment(6)
thanks for the response, yes the class was thight couple, but the process is repetitive, actually there are tasks in MyBaseClass.Validate(): 1. Get the erros from validator.Validate(this), 2. Dispatch the DataErrorEvent 3. Change the HasError property,(Thats why I call it tempalte method) all those task should done in assynchron way. it would be a bad idea to do it in every ClassA and ClassB right?Gantlet
If so then Validate() is indeed template method. Still, why not put the ingredient method, say ValidateCore() in ClassA and not in ValidatorA? If it's repetitive, then put it in MyBaseClass. Extracting the Validator class adds more complexity and hinders clarity.Bengaline
if im not msitaken, you mean instantiate the ValidatorA inside ClassA and make new method ValidateCore then call ValidateCore from MyBaseClass.Validate() right?. i think i can't do that because it will impossible to do unit test where ValidatorA or ValidatorB in any cases should connect to database.Gantlet
I mean remove Validator* and put that logic in Class*.ValidateCore() and call it in MyBaseClass.Validate(). What exactly are you unit testing? Why is ValidatorA connecting to a database? Give more context.Bengaline
ClassA or ClassB should only contains logic of the view (state of button, selectedIndex of combobox etc) it is really required to test its logic outside the database call, in some case Validator connect to database to get information, is there some property match specific condition of data or not? if i remove Validator and do the validation inside it so my unit testing should connect to database and i think it is not a good idea, do you think I over used the validator class? btw.. once again thank you, i really appreciate your time to try to solve my problemGantlet
The architecture seems somewhat odd, with DB validation bound directly to view. To cut further discussions about it, I recommend just going with the approach suggested by Jack Hughes in comments. There is no repetition in that.Bengaline

© 2022 - 2024 — McMap. All rights reserved.