Dependency Injection and Class Inheritance
Asked Answered
M

6

20

I feel like this is something I should already know, but I'm just not firing on all engines today...

I have a base class with a single ctor that takes an implementation of an interface as it's only parameter. I'm using a DI framework and have my component registrations all set up and working fine.

When I inherit from this base class, unless I pass in a value to the base constructor, I have to define a parameterless ctor, which bypasses the DI.

So right now I have:

public class MyObjectBase
{
    IMyRequiredInterface _InterfaceImpl;
    public MyObjectBase(IMyRequiredInterface interfaceImpl)
    {
        _InterfaceImpl = interfaceImpl;
    }
    ...
}

public class AnotherObject : MyObjectBase
{
    public AnotherObject()
    {
    }
    ...
}

So, out of the gate this fails. I get errors when AnotherObject is instantiated indicating that there is no base class ctor that takes 0 parameters. Ok, I get that. But now I have a choice: either modify the descendant class ctor to take a similar parameter and pass that value on to the base ctor, or wire up a ctor chain in the base class that forces me to bypass DI and create a concrete implementation of the required interface and pass it in as part of the parameterless ctor declaration.

The goal is to meet the requirement of the base class without the descendant classes knowing anything about it.

Maybe I'm going about this all wrong, but it's bugging me. Any thoughts on a better way to handle this? I feel like I've got to be missing something simple...

Max answered 25/5, 2009 at 19:5 Comment(1)
For context, the actual classes I'm working on are MVC controllers and a ControllerBase. The ControllerBase does some work in an override of the Initialize method. The descendant classes really don't need to know anything about this requirement and don't directly gain behavior as a result of this occurence. I agree that this is a code smell and could be handled differently. I'm just looking for answers that specifically address the implications of using a dependency injection framework to manage ctor requirements.Max
D
23

The correct approach is:

public class AnotherObject : MyObjectBase {
    public AnotherObject(IMyRequiredInterface interfaceImpl) : 
        base(interfaceImpl) {
    }
}

You specifically asked for an approach other than this approach. Why?

The goal is to meet the requirement of the base class without the descendant classes knowing anything about it.

That's generally the wrong thing to do. Why do you want to do it?

Update:

Based on your later comment, you should probably use (and configure your container to use) property injection instead of constructor injection. That will get you all of your requirements.

Desberg answered 25/5, 2009 at 19:12 Comment(3)
It's not wrong if the subclass can reasonably supply an IMyRequiredInterface object for the superclass that would work for all instances of the subclass....Mouthwash
In many cases, you are correct. In the particular case, however, the context is the exclusion of explicit dependencies from within the services classes, with the use of a container to wire the dependencies together.Desberg
Code smells aside, the best solution for me would have been to use property injection. I opted to readdress the problem from a different perspective which allowed me to simply have a parameterless ctor in the base class. Thanks to everyone who answered.Max
M
4

Err....the whole point of inheriting from MyObjectBase is that, as it were, you get the good and the bad, as far as the behaviour goes. If you can't create a MyObjectBase without an object implementing IMyRequiredInterface, you can't create a subclass without such an object either.

So what do you do when someone doesn't hand you that. Do you have a default?

It's quite reasonable for a subclass to instantiate something that implements IMyRequiredInterface, and pass that to the superclass constructor with a super(...) call. Can you do that? (Though, as I recall, you can get a bit hung up on this in Java, having to call super before doing anything else....)

Mouthwash answered 25/5, 2009 at 19:11 Comment(0)
C
2

There's a code smell here. If you inherit from a class that has a parameterless constructor it means that the author of this base class intended that it cannot function properly without supplying the necessary dependency. If you inherit from it and call a base method that required this dependency your code will probably fail if the dependency is not supplied. So if you really think that you should completely override this behavior you don't have to inherit from this base class, otherwise just copy the constructor in the inherited class.

Clue answered 25/5, 2009 at 19:11 Comment(0)
E
0

What about providing a protected constructor in the base class that takes no paramters?

    class MyBase
{
    readonly int _x;
    public MyBase(int x)
    {
        _x = x;
    }

    protected MyBase()
    {
        _x = 0;
    }
}

class MyChild : MyBase
{
    public MyChild()
    {

    }
}
Exciter answered 25/5, 2009 at 19:19 Comment(0)
C
0

Most DI frameworks have the functionality to inject services into properties (Property Setter Injection) using Attributes so you can try that.

You can subclass but the subclass will have to know how to create or get the Interface Implementation (using the ServiceLocator or something).

From the DI's point of view it doesn't have anything to fulfill because the class only has an empty constructor (most use the constructor with the most params) and no attributes telling him to do anything else.

Cherenkov answered 25/5, 2009 at 19:52 Comment(0)
S
0

I had the same scenario where my base (concrete) class and the child class had a dependency, I was worried if these are different instances.

But, Autofac (I believe other container tools too) has .InstancePerRequest() which will share the same instance per Http request.

builder.RegisterType().As().InstancePerRequest();

Smalley answered 11/8, 2016 at 15:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.