English 中文(简体)
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 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>();




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签