5

ご覧いただきありがとうございます。私はNinjectの初心者で、今のところ気に入っています。あるものをデバッグ モードでバインドし、別のものをリリース モードでバインドする部分を取得します。これらは、Ninjects サンプル コードを使用して、すべてのサムライが剣または短剣を持っていることを宣言する必要があるグローバル バインディングです。どちらでもない/または、どちらか一方です。

剣を持ったサムライと短剣を持ったサムライがいて、必要に応じて武器を切り替えることができるようにするにはどうすればよいでしょうか。異なるバインディング モジュールを使用して一連のカーネルを作成する以外に別の方法はありますか?

Ninject のサンプル コードを次に示します。コンソール アプリにドロップすると、次のように実行されます。

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();
        }
    }
}

編集

リプレイと提案をありがとう。

私は自分が欲しいものをほとんど手に入れる方法を見つけました。さて、これが私がやったことです:

  1. デフォルト/初期バインディングを最も弱い武器に設定します。new-b のようなものです。
  2. 別の武器を追加 (ダガー)
  3. IWeapon を拡張して、武器の値を評価する WeaponHitPoints 値を含めました。
  4. 武士が武器を獲得したり失ったりできるように、武器の追加と削除方法を含めるように武士を拡張しました。
  5. 攻撃方法を最適な武器を使用するように変更しました。
  6. 追加された機能を利用するためにプログラムを修正しました。
  7. TODO: try/catch と null チェックを追加...

NinjectConsole というコンソール プロジェクトを作成し、Ninject をインストールすると、これをドロップして実行できるはずです。

新しいコードは次のとおりです。

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();
        }
    }
}
4

2 に答える 2

4

一般的に、適切な実装(武器)を選択するために満たす必要のある条件を指定しない限り、IOCコンテナーでこれを実現することはできません。コンテナは、現在の状況でどの実装を選択するかを知る必要があります。

ある種のコンテキストバインディングを探していることをお勧めします。

Ninjectにはたくさんの条件付きバインディングメソッドがあります(上記のリンクでそれらすべてを参照してください)。例として非常に簡単なので、名前付きバインディングを選択しました。

名前付きバインディング

依存関係は、構成された名前に従って解決されます。

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");

マルチインジェクション

Samurai複数の武器を処理できるようにしたい場合は、の複数のバインディングを宣言して、それらをコレクションとしてIWeapon挿入することができます。Samurai

public Samurai(IEnumerable<IWeapon> weapons)
{
     this.AllMyWeapons = weapons;
}
于 2013-02-06T22:42:59.157 に答える
1

残念ながら、Ninject のドキュメントではコンテナーの構文を簡単に理解できますが、IoC コンテナーをいつ使用すべきかについて誤った印象を与える可能性もあります。

サービスは IoC コンテナーから登録および解決されるという考え方ですが、Samurai実際にはサービスではなく、ドメイン オブジェクトです。これらは (たとえば) a で構築する必要がありますSamuraiFactory(今、恐ろしい考えがあります...)

サービスを解決する場合、コンポーネントを注入する必要があるのは、初期化中に 1 回だけであると予想されます。これは、IoC コンテナーが使用される唯一の時間です。依存関係のネットワークをスピンアップするために、理想的には .Resolve() を 1 回だけ呼び出す必要があります。コンポジション ルートを取得すると、プログラムへのエントリ ポイントが作成されます。それ以降は、IoC コンテナーを参照せず、プログラムは通常どおり実行されます。

于 2013-02-07T18:01:25.820 に答える