最近,我投入了大量精力学习和理解DI。现在我越来越流利了,还有其他一些问题出现了。
是否应将列表成员注入域模型?
在Ninject的着名例子中,我认为它的想法是:
public class Warrior {
public Warrior(IWeapon weapon) { this.weapon = weapon; }
public void Attack() { weapon.Hit(); }
}
public Archer : Warrior {
// Here, the weapon would be bound to a bow.
public Archer(IWeapon weapon) : base(weapon) { }
}
public Ninja : Warrior {
// On the contrary, the weapon would here be bound to a katana.
public Ninja(IWeapon weapon) : base(weapon) { }
}
因此,根据注入IWeapon
的内容来定义应该创建什么武器的上下文绑定。
据我所知,在我的模型中注入DAO将实现Active Record设计模式,某些人认为这种设计模式是反模式的。其他人则更喜欢POCO作为域对象,简单的数据表示,而不遵循DDD的规则。
回到我的担忧,我会说Warrior
,Archer
和Ninja
都是我的域模型的一部分。
现在,如果我的模型有IList<T>
而不是IWeapon
怎么办? DI会有用吗,还是会变得无用?
public class MyClass {
public MyClass(IList<MyOtherClass> myOtherClasses) {
MyOtherClassesA = myOtherClasses.OfType<MyOtherClassA>().ToList();
MyOtherClassesB = myOtherClasses.OfType<MyOtherClassB>().ToList();
}
public IList<MyOtherClassA> MyOtherClassesA { get; protected set; }
public IList<MyOtherClassB> MyOtherClassesB { get; protected set; }
}
修改
不,请注射它们!但是不要注入一个基类型列表,然后按派生类型进行拆分。
让我们把自己置于Scrum和Sprint的背景下。
在Sprint中,开发团队可能有 Bugs , Impediments , Tasks 和 UserStories 。
所有这些都有标题和描述,以及特定于每种类型的一些其他属性。让我们创建一个名为 Artifact 的抽象类(不是ScrumArtifact)。
工件
public abstract class Artifact {
public string Description { get; set; }
public string Title { get; set; }
}
错误
public class Bug : Artifact {
public string Resolution { get; set; }
}
障碍
public class Impediment : Artifact {
}
任务
public class Task : Artifact {
public float EstimatedTime { get; set; }
public float RealTime { get; set; }
}
用户故事
public class UserStory : Artifact {
public string AcceptanceCriteria { get; set; }
public int BusinessValue { get; set; }
public int Complexity { get; set; }
public IList<Impediment> Impediments { get; protected set; }
public IList<Task> Tasks { get; protected set; }
}
在这里,我有UserStory
“依赖”两个列表:Impediments
和Tasks
。
所以我应该让UserStory
的构造函数采用如下两个列表。
public UserStory(IList<Impediment> impediments, IList<Task> tasks) {
Impediments = impediments;
Tasks = tasks;
}
我的单元测试:
[TestFixture]
public class UserStoryTests {
[Test]
public void ImpedimentsShouldBeInitializedByDefault() {
userStory.Impediments.Should().NotBeNull().And.BeOfType<IList<Impediment>>();
}
public void TasksShouldBeInitializedByDefault() {
userStory.Tasks.Should().NotBeNull().And.BeOfType<IList<Task>>();
}
[TestFixtureSetUp]
public void UserStorySetUp() {
impediments = new Mock<IList<Impediment>>();
tasks = new Mock<IList<Task>>();
userStory = new UserStory(impediments.Object, tasks.Object);
}
private Mock<IList<Impediment>> impediments;
private Mock<IList<Task>> tasks;
private UserStory userStory;
}
问题来自于Sprint
。 Sprint
需要四个列表,我发现为了清晰和可读性而注入的对象太多了。据说,如果我没弄错的话,一个依赖太多的类可能会破坏Single Responsibility Principle。虽然即使使用SRP
课程我也没有打破Sprint
,但我对注入四个不同的列表感到非常不舒服。我以为我可以使用多态来注入一个包含它们的列表,因为毕竟它们基本都是Artifact
s。
也许我应该只考虑使用AbstractFactory
模式,以便我的四个列表按预期正确初始化,我只需要注入一个单一的工厂类,其中一个责任包括创建列表? /强>
答案 0 :(得分:1)
我认为你误解了Ninject example一点点。它不涉及不同的warrior类,它们与IWeapon
的特定子类型相关联。只有Samurai
,可以使用任何类型的IWeapon
。因此,只能使用特定种类武器的Archer
不会被考虑在内并且不适合。而只需将Bow
注入Samurai
。
注入列表完全没问题。一些DI容器甚至允许自动装配列表。即您可以告诉容器将给定程序集中找到的接口的所有实现作为列表注入。
但是只有你能以同样的方式对待所有成员才能真正起作用,即你不必按子类别区分。如果你想将你的战士的近战武器与他的弓箭分开,最好注入两种不同类型的两个不同的名单。
如果您想了解有关正确DI模式的更多信息,我非常推荐Dependency Injection in .NET by Mark Seemann。