开发者

Dependency injection containers: how to handle objects that aren't a dependency of anything?

开发者 https://www.devze.com 2023-01-13 20:08 出处:网络
When we use a dependency injection container, ideally we pull only a single-top level object from it (e.g. an instance of Program) and let the rest of the application be composed automatically by the

When we use a dependency injection container, ideally we pull only a single-top level object from it (e.g. an instance of Program) and let the rest of the application be composed automatically by the container.

However, sometimes there are objects which are not a dependency of anything else, yet we want to have them in the object graph. For example, I could have a Notifier class with a Bazinga event, and this BazingaConsoleLogger class:

public class BazingaConsoleLogger
{
    private readonly Notifier notifier;

    public BazingaConsoleLogger(Notifier notifier)
    {
        this.notifier = notifier;
        this.notifier.Bazinga += HandleBazinga;
    }

    private void HandleBazinga(object sender, EventArgs args)
    {
        Console.WriteLine("Bazinga!");
    }
}

Because Bazing开发者_如何学PythonaConsoleLogger is not a dependency of anything, it will not be created by the dependency injection container. What is the best way to fix this?


If BazingaConsoleLogger is a service and not a dependency of anything, then it's not used anywhere in your program, so the class can be deleted. Less code FTW! :-)

I don't think that's what you really mean, so can you further explain how you are currently using BazingaConsoleLogger? If you are in fact using BazingaConsoleLogger, you already have a dependency (explicit or not) to BazingaConsoleLogger.

EDIT: to wire events loosely I use Windsor's Event Wiring facility. If your container doesn't have anything like it, it shouldn't be hard to code it, here are the general principles.


In Windsor you could hack something like this:

container.Register(
   Component.For<Notifier>()
      .OnCreate((kernel, notifier) => 
         notifier.Bazinga += kernel.Resolve<BazingaConsoleLogger>().HandleBazinga)
);

however I agree with Mauricio Scheffer and I would treat this as smell and rethink your design in this case.


A IListener marker interface could be added to the class. In the application start-up code, I could then pull all IListener instances from the container in addition to the main program instance:

container.Resolve<IEnumerable<IListener>>(); // just to create the listeners
var program = container.Resolve<Program>();
program.Run();

Another option is to use an IStartable interface (inspired by Autofac) which solves a more general problem, i.e. the launching of multiple services at start-up. In the case of an event listener class, the Start method could be used to subscribe to the event (thus removing this responsibility from the constructor) or simply do nothing. The Program class could be one of the IStartable implementations.

var startables = container.Resolve<IEnumerable<IStartable>>();
foreach (var startable in startables)
{
    startable.Start();
}
0

精彩评论

暂无评论...
验证码 换一张
取 消