IoC - Multiple implementations support for a singl

2019-02-06 01:43发布

问题:

I am wondering why .Net IoC containers do not easily support multiple implementations for a single interface! May be I am wrong, but as far I have seen, frameworks like Ninject partially supports this feature using annotations (how?). I do not think other frameworks like Windsor or simple injector have an easy mechanism to support this scenario.

Is there any reason why this is not supported by many frameworks? AFAIK, one of the most important reasons to use interfaces is to achieve loose coupling. If the frameworks designed to improve loose coupling, do not fluently support multiple implementations for a single interface, I do not understand why!

P.S. Of course I understand that there will be a resolution issue during run time, and the container would be confused which implementation to choose, but that is something which has to be considered in the design, right?

回答1:

Unity has the same functionality

Register named dependency

    var container = new UnityContainer();
    container.RegisterType<IConnector, Connector>("TestConnector");

Resolve by name

    container.Resolve<IConnector>("TestConnector");

the same approach

    [Dependency("TestConnector")]
    public IConnector Connector { get; set; }

Windsor has the same

class Program
{
    static void Main(string[] args)
    {
        var container = new WindsorContainer()
            .Register(Component.For<IConnector>().ImplementedBy<ConnectorA>().Named("ConnectorA"))
            .Register(Component.For<IConnector>().ImplementedBy<ConnectorB>().Named("ConnectorB"));

        var connectorA = container.Resolve<IConnector>("ConnectorA");
        Console.WriteLine("Connector type: {0}", connectorA.GetType());
        var connectorB = container.Resolve<IConnector>("ConnectorB");
        Console.WriteLine("Connector type: {0}", connectorB.GetType());
        Console.ReadKey();
    }
}

public interface IConnector
{
}

public class ConnectorA : IConnector
{

}

public class ConnectorB : IConnector
{

}


回答2:

I advice to look at Convention over Configuration and especially Convention Based Dependency Injection and context-based dependency injection. Most of IoC if not all supports both approaches. You could find many interesting samples with different IoC libraries, when several implementation bind to one interface, and how useful it could be.

For example ninject does support binding of several implementation of one interface: depends on context or attributes, by names, and so one.

By context

Following snippet binds on implemetation depends on target type automaticaly:

Bind<IWarrior>().To<Samurai>().WhenInjectedInto(typeof(OnLandAttack));
Bind<IWarrior>().To<SpecialNinja>().WhenInjectedInto(typeof(AmphibiousAttack));

By name

Very helpful when you configuration is in XML or database. Take into account InNamedScope also:

Bind<IWeapon>().To<Shuriken>().Named("Strong");
Bind<IWeapon>().To<Dagger>().Named("Weak");

By convention

With different dependency configuration at different parts of your project.



回答3:

Your premise is wrong.

Windsor quite happily accepts registrations of multiple implementations of the same service. In addition to the named component resolution support mentioned by GSerjo, in Windsor (by default), the first registered implementation will win but you can override this by using IsDefault() method when registering an alternative implementation. Please see http://docs.castleproject.org/Windsor.Registering-components-one-by-one.ashx for more details.

If you wish to exercise more control over the selection from multiple implementations you can create an IHandlerSelector implementation to do so. Please see http://stw.castleproject.org/Windsor.Handler-Selectors.ashx for more details.



回答4:

My container Griffin.Container supports it.

registrar.RegisterConcrete<OneImplementation>();
registrar.RegisterConcrete<AnotherImplementation>();

And to fetch:

var services = container.Resolve<ITheService>();

However, you can't get one specific implementation. It's a design decision. It's much better to register a factory in the container if have to get a specific implementation. Read more here in the best practices section.

Griffin.Container can be found at github: https://github.com/jgauffin/griffin.container



回答5:

StructureMap provides these abilities:

For<IMyInterface>().Add<MyInterfaceImpl1>().Named("MyInterfaceImpl1");
For<IUsingInterface>().Add<UsingInterfaceImpl>().Ctor<IMyInterface>().Is(i => i.GetInstance<IMyInterface>("MyInterfaceImpl1"));


回答6:

Your question is a bit vague, since you don't supply a concrete example of when you think you need this. In most cases there is a problem in your application or design, or you aren't following DI best practices.

All containers allow you to register multiple dependencies with the same interface as an IEnumerable<ThatInterface>, even if they do not have deep support for multiple instances. Injecting lists of services into other services however, is a design smell, and it would be better to hide this list behind a Composite. This hides the fact that there are multiple implementations behind the abstraction, and allows you to easily change the way those multiple implementations are used, by changing just a single place in the application. I don't believe any IoC framework has any support for generating composites for you, since the there is no one default way of processing the wrapped implementations. You'll have to write this Composite yourself. However, since writing such a composite is really, really simple, this justifies not having such feature in the framework.

If you want to have multiple implementations, but always need one to be returned, based on some configuration, there are always ways to do this. Most containers allow you to configure those dependencies in an XML configuration file. But even if a container does not contain such feature, reading this value from the configuration file manually and registering the right type in the container is very easy.

If you have one implementation of a certain interface for production and another implementation for unit testing purposes, you should only register the production implementation in the container. Your unit tests should be clear of any DI container, and you should manually create a class under test, and inject fake dependencies in its constructor, by simply newing the type up. Using a DI container, pollutes and complicates your tests. To pull this off, you will need to design such type around the constructor injection pattern. Don't call the container (or any other facade over the container) inside the service under test, to retrieve dependencies.