Reputation: 546
I've watched quite a few IOC/DI/ninject tutorials and videos over the last couple of days but I am still not convinced I get the point.
In most the examples they usually say something like, what happens if we want a Sword or a Shuriken, we need to define IWeapon. we want to seperate the knowledge of the actual weapon from the warrior.
So we inject the required IWeapon into the Warrior and then let Ninject (or other) get us the required class to go into the IWeapon (say sword or shuriken), but then THEY go ahead and create a default binding which create ONE SINGLE binding of a Sword to IWeapon.
How do we tell it which one to use? we cannot used named bindings because you cannot set the named binding on the weapon and then Get.
In my case I have a message I am reading from a queue, it will include the required details about what to send and who to send to.
I also have an interface which knows how to send a message with implementations for SMS, email, iphone etc. I am not able to understand how to use the DI in this case without having to put a switch somewhere in my code :-(
public interface INotify
{
void Send(Message msg);
}
public class Message
{
public Message(INotify notify)
{
_notify = notify;
}
public void Send()
{
_notify.Send(this);
}
readonly INotify _notify;
public string Type { get; set; }
public string Text{ get; set; }
public string Name { get; set; }
public string Number { get; set; }
public string Email { get; set; }
}
_kernel.Bind<INotify>().To<NotifyEmail>();
//kernel.Bind<INotify>().To<NotifySMS>().Named("sms");
//kernel.Bind<INotify>().To<NotifyAPNS>().Named("iphone");
var msg = _kernel.Get<Message>();
msg.Send();
Isn't the whole point to make it easy to instantiate the required class?
Upvotes: 3
Views: 321
Reputation: 4914
Dependency injection in general is about absolving classes of having to resolve their dependencies for themselves, and instead having them rely on some higher-level code to do that work for them.
The benefits of dependency injection are improved maintainability, simplicity through single-responsibility and is a significant enabler of unit testing, as mock dependencies can be injected when testing.
Frameworks like Ninject and other similar IOC containers generally provide a convenient way to manage dependencies that exist at an application-wide level, where a particular resolution is applied to every instance of a dependency.
On the other hand, your message example is dealing with a scenario-driven situation, where each resolution is dependent on some condition/s.
It is still useful to use dependency injection in scenario-driven situations, and will still give you all the benefits if dependency injection. But as you have stated, it would be necessary to use switch or if/else structures to provide the correct resolutions. These conditional structures should reside in some higher-level, controlling code that orchestrates the classes that have the dependencies
Upvotes: 3
Reputation: 32725
DI is about putting your software together and not solving your business logic. In your scenario you try to resolve DTO's from the IoC Container which is considered as bad practice.
This means you have to model your application in a different way. E.g. The following pseudo code will give you an idea for one way to handle this scenario:
public interface INotify
{
string Type { get; }
void Send(Message msg);
}
public class Message
{
public string Type { get; set; }
public string Text{ get; set; }
public string Name { get; set; }
public string Number { get; set; }
public string Email { get; set; }
}
public class MessageProcessor
{
public MessageProcessor(IEnumerable<INotify> notifiers, IMessageQueue)
{
this.notifiers = notifiers.ToDictionary(n => n.Type, n);
}
public void Process()
{
while (!TerminateApplication)
{
var msg = this.queue.GetNextMessage();
this.notifiers[msg.Type].Send(msg);
}
}
}
public void Main()
{
using (var kernel = new StandardKernel())
{
kernel.Bind<INotifier>().To<NotifyEmail>();
kernel.Bind<INotifier>().To<NotifySms>();
kernel.Bind<INotifier>().To<Notify>();
kernel.Bind<MessageProcessor>().ToSelf();
kernel.Get<MessageProcessor>().Process();
}
}
Upvotes: 4
Reputation: 8679
Instead of using Named Binding you could use Contextual Binding. Rewrite your configuration to automaticaly detect what exactly to inject depends on target of injection:
kernel.Bind<INotify>().To<NotifyEmail>();
kernel.Bind<INotify>().To<NotifySms>().WhenInjectedInto<SmsService>();
kernel.Bind<INotify>().To<NotifyAPNS>().WhenInjectedInto<IPhoneService>();
But this will partially solve your problem as far as I understand.
It seems to me that, you need someting like factory instead of dependency injection to process your message queue. For example:
var factory = new Dictionary<string, Func<Message>>
{
{ "unknow", () => new Message(new NotifyEmail()) },
{ "sms", () => new Message(new NotifySms()) },
{ "iphone", () => new Message(new NotifyAPNS()) }
};
factory["iphone"]().Send();
In case, if you have to construct complex implementations of INotify
you could have both benefits of dependency injection and abstract factory using ninject.extensions.factory
. In this case you could define new factory interface and define how it operates:
kernel.Bind<INotify>().To<NotifyEmail>()
.NamedLikeFactoryMethod<INotify, INotifocationFactory>(f => f.GetNotifyEmail());
kernel.Bind<INotify>().To<NotifySms>()
.NamedLikeFactoryMethod<INotify, INotifocationFactory>(f => f.GetNotifyEmail());
kernel.Bind<INotify>().To<NotifyAPNS>()
.NamedLikeFactoryMethod<INotify, INotifocationFactory>(f => f.GetNotifyEmail());
// receive INotifocationFactory using constructor injection,
// do not resolve it directly, because this will led you to ServiceLocator anti-pattern
var abstractFactory = kernel.Get<INotifocationFactory>();
var factory = new Dictionary<string, Func<Message>>
{
{ "unknow", () => new Message(abstractFactory.GetNotifyEmail()) },
{ "sms", () => new Message(abstractFactory.GetNotifyEmail()) },
{ "iphone", () => new Message(abstractFactory.GetNotifyAPNS()) }
};
factory["iphone"]().Send();
kernel.Bind<INotifocationFactory>().ToFactory();
Last sample is quite complex and could be that 25-dollar term for a 5-cent concept. So it's all down to you what solution to use in particular case
Upvotes: 2
Reputation: 25214
The main advantage of DI is maintenability. By letting DI inject your dependencies for you, as configured in a unified location in your code, it is easier to swap in and out different classes as your program evolves. Because these dependencies are usually based on interfaces, this enforces loose coupling. As you mentioned with the weapons, the idea is that your different components don't have to "know" about each other to function correctly.
The biggest advantage to DI is really when it comes to testing. Because DI usually means defining dependencies by interface and not by explicit classes, this makes it easy to stub these dependencies when you are trying to test a specific aspect of your program.
A good example of where this would be useful is if your program is accessing a webservice through a proxy class which may not be reasonable to call during the testing of your application. Instead of accessing the WS via MyProxy
, you could use DI to have it access the WS via IMyProxy
. You could then create a stub proxy which returns dummy values and allows you to test other components in your application without having direct access to the webservice itself.
All that said, in my personal experience DI is not a magic bullet for every scenario. DI adds a layer of complexity in exchange for a layer of abstraction. This can be a great benefit to the robustness of your application's architecture, but it can also be unnecessary. This article on SO What is dependency injection? has a bit of a discussion of the usefulness of DI. This answer https://stackoverflow.com/a/140655/1068266 I think sums up DI in a fairly reasoned light.
In short, I don't believe in implementing DI for DI's sake. Read this article http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html which is referenced in the post I mentioned above. It is the clearest and simplest definition of the subject that I have found. If you don't think that you can benefit from the pattern based on what is explained in that article, the DI will probably amount to an unnecessary layer of complexity in your application.
Upvotes: 4