我是一个从事相当大型项目的团队的新手,拥有大量组件和依赖项。对于每个组件,都有一个interfaces
包,其中放置了该组件的公开接口。这是一个好习惯吗?
我通常的做法一直是接口和实现都在同一个包中。
答案 0 :(得分:86)
放置界面和实现是很常见的,并且似乎不是问题。
以Java API为例 - 大多数类都将接口及其实现包含在同一个包中。
以java.util
包为例:
它包含Set
,Map
,List
等接口,同时还包含HashSet
,HashMap
和{{1}等实现}。
此外,Javadocs设计为在这些条件下运行良好,因为它在显示包的内容时将文档分离为 Interfaces 和 Classes 视图。 / p>
除非有大量的接口,否则只有接口的包可能实际上有点过分。但是为了这样做而将接口分成他们自己的包听起来像是不好的做法。
如果需要将接口名称与实现区分开来,可以使用命名约定来使接口更容易识别:
使用ArrayList
作为接口名称的前缀。此方法与.NET框架中的接口一起使用。可以很容易地告诉I
是列表的接口。
使用 - IList
后缀。这种方法通常出现在Java API中,例如able
,Comparable
和{{ 1}}仅举几例。
答案 1 :(得分:17)
对于任何语言,将它们放在同一个包中都可以。重要的是暴露在外面的世界,以及它从外面看起来如何。没有人会知道或关心实施是否在同一个包中。
让我们来看看这个特定的例子。
如果您在一个软件包中包含所有公共内容,而另一个软件包中的私有内容未公开,则该库的客户端会看到一个软件包。如果您将私有内容移动到包含公开内容的包中,但不要将其从包中公开,则客户端会看到完全相同的内容。
因此,这有一个没有充分理由的规则的气味:它是根据公开可见的事情作出决定,而不会对公开可见的事物产生任何影响。
也就是说,如果在任何特定情况下将接口和实现拆分为单独的包似乎是一个好主意,请继续前进并执行此操作。我想到这样做的原因是包装很大,或者您有一个替代实施,而不是标准的。
答案 2 :(得分:9)
在许多框架中,例如OSGi,你几乎必须这样做。我认为这会促进更松散的耦合,而不是jar级别。
答案 3 :(得分:7)
将接口放在不同包中的一个理由是,创建可以分发给产品或服务的消费者的“api”罐子更容易。完全可以将接口和实现结合在一起,但如果它们位于不同的包中,则更容易编写脚本。
答案 4 :(得分:5)
是的,这是一个非常好的做法,因为它允许您发布界面而不发布您的特定实现。也就是说,如果您不需要发布外部接口,那么将接口定义放在与实现相同的包中就没有问题。
答案 5 :(得分:5)
这本书 Practical Software Engineering: A Case-Study Approach 主张将接口放在单独的项目/包中。
本书讨论的PCMEF +架构具有以下原则:
原则#3和#7的描述解释了为什么这是一个好主意:
邻居通信原则要求包只能 直接与其邻居包通信。这个原则确保了 系统不会崩溃到不可压缩的网络 相互通信的对象。要强制执行此原则,请传递消息 非相邻对象之间使用委托(第9.1.5.1节)。在 更复杂的场景,熟人包(第9.1.8.2节)都可以 用于对接口进行分组以协助参与的协作 遥远的包裹。
熟人包原则是邻居的结果 沟通原则。熟人包包括 对象传递的接口,而不是具体对象 方法调用的参数。接口可以任意实现 PCMEF包。这有效地允许之间的通信 非依赖包,同时将依赖管理集中到一个 单身熟人包。熟人包的需要是 在第9.1.8.2节中解释,并在PCMEF中再次讨论 上下文。
答案 6 :(得分:3)
我们在这里工作(即:将接口放在一个包中,将实现放在另一个包中),我们从中获得的主要优势是我们可以轻松地在实现之间进行交换。
答案 7 :(得分:2)
我没有太多的Java经验,但我喜欢这样做是C#/ .NET中的一个好习惯,因为它允许将来扩展,其中具有实现接口的具体类的程序集可能不会一直分发直到客户端,因为它们由中间人工厂代理或在Web服务方案中通过线路代理。
答案 8 :(得分:2)
我在一个项目上这样做,因为这些原因它对我很有用:
单独打包的接口和实现用于不同的用例,而不是用于各种类型的Map或Set。没有理由为树提供一个包(java.util.tree.Map,java.util.tree.Set)。它只是一个标准的数据结构,因此将其与其他数据结构放在一起。但是,如果你正在处理一个具有非常简单的调试界面和漂亮的生产界面的益智游戏,作为其前端的一部分,你可能有一个com.your.app.skin.debug和一个com.your.app .skin.pretty。我不会把它们放在同一个包中,因为它们做了不同的事情,我知道如果它们在同一个包中,我会使用一些SmurfNamingConvention(DebugAttackSurface,DebugDefenceSurface,PrettyAttackSurface等)为这两个创建一些非正式的命名空间。
我在单独的包中找到相关接口和实现的问题的解决方法是为我的包采用命名配置。例如。我可以在com.your.app.skin.framework中拥有所有接口,并且知道包树的同一级别上的其他包是实现。缺点是这是一个非传统的惯例。老实说,我会在6个月的时间内看到这个惯例有多好:)
我不虔诚地使用这种技术。存在仅在特定实现中有意义的接口。我不把它们粘在框架包中。有一些软件包看起来不像我要创建40个不同的实现类,所以我不打扰。
我的应用程序使用guice并且有很多接口。
程序设计的问题通常涉及利弊,并且没有一个适合所有人的答案。就像为什么你会在一个小小的200行程序中使用这种技术?鉴于我的其他建筑选择,对我来说,对我的具体问题来说,这是有道理的。 :)
答案 9 :(得分:1)
我通常将接口与实现放在一起但是我可以理解为什么你可能想要将它们分开。比方说,有人想根据你的接口重新实现类,他们需要一个jar / lib / etc与你的实现而不仅仅是接口。将它们分开,您可以只说“这是我对该界面的实现”并完成它。就像我说的那样,不是我做的,但我可以理解为什么有些人想要这样做。
答案 10 :(得分:0)
将它们放在反映您项目的包中。如果将接口和实现放在同一个项目中,那么将接口和实现放在一起是很好的和通用的,但如果您正在编写API,那么其他人可能会选择与其项目相关的包名称。
一般来说,如果它是针对同一个项目的,那么我认为将接口保持在单独的包中不会有任何好处。如果它变得混乱,可能存在其他包命名问题,无论接口安排如何。
答案 11 :(得分:0)
我喜欢他们在同一个包中。创建专门用于接口的包是没有意义的。对于那些只喜欢它们的接口前缀和后缀(例如Java的“I”和“Impl”)的团队来说,这尤其多余。
如果需要将一组接口发布为公共API,那么将它们保存在完全独立的项目中并创建项目依赖项更有意义。但这一切都归结为我想的情况的偏好和便利。
答案 12 :(得分:0)
我认为重要的是要注意,对于OSGi框架,它们在不同的包中更好,这样您就可以在隐藏实现包的同时轻松导出整个包。
答案 13 :(得分:-1)
将接口放在与实现不同的包中有很多充分的理由。例如,您可能希望使用支持Dependency Injection的容器在运行时连接必要的实现,在这种情况下,只需要在构建时存在接口,并且可以在运行时提供实现。或者,您可能希望在运行时提供多个实现(例如,使用mock实现进行测试)或特定实现的多个版本(例如,用于A / B测试等)。对于这些用例,单独打包接口和实现更方便。