接口应该放在一个单独的包中吗?
我是一个工作在一个相当大的项目,有很多组件和依赖关系的团队的新手。 对于每个组件,都有一个interfaces
包,其中放置了该组件的公开接口。 这是一个很好的做法吗?
我惯常的做法是将接口和实现放在同一个包中。
放置接口和实现是常见的地方,似乎没有问题。
以Java API为例,大多数类都有包含在同一个包中的接口和实现。
以java.util
包为例:
它包含了诸如Set
, Map
, List
等接口,同时还具有HashSet
, HashMap
和ArrayList
。
此外,Javadoc被devise为在这些条件下运行良好,因为它在显示包的内容时将文档分隔到“ 接口和类”视图中。
只有接口封装可能实际上有点过分,除非有大量的接口。 但是为了这样做而将接口分离到它们自己的包中听起来像是不好的做法。
如果需要将接口的名称与实现区分开来,可以使用命名约定来使接口更容易识别:
-
用
I
前缀接口名称。 这个方法是在.NET框架中的接口。 可以很容易地判断IList
是一个列表的接口。 -
使用 – 后缀。 这种方法经常出现在Java API中,比如
Comparable
,Iterable
和Serializable
等等。
对于任何语言,将它们放在同一个软件包中是没有问题的。 重要的是暴露在外面的东西,以及外面的样子。 没有人会知道或关心实施是否在同一个软件包中。
我们来看看这个特定的实例。
如果你把所有的公共事物放在一个包里,而另一个包里的私有东西没有公开的话,那么图书馆的客户就会看到一个包。 如果您将私密的东西移动到包含公开内容的包中,但是不要将它们暴露在包中,那么客户端就会看到完全相同的东西。
因此,这有一个没有充分理由的规则的气味:它做出的决定是基于某些公开可见的东西,而这个决定对公开可见的东西没有任何影响。
也就是说,如果在任何特定的情况下,将界面和实现分离到单独的包中似乎是一个好主意,那就直接去做吧。 想到这样做的原因是这个软件包很庞大,或者你有一个替代的实现你可能想要链接而不是标准的。
在许多框架中,比如OSGi,你几乎不得不这样做。 我认为这促进了松散耦合,在包而不是jar级别。
将接口放在不同包中的一个理由是,创build可以分发给您的产品或服务的消费者的“api”jar更容易。 完全有可能通过接口和实现一起完成此操作,但如果它们位于不同的包中,则脚本更简单。
是的,这是一个非常好的做法,因为它允许您发布界面而不公开您的特定实现。 也就是说,如果你不需要发布外部接口,把接口定义放在与实现相同的包中是没有问题的。
我们在这里工作(例如:将接口放在一个包中,在另一个包中实现),我们从中获得的主要优点是可以轻松地在不同的实现之间进行切换。
我没有太多的Java经验,但是我喜欢在C#/。NET中这样做,因为它允许未来的扩展,具有实现接口的具体类的程序集可能不会一直分发到客户端,因为它们是由中间人工厂代理,或者在Web服务场景中通过线路进行代理。
“ 实用软件工程:案例研究方法 ”一书主张将接口放在独立的项目/软件包中。
本书讲述的PCMEF +架构有以下原则:
- 向下依赖原则(DDP)
- 向上通知原则(UNP)
- 邻居通信原理(NCP)
- 显式关联原则(EAP)
- 循环消除原理(CEP)
- 类命名原则(CNP)
- 熟人包装原则(APP)
原则#3和#7的描述解释了为什么这是一个好主意:
邻居通信原则要求一个包只能与邻居包直接通信。 这个原则确保了系统不会分解成不可压缩的相互通信对象networking。 为了执行这个原则,非相邻对象之间的消息传递使用委托(见第9.1.5.1节)。 在更复杂的情况下,可以使用熟人软件包(第9.1.8.2节)对接口进行分组,以协助远程软件包的协作。
熟人包原则是邻居通信原理的结果。 熟人包由对象在方法调用的参数中传递的接口组成,而不是具体的对象。 这些接口可以在任何PCMEF包中实现。 这有效地允许非相邻包之间的通信,同时将依赖pipe理集中到单个相识包中。 第9.1.8.2节解释了对熟人包的需求,接下来在PCMEF环境中再次讨论。
请参阅此链接: http : //comp.mq.edu.au/books/pse/about_book/Ch9.pdf
我正在做一个项目,这对我来说很好,因为这些原因:
-
单独打包的接口和实现针对不同的用例,而不是针对各种types的映射或集合。 没有理由只为树(java.util.tree.Map,java.util.tree.Set)打包。 它只是一个标准的数据结构,所以把它和其他数据结构放在一起。 但是,如果你正在处理一个具有非常简单的debugging界面和一个漂亮的生产界面的益智游戏,作为其前端的一部分,你可能会有一个com.your.app.skin.debug和一个com.your.app .skin.pretty。 我不会把它们放在同一个包中,因为它们做了不同的事情,我知道我会采用一些SmurfNamingConvention(DebugAttackSurface,DebugDefenceSurface,PrettyAttackSurface等)为这两个创build一些非正式的命名空间,如果它们在同一个包中的话。
-
我find相关的接口和实现在单独的包中的问题的解决方法是采用我的包的命名configuration。 例如,我可以在com.your.app.skin.framework中拥有所有的接口,并且知道包树的同一级别上的其他包是实现。 缺点是这是一个非常规的惯例。 老实说,我会看到这个会议在6个月的时间里有多好:)
-
我不虔诚地使用这种技术。 有一些接口只在特定的实现中才有意义。 我不把它们粘在框架包中。 有一些软件包看起来不像我要创build40个不同的实现类,所以我不打扰。
-
我的应用程序使用guice,并有非常多的接口。
程序devise的问题通常涉及好处和缺点,没有一个适合所有答案的答案。 就像你为什么要用这个小巧的200线程序呢? 对于我来说,考虑到我的其他架构select,对于我的具体问题是有道理的。 🙂
把它们放在反映你的项目的软件包中。 将接口和实现放在一起,如果它们是同一个项目的一部分,这是很好的和常见的,但是如果你正在编写一个API,那么其他人可能会select与他们的项目相关的包名称。
一般来说,如果是同一个项目,我不认为将接口保存在一个单独的包中是没有好处的。 如果它变得混乱,可能还有其他包装命名问题,不pipe接口安排如何。
我更喜欢他们在同一个包。 感觉毫无意义的是创build一个专门用于接口的包。 对于那些只喜欢它们的接口前缀和后缀的团队(例如Java的“I”和“Impl”)来说,这尤其重要。
如果需要将一组接口作为公共API发布,那么将它们保存在一个完全独立的项目中并创build项目依赖关系更有意义。 但是,这一切都归结为我想的情况的偏好和方便。
我通常把接口与实施,但我可以看出为什么你可能想保持他们分开。 比方说,有人想根据你的接口来重新实现类,他们需要一个jar / lib / etc的实现,而不仅仅是接口。 与他们分开,你可以只说“这是我的接口的实现”,并完成它。 就像我说的,不是我所做的,但我可以看出为什么有些人可能想要。
我认为重要的是要注意,对于OSGi框架来说,更好的是它们位于不同的包中,以便您可以在隐藏实现包的同时轻松导出整个包。
把接口放在一个单独的软件包中与执行有很多很好的理由。 例如,您可能希望使用支持dependency injection的容器在运行时连接必要的实现,在这种情况下,只有接口需要在构build时存在,并且可以在运行时提供实现。 或者,您可能希望在运行时提供多个实现(例如使用模拟实现进行testing)或特定实现的多个版本(例如A / Btesting等)。 对于这些用例,将接口和实现分开打包更为方便。