jungle_mole
jungle_mole

Reputation: 320

IoC containers: slightly differ the structure of instance created

I am studying IoC, DDD and AOP concepts. I've read a number of articles, docs, Ninject manual (i'm restricted to use .NET 3.5), tried some stuff and so on.

It's hard to shove everything at once to my head, but motivation, concepts and technical matters are somewhat clear. And i'd been always feeling that i was missing something.

Firstly, as i understand IoC containers' purpose is initial object structure set up?

Like, set up container in composition root, create "main" object, that is being wired all the way by IoC container.

Then, as i understand, later all objects are instantiated with factories? Although i can't help myself to perceive factory as a case of service locator (that ended up considered antipattern and by the way is used as core mechanics of IoC containers).

So the question is: What if i want to create an instance with slightly different structure, e.g. i have

interface IFoo{}
interface IBar{}

class SomeClass
{
    SomeClass(IFoo obj){...}   
}

class Foo : IFoo
{
    Foo(IBar obj){...}   
}

class Bar : IBar
{
}

class FooBar : IBar //  also implements IBar interface
{
}

So, initial binding configuration is making SomeClass.Foo.Bar structure. Assume, i also need SomeClass.Foo.FooBar. What do i do? The options i can think of:

the problems here (besides that it looks like hack):

a) we must introduce some mnemonic system, which is not obscene/user friendly.

b) we must have rules/decorators for every factory with this "feature" (but looks like we can somewhat simplify the process at least with rules)

c) it resembles me of reflection usage with convention over configuration, which i'm averted of and treat it as a hack.

Or we may use attributes to set this up. Or may be i just don't know something.

Upvotes: 0

Views: 115

Answers (2)

Steven
Steven

Reputation: 172606

Firstly, as i understand IoC containers' purpose is initial object structure set up?

Forget about IoC containers for a moment. Dependency Injection is not about using tools. It's first and foremost about applying principles and patterns. The driving force behind Dependency Injection are the SOLID principles. I would even go as far as start your application without using an IoC container at all, and only start using one when is a really compelling reason to do so. This means that you simply build up the object graphs by hand. The right place to do this is in your Composition Root. This should be the single place where you compose your object graphs.

And do note that this advice comes from someone who is building and maintaining a IoC container himself.

Then, as i understand, later all objects are instantiated with factories?

When practicing Dependency Injection, you will see that the need to use factories actually minimizes. They can still be useful, but I only use them sparingly nowadays.

Reason for this is that a factory usually just adds an extra (useless) layer of abstraction.

When starting with making code more loosely coupled, developers are tempted to use a factory as follows:

public class SomeClass
{
    public void HandleSomething() {
        IFoo foo = FooFactory.Create();
        foo.DoSomething();
    }
}

Although this allows a Foo implementation to be decoupled from SomeClass, SomeClass still takes a strong dependency on FooFactory. This still makes SomeClass hard to test, and lowers reusability.

After experiencing such a problem, developers often start to abstract away the FooFactory class as follows:

public class SomeClass
{
    private readonly IFooFactory fooFactory;
    public SomeClass(IFooFactory fooFactory) {
        this.fooFactory = fooFactory;
    }

    public void HandleSomething() {
        IFoo foo = this.fooFactory.Create();
        foo.DoSomething();
    }
}

Here a IFooFactory abstraction is used, which is injected using constructor injection. This allows SomeClass to be completely loosely coupled.

SomeClass however now has two external dependencies. It both knows about IFooFactory and IFoo. This duplicates the complexity of SomeClass, while there is no compelling reason to do so. We will immediately notice this increase of complexity when writing unit tests. We will now suddenly have to mock two different abstactions and test them both.

Since we are practicing constructor injection here, we can simplify SomeClass -without any downsides- to the following:

public class SomeClass
{
    private readonly IFoo foo;
    public SomeClass(IFoo foo) {
        this.foo = foo;
    }

    public void HandleSomething() {
        this.foo.DoSomething();
    }
}

Long story short, although the Factory design pattern is still valid and useful, you will hardly ever need it for retrieving injectables.

Although i can't help myself to perceive factory as a case of service locator

No. A factory is not a service Locator. The difference between a factory and a locator is that with a factory you can build up only one particular type of objects, while a locator is untyped. You can build up anything. If you use an IoC container however, you will often see that the factory implementation will forward the request to the container. This should not be a problem, because your factory implementation should be part of your composition root. The composition root always depends on your container and this is not a form of Service Location, as Mark Seemann explains here.

Or we may use attributes to set this up. Or may be i just don't know something.

Refrain from using attributes for building up object graphs. Attributes pollute your code base and cause a hard dependency on an underlying technology. You absolutely want your application to stay oblivious to any used composition tool. As I started with, you might not even use any tool at all.

For instance, your object graph can be composed quite easily as follows:

new SomeClass(
    new Foo(
        new Bar()));

In your example, you seem to have two IBar implementations. From the context it is completely unclear what the function of this abstraction and these implementations are. I assume that you want to be able to switch implementations one some runtime condition. This can typically be achieved by using a proxy implementation. In that case your object graph would look as follows:

new SomeClass(
    new Foo(
        new BarProxy(
            new Bar(),
            new FooBar()));

Here BarProxy looks as follows:

public class BarProxy
{
    private readonly IBar left;
    private readonly IBar right;
    public BarProxy(IBar left, IBar right) {
        this.left = left;
        this.right = right;
    }

    public void BarMethod(BarOperation op) {
        this.GetBar(op).BarMethod(op);
    }

    private IBar GetBar(BarOperation op) {
        return op.SomeValue ? this.left : this.right;
    }
}

It's hard to say when you should start using a DI container. Some people like to stay away from DI containers almost always. I found that for the type of applications I build (that are based on these and these patterns), a DI container becomes really valuable, because it saves you from having to constantly update your Composition Root. In other words:

Dependency Injection and the SOLID principles help making your application maintainable. A DI library will help in making your composition root maintainable, but only after you made your application maintainable using SOLID and DI.

Upvotes: 3

Related Questions