我了解控制反转背后的整个概念,但是努力弄清楚IoC容器的位置以及它可能有什么帮助。
这是一个例子。假设我们有以下接口...
public interface IWarrior
{
string Name { get; }
IWeapon Weapon { get; }
void EquipWeapon(IWeapon weapon);
void Attack(ITarget target);
void DoVictoryDance();
}
public interface IWeapon
{
int AttackPower { get; }
}
public interface ITarget
{
int Health { get; }
int ArmorValue { get; }
int ReceiveAttack(int damage);
}
IWarrior基类...
public abstract class BaseWarrior : IWarrior
{
private static readonly Random Random = new Random();
protected BaseWarrior(string name, IWeapon weapon)
{
Name = name;
Weapon = weapon;
}
public string Name { get; }
public IWeapon Weapon { get; private set; }
public virtual void Attack(ITarget target)
{
var attackValue = Random.Next(0, Weapon.AttackPower + 1);
var damageDone = target.ReceiveAttack(attackValue);
Console.WriteLine($"{Name} did {damageDone} to {target.GetType().Name}");
if (target.Health <= 0)
DoVictoryDance();
}
public virtual void EquipWeapon(IWeapon weapon)
{
Weapon = weapon;
Console.WriteLine($"{Name} equips {weapon.GetType().Name}");
}
public abstract void DoVictoryDance();
}
具体的战士类型...
public class Samurai : BaseWarrior
{
public Samurai(string name, IWeapon weapon) : base(name, weapon)
{
}
public override void DoVictoryDance()
{
Console.WriteLine($"{Name} dances on top of the corpses of his foes.");
}
}
public class ChuckNorris : BaseWarrior
{
public ChuckNorris() : base("Chuck Norris", null)
{
}
public override void Attack(ITarget target)
{
var targetName = target.GetType().Name;
Console.WriteLine($"Chuck Norris stares at {targetName}. {targetName} collapses on the floor, dead. {targetName} never saw Chuck Norris.");
DoVictoryDance();
}
public override void EquipWeapon(IWeapon weapon)
{
Console.WriteLine("Chuck Norris needs no weapons you fool!");
}
public override void DoVictoryDance()
{
Console.WriteLine("Chuck Norris doesn't dance. He stares at you until you do it for him.");
}
}
以下武器...
public class Sword : IWeapon
{
public int AttackPower => 10;
}
public class CrossBow : IWeapon
{
public int AttackPower => 15;
}
最后是一个基本敌人:
public class Bear : ITarget
{
public int Health { get; private set; } = 100;
public int ArmorValue => 2;
public int ReceiveAttack(int damage)
{
var damageTaken = damage - ArmorValue;
if (damageTaken > 0)
Health -= damageTaken;
return damageTaken;
}
}
我很清楚这里的控制权是什么。我的主班不必知道IWarrior
的具体类型,IWarrior
也不知道IWeapon
或ITarget
的具体类型。
所以这允许我做类似...
ITarget target = new Bear();
IWarrior warrior = new ChuckNorris();
warrior.Attack(target);
或...
ITarget target = new Bear()
IWarrior warrior = new Samurai("Ben", new Sword());
warrior.Attack(target);
我可以看到拥有一个只存储我的对象的容器会变得更容易,这样我就不会“丢失它们的踪迹”(例如,不想让很多剑或武器飞来飞去,可能是单身人士。
我所见过的IoC容器示例显示了类似的内容:
IocContainer container = new IocContainer();
container.Bind<IWarrior>().To<Samurai>();
container.Bind<IWeapon>().To<Sword>();
container.Bind<ITarget>().To<Bear>();
然后做类似...
ITarget target = container.Get<ITarget>();
IWarrior warrior = container.Get<IWarrior>();
warrior.Attack(target);
但是现在我基本上是说我的IWarrior
总是Samurai
,我的IWeapon
总是Sword
,而我的ITarget
是始终是Bear
。
那不是我真正想要的!我希望能够使用不同的IWarrior
组合创建不同的IWeapon
类型并攻击大量ITargets
!
也许我误解了有关IoC容器如何工作的一些基本知识,但是我看过一些有关它们的视频(使用多个库),当我想要“某些”实现接口XX时,它们似乎都在说 ,请在您的容器中找到具体类型为YY的对象。如果您还没有,请创建一个,保存并发送回给我。
有人可以解释一下IoC容器的优点,给出它如何工作以及如何保留选择的真实示例(例如,不像上面那样将IWarrior
限制为Samurai
)吗?