依赖注入与行为差异的子类化

时间:2014-05-05 21:37:12

标签: design-patterns dependency-injection dry

假设我有一个带有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();
    }
}

问题是我似乎编码越来越像第二个例子,我将这些差异分解出来,然后将它们注入一个公共类。我担心自己会养成一个坏习惯。我呢?我希望一些经验丰富的开发人员可以了解情况。

2 个答案:

答案 0 :(得分:1)

你显然想要第一个选择,因为狗是一种与猫不同的动物,而且他们的行为方式也不同(说话)。他们也有特定的行为。

有些情况下使用Animal就足够了,这取决于应用程序的要求,但是类或DRY的数量绝不是建模原则。您将定义尽可能多的类,因为它对业务有意义。 DRY仅仅意味着“不要在多个地方放置相同的行为”,但它在which layer you're working中很重要,如果重新使用行为会使您的代码紧密耦合。

我说要担心业务对象对业务概念和用例的建模有多好,然后关于正确的封装,然后是解耦。不过,很多事情都伴随着经验。我会说将DRY放在一边,直到你面前的代码尖叫“我正在其他地方实施确切的行为,请使用它”

哦,这不是DI的情况。您正在“注入”行为使用的一些数据,而不是行为提供者。而且House应该永远不会有这些方法,因为房子不能决定事物,它是一个没有生命的对象。另一方面,人类或PetOwner可以决定让动物出去。

答案 1 :(得分:0)

AnimalDogCat重命名为FooBarQux,答案显而易见。

类用于捕获常见行为,如果Foo可以识别string的常见行为,那么您不一定需要围绕该string的类层次结构。如果您不仅需要说话,还需要移动,宠物,吃饭,睡觉和做其他事情,该怎么办?你会以ThreeLegSpidersThatBarelyEatButSometimesCanBePetted的庞大等级结束吗?在这种情况下,一系列装饰器会有所帮助,这使它更像第二种方法。

我想说的是,你的问题没有简单的答案,而这完全取决于更广泛的背景。有时候你会从一种方法开始,最后以另一种方式结束,只是因为......(填写理由)。