分离接口和构造函数注入
Separating interfaces and Constructor injection
现在我有一种进退两难的心理。有一个类似于下面的class结构:
public interface IMammal
{
void Eat();
}
public interface IBarking
{
void Bark();
}
有 IBarking
和 IMammal
的实例。从理论上讲,我们的动物可以是其中之一,也可以只是其中之一。 Cow
就是大家看到的IMammal
,而Dog
就是IMammal
和IBarking
。从理论上讲,我们甚至可以拥有会吠叫但不是哺乳动物的人。
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
。这个设计有什么问题,如何解决?
这样简化的例子几乎没有任何意义。你错过了 "problem case" 以及你想做什么。显示实现 IBarking
但不实现 IMammal
的 class 以及将其传递给 Farm
时出现的问题。无论如何,给定前提:
- 接口
IMammal
存在。
- 接口
IBarking
存在。
- A class 实施
IBarking
没有 有 实施 IMammal
.
- Class 构造函数
Farm
必须接受 IBarking
和 IMammal
.
- 当前 class 构造函数接受
IMammal
.
在这种情况下,您要么需要一个新的构造函数、一个新的私有成员和更多代码以在两者之间进行选择,要么需要一个重叠的接口。我会选择后者:IFarmable
.
那么你需要:
public interface IMammal : IFarmable
public interface IBarking : IFarmable
public Farm(IFarmable farmable) { ... }
您很可能还有其他限制,例如 "but I want to call Eat()
on the variable passed into the constructor",但是您的描述 ("we assume that IBarking
is always IMammal
")不正确或不完整,您需要将 Eat()
移动到 IFarmable
界面。
我会尝试解读你的意图。
- 你想要一个农场,让动物长大(以后被宰杀作为食物)。
- 您可能需要另一只动物来保护它们(自从您试过施法后可能需要)。
更好的设计是:
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();
}
}
您的设计更改:
- 我说清楚了crystal可以有守卫动物
- 守卫动物是可选的(因为它是通过 属性 而不是构造函数分配的)。
我之所以要做出这种区分,是因为大多数动物都是被动的(你喂养和收获它们),而守卫动物有特定的用例,因此不应隐藏在其他动物中。
如果你想喂狗,你应该让那个接口继承IMammal
(除非你在哺乳动物中引入更多功能,在这种情况下你应该提取IFeedable
或类似的)。
现在我有一种进退两难的心理。有一个类似于下面的class结构:
public interface IMammal
{
void Eat();
}
public interface IBarking
{
void Bark();
}
有 IBarking
和 IMammal
的实例。从理论上讲,我们的动物可以是其中之一,也可以只是其中之一。 Cow
就是大家看到的IMammal
,而Dog
就是IMammal
和IBarking
。从理论上讲,我们甚至可以拥有会吠叫但不是哺乳动物的人。
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
。这个设计有什么问题,如何解决?
这样简化的例子几乎没有任何意义。你错过了 "problem case" 以及你想做什么。显示实现 IBarking
但不实现 IMammal
的 class 以及将其传递给 Farm
时出现的问题。无论如何,给定前提:
- 接口
IMammal
存在。 - 接口
IBarking
存在。 - A class 实施
IBarking
没有 有 实施IMammal
. - Class 构造函数
Farm
必须接受IBarking
和IMammal
. - 当前 class 构造函数接受
IMammal
.
在这种情况下,您要么需要一个新的构造函数、一个新的私有成员和更多代码以在两者之间进行选择,要么需要一个重叠的接口。我会选择后者:IFarmable
.
那么你需要:
public interface IMammal : IFarmable
public interface IBarking : IFarmable
public Farm(IFarmable farmable) { ... }
您很可能还有其他限制,例如 "but I want to call Eat()
on the variable passed into the constructor",但是您的描述 ("we assume that IBarking
is always IMammal
")不正确或不完整,您需要将 Eat()
移动到 IFarmable
界面。
我会尝试解读你的意图。
- 你想要一个农场,让动物长大(以后被宰杀作为食物)。
- 您可能需要另一只动物来保护它们(自从您试过施法后可能需要)。
更好的设计是:
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();
}
}
您的设计更改:
- 我说清楚了crystal可以有守卫动物
- 守卫动物是可选的(因为它是通过 属性 而不是构造函数分配的)。
我之所以要做出这种区分,是因为大多数动物都是被动的(你喂养和收获它们),而守卫动物有特定的用例,因此不应隐藏在其他动物中。
如果你想喂狗,你应该让那个接口继承IMammal
(除非你在哺乳动物中引入更多功能,在这种情况下你应该提取IFeedable
或类似的)。