我正在阅读R. Martin和M. Martin在C#中的敏捷原则,模式和实践,他们在书中建议将所有界面保存在一个单独的项目中,例如。 接口
例如,如果我有一个包含所有自定义Gui类的 Gui 项目,我会将其接口保留在 Interfaces 项目中。具体来说,我在 Gui 中有一个CustomButton类,我会在 Interfaces 中保留ICustomButton接口。
优点是,任何需要ICustomButton的类都不需要引用 Gui 本身,而只需要更轻量级的 Interfaces 项目。
此外,如果 Gui 项目中的某个类发生更改并因此导致重建,则只有直接引用CustomButton的项目才需要重新编译,而引用ICustomButton的项目可能保持不变
我理解这个概念,但看到一个问题:
假设我有这个界面:
public interface ICustomButton
{
void Animate(AnimatorStrategy strategy);
}
正如您所看到的,它指的是AnimatorStrategy,它是一个具体的类,因此会坐在不同的项目中,让我们称之为动画。 现在接口项目需要引用 Animation 。另一方面,如果 Animation 使用 Interfaces 中定义的接口,则需要引用它。
循环依赖 - “我们来了”。
我看到,这个问题的唯一解决方案是,接口中定义的所有方法都接受本身作为接口的输入。试图实现这一点,很可能会产生多米诺骨牌效应,并且很快就需要实现一个接口,即使对于最基本的类也是如此。
我不知道我是否愿意在开发过程中处理这种开销。
有什么建议吗?
答案 0 :(得分:16)
要小心永远,永远,永远 - 特别是接近所有,没有,或每一个。
你总是将所有接口放在一个单独的程序集中吗?不 - 不一定。
您是否应该实现您希望代码的外部使用者实现的接口 - 可能。如果您希望项目中的多个程序集依赖它们,我会将接口放入外部程序集中 - 这有助于破坏耦合依赖性。我也使用这种做法来解决循环引用问题,其中程序集需要知道彼此之间的接口。
我不会将仅在项目内部使用的接口放在单独的程序集中。当我的项目相对较小时,我也不会将接口提升到自己的程序集中 - 或者如果没有依赖于它们的程序集,则不打算使用接口。
至于您提供的示例 - 我建议您考虑不从接口引用系统中的类。只要有可能,我尝试让接口只引用其他接口 - 这使事情相对分离。你不能总是实现这一点 - 所以当你有这些类型的接口时 - 你必须将它们与它们所依赖的组件保持一致。
如果您决定将接口放入单独的程序集中 - 您不一定要将它们全部放入单个程序集中。您可能希望根据其预期用途将其分解 - 这样,消费者可以仅仅引入与特定功能域相关的接口。
答案 1 :(得分:3)
简单的答案是AnimatorStrategy也应该是一个界面。 AnimatorStrategy的实现在Animation项目中,但接口将在AnimationInterfaces或Interfaces(根据需要)项目中。
我不一定有单个接口项目,但每个功能分组都有一个。动画的客户端将引用AnimationInterfaces,Gui的客户端将引用GuiInterfaces。
这样就可以保留合同,而不会混淆任何实现。
对于它的价值,我更倾向于使用命名约定。将接口放在Animation中,将实现放在AnimationImpl(或类似的)中。这样你就引用了功能名称。
答案 2 :(得分:1)
我认为即使你将所有东西都声明为接口,循环依赖也可能是不可避免的,因为程序集Foo中可能有一个方法接受参数IBar,而程序集Bar中的方法接受参数IFoo。
我会说没有通用的配方,但你应该使用“常识”来根据需要划分组件中的接口。
答案 3 :(得分:-2)
使用采用泛型类型的方法定义接口怎么样? E.g。
public interface IThing
{
int DoMyThing<T>(T variable);
}
如果您需要T variable
的某些限制,可以执行以下操作:
int DoMyThing<T>(T variable) where T : IOtherInterface;
您的Interfaces项目中还定义了IOtherInterface
。然后,只要您的特定类继承自IThing
和IOtherInterface
,您就可以使用DoMyThing
方法并传入特定类的实例,而不具有循环依赖关系。您的代码可能会变为:
public interface ICustomButton
{
void Animate<T>(T strategy) where T : IAnimateable;
}
接口没有引用具体类AnimatorStrategy
。