Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor injection with non-dependency parameters

I have an interface ITradingApi like so:

public interface ITradingApi
{
    IOrder CreateOrder(...);
    IEnumerable<Symbol> GetAllSymbols();
    // ...
}

This is meant to be a facade for the different APIs of the vendors of trading software. My view model has a dependency on this trading API in its constructor:

public class MainViewModel
{
    public MainViewModel(ITradingApi tradingApi) { /* ... */ }
    // ...
}

I use Ninject as an IoC container, so I will create an instance of my view model like this:

var vm = kernel.Get<MainViewModel>();

Now, my problem:

The implementation of ITradingApi might need additional parameters to work.
Example:

  • One vendors API uses TCP/IP internally, so I need a hostname and a port.
  • Another vendor uses a COM object. Here I don't need any info.
  • A third vendor needs username and password of the account.

In the spirit of not allowing incomplete objects, I added these as parameters to the constructors of the concrete implementations.

Now, I am not sure, how this would work. Clearly, these additional parameters do not belong into the interface, because they are specific to each implementation.
On the other hand, these additional parameters need to be entered by the end-user and then passed to the implementation of ITradingApi, meaning that the user of ITradingApi needs intimate knowledge about the concrete implementation.
How to solve this dilemma?

UPDATE:
One approach could be to create an ITradingApiProvider that exposes a list of required parameters. The View could automatically create an input form for these parameters that is databound to the parameters in ITradingApiProvider. Now, when an ITradingApi instance is requested from the provider, it can make use of these parameters to create an instance of the concrete implementation. Clearly the implementation of ITradingApiProvider and ITradingApi are tightly coupled, but I think that is not a problem as long as each implementation of ITradingApi comes with a corresponding implementation of ITradingApiProvider.

like image 301
Daniel Hilgarth Avatar asked Jul 21 '11 07:07

Daniel Hilgarth


People also ask

Which injection is used for non mandatory dependencies?

Setter based Injection - It can be used by calling setter methods on your beans. It should be used for optional dependencies.

What is the limitations of constructor injection?

The main disadvantage to Constructor Injection is that if the class you're building is called by your current application framework, you might need to customize that framework to support it. Some frameworks assume that your classes will have a parameterless constructor.

Does @autowired use constructor injection?

With setter injection, Spring allows us to specify optional dependencies by adding @Autowired(required = false) to a setter method. This is not possible with constructor injection since the required=false would be applied to all constructor arguments.

What is constructor based dependency injection?

Dependency injection (DI) is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method.


2 Answers

Based on the information so far put forth here, I'd like to point out one or two things:

First of all, whether or not the concrete configuration values are supplied at composition time or truly first available at runtime as user input makes a huge difference. As long as they can be resolved at composition time things are easy because you can simply read the values from the environment and supply them to the appropriate constructors. So, for the rest of this answer I'm going to assume that things are much harder and you actually need to get those values from the user at runtime.

Instead of attempting to come up with a general-purpose configuration API I'd much rather model what's actually going on. In this case it sounds to me like we're collecting configuration values from the user, so why not model this explicitly?

Product Trader

Define an interface like this:

public interface ITradingApiTrader
{
    ITradingApi Create(Type apiType);
}

Here, it's assumed that apiType can cast to ITradingApi, but this can't be enforced by the compiler. (The reason I'm calling this a 'Trader' is because this is a variation of the Product Trader pattern (PLoPD 3).)

How is this different than before?

Well, you can implement the Create method by showing a user interface for each type of ITradingApi. Each concrete user interface gathers the values required for its own concrete ITradingApi implementation and subsequently returns a correctly configured instance.

If you know the concrete types at compile time, other variations include these:

public interface ITradingApiTrader
{
    ITradingApi CreateMT4TradingApi();

    ITradingApi CreateFooTradingApi();

    ITradingApi CreateBarTradingApi();

    // etc.
}

Perhaps you can also do this (although I haven't tried to compile this):

public interface ITradingApiTrader
{
    ITradingApi Create<T>() where T : ITradingApi;
}

Note also that you don't need to define the first ITradingApiTrader's Create method based on a Type - any identifier (such as an enum or string) might do instead.

Visitor

If the set of ITradingApi is (finite and) known at design time, the Visitor design pattern might also offer an alternative.

If you use a Visitor, you can make the Visit method show an appropriate user interface and then subsequently use the values collected from the user interface to create the appropriate ITradingApi instance.

Basically this is just a variation on the previous 'solution' where the Product Trader is implemented as a Visitor.

like image 77
Mark Seemann Avatar answered Sep 22 '22 16:09

Mark Seemann


Is this what your after?

   ninjectKernel.Get<MainViewModel>().WithConstructorArgument("tradingApi", 
       kernel.Get<ITaxCalculator>() .WithConstructorArgument("additionalParameter","someValue")));
like image 20
Razor Avatar answered Sep 21 '22 16:09

Razor