views:

131

answers:

4

I have a method returning an ISomething. I am trying to use Inversion of Control throughout my program... but I have no idea how to do it when it comes to the return value - I cannot pass it to the method, or to the object when constructing it, because this method is supposed to create the object.

The only solution I see is to pass an ISomething factory (Func<ISomething>) to the object when constructing it. Is this the usual / correct solution or is something else recommended?

[Edit] Weird - nobody else had this problem? I mean, there's no up- (or even down-) voting...

[Edit] More details: the method is supposed to import an IExcel into an ITable. The consensus below seems to be that I should think of this method as a converter, transforming an IExcel into an ITable, thus receiving both as arguments instead of returning the ITable value. I'll leave this up for one more day and then go with one of the responses if nothing new shows up.

+1  A: 

Is your method supposed to be constructing the ISomething, or setting it's state and returning an ISomething with state initialized? (or does this need to be one operation?)

If your class has a method that is responsible for creating an ISomething, then it is an ISomethingFactory (unless you have mixed concerns). In that case, it has the logic to choose the correct ISomething implementation and create it, so you shouldn't worry too much about the IoC aspect.

However, if this class's concern is not creating the appropriate ISomething, then I would probably pass a factory in. The issue is, that in most cases your DI container should be able to do this for you (call a factory to get the appropriate ISomething), so seeing a method that must create an ISomething that is not a factory makes me nervous.

Trying to give architectural guidance is always tough in a forum like this though - but with the limited input there I'd say either you have a factory that you didn't know was a factory, or you have a problem.

Philip Rieck
Interesting. It's supposed to construct the ISomething. The method reads an Excel file and returns an ITable (what kind of ITable is the issue - for a while I'll work with an in-memory table, but I'll want to switch to an SQLite or even SQL Server table at some point). It sounds weird to consider my importer class an ITable factory though...
Marcel Popescu
Thinking "out loud" some more... if I don't go with the "my class is an ITable factory" option, it seems like I should treat it as a Converter instead of an Importer. That way, it can take two arguments, an Excel file and an (out) ITable, and convert the first to the second. I guess I'll have to sleep on it, but thanks for the brainstorm :)
Marcel Popescu
I'd probably lean to your second idea (a converter or parser). Like you, I would also be hesitant to put a lot of importer logic in a factory.
Philip Rieck
A: 

In the constructor of the object that contains the method returning ISomething, get an instance of the IoC container. Then the method that instantiates the ISomething can use the IoC container to get an object of the correct type.

I.e:

public class ExcelImporter { private IObjectFactory ObjectFactory { get; set; }

public ExcelImporter(IObjectFactory objectFactory)
{
    ObjectFactory = objectFactory;
}

public ITable Import()
{
    var result = ObjectFactory.Get<ITable>();
    { Do import here }
    return result;
}

}

Harley Pebley
This works as a "how to do it" method... I was looking for a "should I do it this way, and if so why" kind of answer :)
Marcel Popescu
Well that's more of a philosophical discussion; you'll never get a hard a fast answer. :-)That said, I don't have a lot of grief with doing it the way I showed. It leaves the instantiation of the object with the IoC container, where it belongs. Thus, it allows testing by giving the IoC container a test implementation of ITable in the test framework....
Harley Pebley
...OTOH, thinking about it as a converter as Marcel Popescu mentioned in comments above also lets the IoC container take care of object instantiation without the importer object knowing about it. This reduces dependencies a bit and makes test setup easier; you just pass in the test ITable rather than a test IoC container that has been configured to return a test ITable. However, this has a drawback in that you have to instantiate a new importer each time you want to call Import.So the right way depends on your use case.
Harley Pebley
A: 

I think you are just going too deep. One of the purposes of IoC is decoupling of the execution of a certain task from implementation and it sounds like you are decoupling the execution from itself. For the execution, you want to know the returned interface so you can run your process so you can call the appropriate methods (i.e., your app should eventually do something).

Consider this:

interface IPrinter
{
    void Print();
}

interface IReader
{
    string Read();
}

void Run(IReader reader, IPrinter printer)
{
    string text = reader.Read();
    printer.Print(text);
}

Your execution is to read and print something. The readers and printers are independent systems and your execution doesn't care how they work. You can then use a dependency injection framework to build the objects for you.

If you're worried about return values in your systems, you shouldn't be -- you need a concrete implementation somewhere.

Austin Salonen
I think the Wikipedia article is badly mistaken, what he's showing has no relationship to what I'm talking about. (He's moving logic into the DAO object - maybe that's related to the SRP, but definitely not to IoC.)
Marcel Popescu
+1  A: 

"I have an object that needs to create an object and do something with it, but I don't want the object to know what kind of object is being created, except that it should be of a certain interface."

That sounds almost like a textbook description of the Abstract Factory pattern, which would suggest that your object has a dependency on an appropriate factory.

Which also neatly splits the responsibilities - one object is responsible for creating an object of the appropriate type, while the other is responsible for performing operations on it before returning it.

kyoryu
Thanks; good point.
Marcel Popescu