Thanks for looking. I'm kind of new to Ninject and like it so far. I get the part where you bind one thing in debug mode and bind another in release mode. Those are global bindings where you have to declare that every Samurai will have a sword or a dagger, using Ninjects example code. It's not either/or, it's one or the other.
How do I do something where I can have one Samurai with a sword, and another with a dagger where they could even switch weapons if they like. Is there another way other than creating a bunch of kernels with different binding modules?
Here is the example code from Ninject. If you drop it in a console app it should run:
using System;
using Ninject;
namespace NinjectConsole
{
class Program
{
//here is where we have to choose which weapon ever samurai must use...
public class BindModule : Ninject.Modules.NinjectModule
{
public override void Load()
{
//Bind<IWeapon>().To<Sword>();
Bind<IWeapon>().To<Shuriken>();
}
}
class Shuriken : IWeapon
{
public void Hit(string target)
{
Console.WriteLine("Pierced {0}'s armor", target);
}
}
class Sword : IWeapon
{
public void Hit(string target)
{
Console.WriteLine("Chopped {0} clean in half", target);
}
}
interface IWeapon
{
void Hit(string target);
}
class Samurai
{
readonly IWeapon weapon;
[Inject]
public Samurai(IWeapon weapon)
{
if (weapon == null)
throw new ArgumentNullException("weapon");
this.weapon = weapon;
}
public void Attack(string target)
{
this.weapon.Hit(target);
}
}
static void Main(string[] args)
{
//here is where we bind...
Ninject.IKernel kernel = new StandardKernel(new BindModule());
var samurai = kernel.Get<Samurai>();
samurai.Attack("your enemy");
//here is I would like to do, but with DI and no local newing up...
var warrior1 = new Samurai(new Shuriken());
var warrior2 = new Samurai(new Sword());
warrior1.Attack("the evildoers");
warrior2.Attack("the evildoers");
Console.ReadKey();
}
}
}
EDIT
Thanks for the replays and suggestions.
I figured out how to get pretty much what I wanted. Okay, so here is what I did:
Create a Console project called NinjectConsole, install Ninject and you should be able to just drop this in and run it.
Here is the new code:
using System;
using System.Collections.Generic;
using System.Linq;
using Ninject;
namespace NinjectConsole
{
class Program
{
public class BindModule : Ninject.Modules.NinjectModule
{
// default bind to weakest weapon
public override void Load()
{
Bind<IWeapon>().To<Dagger>();
}
}
class Dagger : IWeapon
{
public int WeaponHitPoints { get { return 5; } }
public string Hit(string target)
{
return String.Format("Stab {0} to death", target);
}
}
class Shuriken : IWeapon
{
public int WeaponHitPoints { get { return 9; } }
public string Hit(string target)
{
return String.Format("Pierced {0}'s armor", target);
}
}
class Sword : IWeapon
{
public int WeaponHitPoints { get { return 11; } }
public string Hit(string target)
{
return string.Format("Chopped {0} clean in half", target);
}
}
interface IWeapon
{
int WeaponHitPoints { get; }
string Hit(string target);
}
private class Samurai
{
private IEnumerable<IWeapon> _allWeapons;
public Samurai(IWeapon[] allWeapons)
{
if (!allWeapons.Any())
throw new ArgumentException("Samurai");
_allWeapons = allWeapons;
}
public void AddWeapon(IWeapon weapon)
{ //TODO: check for nulls...
_allWeapons = _allWeapons.Concat(new[] { weapon });
}
public void DropWeapon(IWeapon weapon)
{ //TODO: check for nulls...
Console.WriteLine("A Samurai got rid of a " + weapon.WeaponName);
_allWeapons = _allWeapons.Where(x => x.WeaponName != weapon.WeaponName);
}
public void Attack(string target)
{
int points = 0;
try
{
points = _allWeapons.Max(x => x.WeaponHitPoints);
}
catch ()
{
Console.WriteLine("You just punched " + target + " on the nose!");
}
var attackWeapon = _allWeapons.FirstOrDefault(i => i.WeaponHitPoints == points);
//TODO: check for nulls...
Console.WriteLine(attackWeapon.Hit(target));
}
}
static void Main(string[] args)
{
Ninject.IKernel kernel = new StandardKernel(new BindModule());
var samurai1 = kernel.Get<Samurai>();
var samurai2 = kernel.Get<Samurai>();
Console.WriteLine("Samurai #1");
samurai1.Attack("your enemy");
samurai2.AddWeapon(new Shuriken());
Console.WriteLine("\nSamurai #2 selects best weapon for attack");
samurai2.Attack("your enemy");
Console.WriteLine("\nSamurai #1 gets new weapon!");
samurai1.AddWeapon(new Sword());
Console.WriteLine("Samurai #1 selects best weapon for attack");
samurai1.Attack("your enemy");
Console.ReadKey();
}
}
}
The two swords of the samurai, katana and wakizashi, are together known as daisho and are worn inserted in the belt with the edge facing upwards.
Naginata were originally used by the samurai class of feudal Japan, as well as by ashigaru (foot soldiers) and sōhei (warrior monks). The naginata is the iconic weapon of the onna-musha, a type of female warrior belonging to the Japanese nobility. Heian period or Kamakura period until present.
Samurai swords were slightly curved, and blades varied in length, but it became common for elite samurai to carry two swords - a long and a short one.
Long swords, known as katana, were primarily used by the samurai, but short swords, known as wakizashi, and knives, known as tanto, were used by almost everyone. The katana are famous world wide for their unique balance between deadly efficiency and beauty.
Generally speaking, you can't achieve this with an IOC container unless you specify some condition, which should be met to choose the right implementation (weapon). The container needs to know which one implementation to choose under current circumstances.
I suggest, you are looking for some sort of Contextual binding.
There are plenty of conditional binding methods in Ninject (see them all on the link above). I have chosen Named binding, as it is very straightforward for an example.
Named binding
Dependencies are resolved according to configured names.
kernel.Bind<Samurai>().ToSelf().Named("SwordMaster");
kernel.Bind<Samurai>().ToSelf().Named("ShurikenMaster");
kernel.Bind<IWeapon>().To<Sword>().WhenParentNamed("SwordMaster");
kernel.Bind<IWeapon>().To<Shuriken>().WhenParentNamed("ShurikenMaster");
warrior1 = kernel.Get<Samurai>("SwordMaster");
warrior2 = kernel.Get<Samurai>("ShurikenMaster");
Multi injection
If you want your Samurai
to be able to handle multiple weapons, you can declare multiple bindings for IWeapon
and those could be injected into Samurai
as a collection.
public Samurai(IEnumerable<IWeapon> weapons)
{
this.AllMyWeapons = weapons;
}
Unfortunately although the Ninject documentation makes it easy to understand the syntax of the container, it can also give the wrong impression of when you should be using an IoC container.
The idea is that your services are registered and resolved from the IoC container - but a Samurai
isn't really a service, it's a domain object. These should be constructed by (for example) a SamuraiFactory
(Now there's a scary thought...)
When resolving a service, it is expected that you will only need to inject your components once, during initialization. This is the only time when the IoC container is used - ideally you should call .Resolve() just once, in order to spin up your network of dependencies. When you have your composition root, you have your entry point to the program - from then on, you do not reference the IoC container and your program executes as normal.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With