假设我有一个带有Speak()方法的Animal类。通常,Dog和Cat将是Animal的子类,并提供自己的实现:
public class Animal {
public abstract void Speak();
}
public class Dog : Animal {
public override void Speak() {
Console.WriteLine("Woof!");
}
}
public class Cat : Animal {
public override void Speak()
Console.WriteLine("Meow!");
}
}
Animal dog = new Dog();
Animal cat = new Cat();
第二种方式是注入不同的行为:
public class Animal {
private readonly String noise;
public Animal(String noise) {
this.noise = noise;
}
public abstract void Speak() {
Console.WriteLine(noise);
}
}
Animal dog = new Animal("Woof!");
Animal cat = new Animal("Meow!");
我用第二种方式看到的优点是它的DRYer,而且课程更少,而且更灵活。
我看到的缺点是代码不太清晰,可能更容易出错。变量名必须提供一些关于类具有什么实现的线索。此外,如果你有一个需要同时注射猫和狗的类,开发人员可以正确地确保一个新动物(" woof!")和新动物("喵!")按照班级要求的正确顺序传递,这似乎很脆弱:
public class House {
public House(Animal cat, Animal dog) {
...
}
public void letCatOutside() {
cat.Speak();
}
public void letDogOutside() {
dog.Speak();
}
}
问题是我似乎编码越来越像第二个例子,我将这些差异分解出来,然后将它们注入一个公共类。我担心自己会养成一个坏习惯。我呢?我希望一些经验丰富的开发人员可以了解情况。
答案 0 :(得分:1)
你显然想要第一个选择,因为狗是一种与猫不同的动物,而且他们的行为方式也不同(说话)。他们也有特定的行为。
有些情况下使用Animal就足够了,这取决于应用程序的要求,但是类或DRY的数量绝不是建模原则。您将定义尽可能多的类,因为它对业务有意义。 DRY仅仅意味着“不要在多个地方放置相同的行为”,但它在which layer you're working中很重要,如果重新使用行为会使您的代码紧密耦合。
我说要担心业务对象对业务概念和用例的建模有多好,然后关于正确的封装,然后是解耦。不过,很多事情都伴随着经验。我会说将DRY放在一边,直到你面前的代码尖叫“我正在其他地方实施确切的行为,请使用它”
哦,这不是DI的情况。您正在“注入”行为使用的一些数据,而不是行为提供者。而且House应该永远不会有这些方法,因为房子不能决定事物,它是一个没有生命的对象。另一方面,人类或PetOwner可以决定让动物出去。
答案 1 :(得分:0)
将Animal
,Dog
,Cat
重命名为Foo
,Bar
,Qux
,答案显而易见。
类用于捕获常见行为,如果Foo
可以识别string
的常见行为,那么您不一定需要围绕该string
的类层次结构。如果您不仅需要说话,还需要移动,宠物,吃饭,睡觉和做其他事情,该怎么办?你会以ThreeLegSpidersThatBarelyEatButSometimesCanBePetted
的庞大等级结束吗?在这种情况下,一系列装饰器会有所帮助,这使它更像第二种方法。
我想说的是,你的问题没有简单的答案,而这完全取决于更广泛的背景。有时候你会从一种方法开始,最后以另一种方式结束,只是因为......(填写理由)。