Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Castle Windsor - How to map Named instance in constructor injection

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?
like image 667
ktutnik Avatar asked Jul 04 '12 08:07

ktutnik


2 Answers

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>();
like image 180
nemesv Avatar answered Nov 12 '22 21:11

nemesv


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.

like image 2
Jacek Gorgoń Avatar answered Nov 12 '22 19:11

Jacek Gorgoń