分离接口和构造函数注入

时间:2015-08-11 15:16:29

标签: c# oop dependency-injection architecture code-design

现在我有一种两难的境地。有一个类结构类似于以下类:

public interface IMammal
{
    void Eat();
}

public interface IBarking
{
    void Bark();
}

IBarkingIMammal的情况。从理论上讲,我们的动物可以是其中之一,也可以只是其中之一。您可以看到Cow IMammalDogIMammalIBarking。从理论上讲,我们甚至可以有人可以吠叫,但不是哺乳动物。

public class Mammal : IMammal
{
    public void Eat()
    {
        Console.Write("Om-nom-nom");
    }
}

public class Cow : Mammal
{
}

public class Dog : Mammal, IBarking
{
    public void Bark()
    {
        Console.Write("Bark-bark!!!");
    }
}

这是Farm,只有一只动物生活在其中:

public class Farm
{
    private readonly IMammal _animal;

    public Farm(IMammal animal)
    {
        _animal = animal;
    }

    public void Feed()
    {
        _animal.Eat();
    }

    public void Guard()
    {
        var dog = _animal as IBarking;
        if (dog != null)
            dog.Bark();
    }
}

我在这里看到的问题是,我们假设IBarking总是IMammal。这个设计有什么问题,如何解决?

2 个答案:

答案 0 :(得分:3)

这些简化的例子几乎没有任何意义。您错过了"问题案例"那你想做什么显示一个实现IBarking但不是IMammal的类,以及将其传递给Farm时出现的问题。无论如何,考虑到前提:

  • 接口IMammal存在。
  • 接口IBarking存在。
  • 实施IBarking的班级不 来实施IMammal
  • 类构造函数Farm必须接受IBarking IMammal
  • 当前类构造函数接受IMammal

在这种情况下,您需要一个新的构造函数,一个新的私有成员和更多代码来在两者之间进行选择,或者需要重叠的接口。我跟随后者:IFarmable

然后你需要:

  • public interface IMammal : IFarmable
  • public interface IBarking : IFarmable
  • public Farm(IFarmable farmable) { ... }

很可能你有其他约束,比如"但是我想在传递给构造函数" 的变量上调用Eat(),但是然后你的描述( >"我们认为IBarking总是IMammal" )不正确或不完整,您需要将Eat()移至IFarmable接口。

答案 1 :(得分:1)

我会尝试解释您的 意图

  1. 你想拥有一个动物长大的农场(后来被屠宰作为食物)。
  2. 你可能需要另一只守护它们的动物(可能是因为你试过了演员)。
  3. 更好的设计是:

    public class Farm
    {
        private readonly IMammal[] _animals;
    
        public Farm(IMammal[] animals)
        {
            _animals = animals;
        }
    
        public void Feed()
        {
           foreach (var animal in _animals)
                animal.Eat();
        }
    
        public IBarking GuardingAnimal { get; set; }
    
        public void Guard()
        {
            if (GuardingAnimal != null)
                GuardingAnimal .Bark();
        }
    }
    

    您设计的变化:

    1. 我已经清楚地知道可以有一只守卫动物
    2. 守护动物是可选的(因为它通过属性而不是构造函数分配)。
    3. 我想要做出这种区分的原因是大多数动物都是被动的(你喂它们并且收获它们),而保护动物有一个特定的用例,因此不应该隐藏在其他动物中。

      如果你想喂狗,你应该让那个界面继承IMammal(除非你在哺乳动物中引入更多功能,在这种情况下你应该提取IFeedable或类似)。