Melted Away
Melted Away

Reputation: 436

Injecting a dependency having constructor parameters in common with the higher level class

Sometimes when I design my classes, I need some dependency classes with similar constructor parameters. Assume we have

interface IDependency {
    void DoSomething();
}

class DependencyClass : IDependency {
    public DependencyClass(int length) {...}
    ...
}

class MainClass {
    public MainClass(int length, IDependency dependency) {...}
    ...
}

MainClass needs an instance of DependencyClass with the same length. I have two methods in mind to deal with it:

  1. Not creating instances of MainClass directly and always getting them from a factory. This way I can make sure that the desired IDependency instance will be provided to MainClass constructor.

  2. Passing a factory interface, say IDependencyFactory, to MainClass constructor instead of IDependency. So that I can get the desired instance of DependencyClass within my MainClass implementation.

I'm not sure if either of them is a good choice. Is there a best practice in this case?

Upvotes: 7

Views: 508

Answers (3)

Orest Savchak
Orest Savchak

Reputation: 4569

If your length is runtime variable - refer to this comment.

If you need this in runtime, you can use the solution #2 from this answer.

If it's compile time setting, you can inject it as any other dependency, that's normal that both MainClass and DependencyClass are dependent on the same value.

To have a safe compile-time injection, you can introduce something like that:

interface ILength {
    int Length { get; }
}

class StaticLength : ILength {
    public StaticLength(int length) {
        Length = length;
    }
}

void Main() {
    const int length = 10;
    ILength lengthDependency = new StaticLength(length);
    IDependency dep = new DependencyClass(lengthDependency);
    MainClass mainClass = new MainClass(lengthDependency, dep); 
}

And now you can implement any kind of ILength - read from configuration file, constant, web, mock, calculate base on any other dependency, random etc.

Upvotes: 4

Kit
Kit

Reputation: 21709

One of the simplest changes you could make to guarantee the length will be the same for both the DepencyClass and the MainClass is to add a Length property to the interface and initialize it from a constructor, and then allow MainClass to access the length (you then save one parameter on the MainClass, which is more DRY.

interface IDependency {
    int Length { get; }
    void DoSomething();
}

class DependencyClass : IDependency {
public DependencyClass(int length) { Length = length; }
    ...
}

class MainClass {
    public MainClass(IDependency dependency) {...}
    ...
    // do something with dependency.Length
}

This does have the possible drawback of "polluting" your interface and perhaps exposing the length to other code. That's up to you if that matters.

Upvotes: 4

Barr J
Barr J

Reputation: 10929

This is a very interesting question and the answer lies in what you try to achieve here.

Solution 1. you provided will make your IDependency the main factory interface, all instanced will be implemented with IDependency interface which will provide you with a lot of scalability - you will be able to use the IDependency interface to initialize additional classes in the future with different functionality, which is the classic use and implementation of factory design pattern.

solution 2. you provided will make your MainClass well, the Main Class of the program, you will handle the instances and their behavior inside the main class and the advantage here is that you can manage all of your instances through IDependency, and the only thing you do in MainClass is handle their logic, MinaClass will be your instances transporter and all of the fundamental changes for the base classes will be made in the interface IDependency which is a classic solution for Dependency Inversion.

The question still remains, what you are trying to achieve. Consider this question and the two possibilities and you have your answer.

Upvotes: 6

Related Questions