当(当前)只有一个实现它的类时,您是不是应该创建一个接口?
Posted
技术标签:
【中文标题】当(当前)只有一个实现它的类时,您是不是应该创建一个接口?【英文标题】:Should you create an interface when there (currently) is only going to be one class that implements it?当(当前)只有一个实现它的类时,您是否应该创建一个接口? 【发布时间】:2009-04-06 02:39:20 【问题描述】:如果可能有其他东西可以使用它,你应该总是创建一个接口,还是等到真正需要它然后重构以使用接口?
对接口进行编程通常似乎是合理的建议,但还有 YAGNI...
我想这可能取决于具体情况。现在我有一个对象代表一个可以包含食谱或其他文件夹的文件夹。我应该担心实现 IContainer 之类的东西,而不是直接使用 Folder 吗?以防将来我想要一个引用其他子食谱的食谱(比如一个苹果派食谱,它也是一个馅饼皮食谱的容器)
【问题讨论】:
【参考方案1】:这总是取决于情况。如果您知道将有另一个类使用该接口,那么是的,创建接口类以节省时间。但是,如果您不确定(而且大多数情况下您不确定),请等到您需要它。
现在这并不意味着忽略接口的可能性 - 考虑对象的公共方法等,着眼于稍后制作接口,但不要用你实际上不需要的任何东西来混淆你的代码库.
【讨论】:
YAGNI,你不会需要它 -> 你不会需要它 没错。每次我放入接口或任何“以防万一”时,它们都完全不需要。但您的里程可能会有所不同。【参考方案2】:总会有一个使用它的测试,对吧(你做单元测试,不是吗?)。这意味着总是有 N + 1 个类使用它,其中 N 是在应用程序中使用您的类的类的数量。
除了依赖注入之外,接口的另一个目的是关注点分离,以便您的类实际上可以实现多个接口。
请记住所有这些,但如果一开始没有实现,您始终可以通过重构稍后引入接口。
【讨论】:
如果类足够简单以至于单元测试不需要模拟对象(即,它足够简单,只使用真实对象)怎么办? 我从来没有说过任何关于模拟对象的事情,不是吗:-)?我只是说使用它的类的数量总是+ 1(测试)。而且我同意以后可以通过简单的重构来引入接口。 +1:依赖注入绝对是使用接口的驱动。【参考方案3】:一般来说,如果只有一个类要实现接口,即使您预计可能会为它创建一个类,您也不应该为创建接口而烦恼,因为在类真正实现之前可能不会出现实现问题在一个场景中测试,在这种情况下,过早创建的接口可能有太多的成员或可能缺少成员。
例如,.NET Framework Bas 类库团队承认在包含SyncRoot
属性时过早地设计了ICollection
。对于后来的通用ICollection<T>
,他们决定将其删除(http://blogs.msdn.com/bclteam/archive/2005/03/15/396399.aspx)。
如果您要创建一个实现相同接口的模拟对象,那么这将被视为证明创建该接口的第二个实现。不过,并非所有单元测试都需要模拟风格的界面。
【讨论】:
【参考方案4】:我会说这更多地取决于您将在多少地方使用该类,而不是取决于有多少类可能实现该接口。如果您只在一个或两个地方使用 Folder,那么我会说等到真正需要该接口后再实现并重构它。但是,如果文件夹要在 100 个不同的地方使用,那么您可以通过预先对界面进行编程来节省一些时间。
【讨论】:
【参考方案5】:将接口视为定义语义或概念的契约。这是一种通用方法,而不是真正特定于语言的方法。在 OO 的上下文中,如果您使用的是单一继承模型,那么在定义对象模型时优先选择接口而不是类是一个很好的案例,因为单一的超类路径非常宝贵,您希望将其保存为比定义在对象或方法上公开的属性更“实质性”的东西。
拥有 IContainer 语义(契约)是从文件夹中创建接口的一个相当糟糕的理由;最好让您的文件夹(如果它正在执行任何重要的逻辑)在您的语言的核心框架中“实现”(可能已经存在的)IContainer 或 ICollection 接口。
与往常一样,更好的选择完全取决于具体问题。如果您的食谱也是文件夹(?!),您可能正在考虑父子关系或组合关系 - 如果您的系统中有其他元素,可以(并且应该)使用接口表达的语义对使用这种语义组成的事物进行“操作”。
接口有一些开销(编程方面),而且,如果你发现自己只使用了一组 Woof 和 IWoof 类和接口,那么你就会知道你可能没有需要用接口来表达你的问题——简单的类就足够了。
作为一项规则,对于任何 I,您至少应该有几个具体的类(除了 IImpl 或 之外,还有更有意义的名称)。
希望对您有所帮助。
【讨论】:
这东西吃了我的尖括号:对于任何 Ixxx ... 名称,而不是 IxxxImpl 或 XXX。 SO 转换成角度的<
>
括号,就好像它是 html 样式一样。为避免这种情况,请在字符周围使用反引号,【参考方案6】:
很多人已经提出了非常合理的建议。 我想补充的一件事是,如果您希望避免对具体类的直接硬依赖,那么接口将通过提供松散耦合来提供帮助。 如果您正在创建基于插件的架构,那么接口绝对是您的最佳选择。此外,如果您计划并排或稍后编写单元测试,您可能会注意到调用文件夹类的代码必须携带一个具体的实现,以便调用代码可测试. 如果您的文件夹类的具体实现反过来又与数据库或服务通信,那么您也需要将其转移到您的测试中,这将很快变得笨拙。 只是我的 2 美分。
【讨论】:
以上是关于当(当前)只有一个实现它的类时,您是不是应该创建一个接口?的主要内容,如果未能解决你的问题,请参考以下文章