Dependency Injection Wireup Question
If there are 3 interfaces like the following
public interface IWeapon {
void Kill();
}
public interface ISword:IWeapon {
void Slice();
}
public interface IShuriken: IWeapon {
void Pierce();
}
public class Ninja {
public IWeapon Weapon {get;set;}
public void BrutalKill() {
/* warrior must pierce, pierce, pierce and then kill
*/
}
public void HonorKill {
/* warrior must kill by one slice */
}
}
For a scenario like this how would you wireup the container and what would your method body look like for BrutalKill and HonorKill ?
EDIT: Based开发者_StackOverflow on comments, I was thinking on the lines a ninja should be armed with a weapon... if it wants to be armed with a sword or shuriken...should be decided later... not sure if i am thinking right .. maybe we need to subclass Ninja as NinjaWithShuriken and NinjaWithSword
Not sure if this is what you are looking for, but I would have this:
// first some implementations
public class Sword : ISword {
public void Kill() { // imp }
public void Slice() { // imp }
}
public class Shuriken : IShuriken {
public void Kill() { // imp }
public void Pierce() { // imp }
}
// and I would change the Ninja class to
public class Ninja {
public ISword Sword { get; private set; }
public IShuriken Shuriken { get; private set; }
public Ninja(ISword sword, IShuriken shuriken) {
this.Sword = sword;
this.Shuriken = shuriken;
}
public void BrutalKill() {
Shuriken.Pierce();
Shuriken.Pierce();
Shuriken.Pierce();
// either weapon can kill
// so lets close the distance and use the sword
Sword.Kill();
}
public void HonorKill {
Sword.Slice();
}
}
// creating the class
// where Ioc.Resolve is specific to the container implementation
var ninja = new Ninja(IoC.Resolve<ISword>(), IoC.Resolve<IShuriken>());
Update I like Phil Sandler's comment so a quick update to reflect that:
// a ninja interface
public interface INinja {
void BrutalKill();
void HonorKill();
}
// and then update the ninja class to
public Ninja : INinja {
...
}
// and have the ninja class created like this with the container
// resolving the dependencies:
var ninja = IoC.Resolve<INinja>();
Update Based on the update to the original question I would say:
public interface IWeapon {
void Attack();
void Kill();
}
public class Sword : ISword {
public void Attack() {
// implement as a slash
}
...
}
public class Shuriken : IShuriken {
public void Attack() {
// implement as a pierce
}
...
}
The idea being that we don't really care how Sword and Shuriken implement Attack, as long as the ninja can use them to perform his duty when called upon. The assassination can be caried out how the specific ninject wishes, as long as the job gets done within the confines of the stated agreement, in this case by Attacking.
// create the specific ninja
var swordNinja = new Ninja(IoC.Resolve<ISword>());
var shurikenNinja = new Ninja(IoC.Resolve<IShuriken>());
// with the ninja class updated to only have an IWeapon
// property that gets set in the constructor.
If your ninja is able to BrutalKill and HonorableKill, he absolutely must have a ISword and a IShuriken. Ninja is dependent on these, so we declare them in the ctor:
public class Ninja
{
readonly IShuriken shuriken;
readonly ISword sword;
public Ninja(IShuriken sh, ISword sw)
{
shuriken = sh;
sword = sw;
}
public void BrutalKill()
{
shuriken.Pierce();
shuriken.Pierce();
shuriken.Pierce();
sword.Slice();
shuriken.Kill();
}
public void HonorKill()
{
sword.Slice();
sword.Kill();
}
}
Here's our weapons:
public interface IWeapon
{
void Kill();
}
public interface IShuriken : IWeapon
{
void Pierce();
}
public interface ISword : IWeapon
{
void Slice();
}
Let's get a couple implementations of these dependencies:
using System;
public class BronzeShuriken : IShuriken
{
public void Pierce()
{
Console.WriteLine("Bronze shuriken pierce time now!");
}
public void Kill()
{
Console.WriteLine("Bronze shuriken kill!!!");
}
}
public class RustySword : ISword
{
public void Slice()
{
Console.WriteLine("Rusty sword slice time now!");
}
public void Kill()
{
Console.WriteLine("Rusty sword kill!!!");
}
}
Our configuration looks like this:
using Ninject.Modules;
class DefaultModule : NinjectModule
{
public override void Load()
{
Bind<IShuriken>().To<BronzeShuriken>();
Bind<ISword>().To<RustySword>();
}
}
And our entry point looks like this:
static void Main()
{
using (var kernel = new StandardKernel())
{
kernel.Load(new DefaultModule());
kernel.Get<Ninja>().BrutalKill();
}
}
Kill()
, with no parameters, seems like you are commanding the ninja to commit suicide. I would define Ninja
to act on other ninjas:
public interface INinja
{
void KillBrutally(INinja otherNinja);
void KillHonorably(INinja otherNinja);
}
Then, raise the level of abstraction from weapon to kill move:
public interface IKillMove
{
void Kill(INinja ninja);
}
and have ninjas support the kill types:
public sealed class Ninja : INinja
{
private readonly IKillMove _brutalKillMove;
private readonly IKillMove _honorableKillMove;
public Ninja(IKillMove brutalKillMove, IKillMove honorableKillMove)
{
_brutalKillMove = brutalKillMove;
_honorableKillMove = honorableKillMove;
}
#region INinja
public void KillBrutally(INinja otherNinja)
{
_brutalKillMove.Kill(otherNinja);
}
public void KillHonorably(INinja otherNinja)
{
_honorableKillMove.Kill(otherNinja);
}
#endregion
}
Now we can worry about weapons:
public interface IWeapon
{
void Attack(INinja ninja);
void Finish(INinja ninja);
}
and kill moves:
public sealed class MoveInKillMove : IKillMove
{
private readonly IWeapon _shortRangeWeapon;
private readonly IWeapon _longRangeWeapon;
public MoveInKillMove(IWeapon shortRangeWeapon, IWeapon longRangeWeapon)
{
_shortRangeWeapon = shortRangeWeapon;
_longRangeWeapon = longRangeWeapon;
}
#region IKillMove
public void Kill(INinja ninja)
{
_longRangeWeapon.Attack(ninja);
_longRangeWeapon.Attack(ninja);
_longRangeWeapon.Attack(ninja);
_shortRangeWeapon.Finish(ninja);
}
#endregion
}
public sealed class FinishingMove : IKillMove
{
private readonly IWeapon _weapon;
public FinishingMove(IWeapon weapon)
{
_weapon = weapon;
}
#region IKillMove
public void Kill(INinja ninja)
{
_weapon.Finish(ninja);
}
#endregion
}
Here is a sample wiring (translate to your IoC container as necessary):
var sword = new Sword();
var ninja = new Ninja(
new MoveInKillMove(sword, new Shuriken()),
new FinishingMove(sword));
In unity:
Container.RegisterType<INinja,Ninja>();
Container.RegisterType<ISword,Sword>();
Container.RegisterType<IShuriken,Shuriken>();
Assuming that Ninja has both Sword and Shuriken since only Sword can slice and only Shuriken can pierce.
Also, Ninja has a constructor that accepts IShuriken and ISword as arguments.
And so to get Ninja,
var ninja= Container.Resolve<INinja>();
精彩评论