您在Java项目中使用什么策略进行软件包命名?为什么?

我在一段时间以前想过这个问题,当我的商店正在开发其第一个真正的Java Web应用程序时,它最近又重新浮出水面。

作为介绍,我看到两个主要的命名策略。 (说清楚,我并不是指整个'domain.company.project'部分,我正在谈论下面的包规范。)无论如何,我看到的包命名约定如下:

  • 功能:根据业务领域的结构而不是其身份,根据其功能命名包。 另一个术语可能是根据'图层'来命名。 所以,你需要一个* .ui包和一个* .domain包以及一个* .orm包。 你的包是水平片而不是垂直片。

    这比逻辑命名更普遍。 事实上,我不相信我曾经见过或听说过这样的项目。 这当然会让我感到不安(有点像以为你提出了一个NP问题的解决方案),因为我不是非常聪明,我认为每个人都必须有很好的理由按照他们的方式去做。 另一方面,我并不反对人们忽略了房间里的大象,我从来没有听说过以这种方式进行软件包命名的实际说法。 它似乎是事实上的标准。

  • 逻辑:根据其业务域标识命名包 ,并将与该垂直切片功能相关的每个类放入该包中。

    正如我之前提到的,我从来没有见过或听说过这样的事情,但这对我来说意义重大。

  • 我倾向于垂直而不是水平地接近系统。 我想进入并开发订单处理系统,而不是数据访问层。 显然,在系统的开发中,我很有可能触及数据访问层,但重要的是我并不这么想。 当然,这意味着当我收到更改订单或想要实现一些新功能时,最好不必为了找到所有相关类而在一堆包中钓鱼。 相反,我只是看X包,因为我在做什么与X.

  • 从开发的角度来看,我认为这是让您的软件包记录您的业务领域而不是您的架构的主要胜利。 我觉得这个领域几乎总是系统的一部分,因为这个系统的体系结构,特别是在这一点上,在实现中几乎变得平淡无奇。 事实上,我可以通过这种命名惯例来到系统,并立即从软件包的命名知道它处理订单,客户,企业,产品等,这似乎相当方便。

  • 看起来这样可以让你更好地利用Java的访问修饰符。 这使您可以更清楚地将接口定义为子系统而不是系统的各个层。 因此,如果你有一个需要透明持久化的订单子系统,理论上你可以永远不要让别的东西知道它是持久的,因为它不必为dao层中的持久化类创建公共接口,而是将dao类打包成只有它处理的类。 很明显,如果你想公开这个功能,你可以为它提供一个接口或者公开它。 它看起来像是通过将系统功能的垂直切片分割到多个软件包中而使您失去了很多。

  • 我认为我可以看到的一个缺点是,它确实会让图层变得更加困难。 除了删除或重命名包,然后用替代技术删除新包之外,您必须进入并更改所有包中的所有类。 但是,我不认为这是一个大问题。 这可能来自缺乏经验,但我必须想象,与您在系统中使用和编辑垂直要素切片的次数相比,换出技术的次数会减少。

  • 所以我想这个问题会发生在你身上,你如何命名你的软件包,为什么? 请理解,我不一定认为我偶然发现了金鹅或某物。 我对所有这些大部分学术经验都很陌生。 然而,我无法发现我的推理中的漏洞,所以我希望你们都可以这样我可以继续前进。


    对于包装设计,我首先是分层,然后是其他功能。

    还有一些额外的规则:

  • 层从最普通(底部)到最具体(顶部)
  • 每一层都有一个公共接口(抽象)
  • 一层只能依赖另一层的公共接口(封装)
  • 一个图层只能依赖更一般的图层(从上到下依赖)
  • 层优选取决于其正下方的层
  • 因此,例如,对于Web应用程序,您可以在应用程序层中具有以下图层(从上到下):

  • 表示层:生成将在客户层中显示的UI
  • 应用层:包含特定于应用程序的逻辑,有状态的
  • 服务层:按域分组功能,无状态
  • 集成层:提供对后端层(db,jms,email,...)的访问
  • 对于最终的软件包布局,这些是一些额外的规则:

  • 每个包名称的根目录是<prefix.company>.<appname>.<layer>
  • 层的接口被功能进一步分割: <root>.<logic>
  • 图层的私有实现以private: <root>.private作为前缀
  • 这是一个示例布局。

    表示层被视图技术分开,并且可选地由(应用)组分开。

    com.company.appname.presentation.internal
    com.company.appname.presentation.springmvc.product
    com.company.appname.presentation.servlet
    ...
    

    应用程序层分为用例。

    com.company.appname.application.lookupproduct
    com.company.appname.application.internal.lookupproduct
    com.company.appname.application.editclient
    com.company.appname.application.internal.editclient
    ...
    

    服务层被划分为业务域,受后端层中域逻辑的影响。

    com.company.appname.service.clientservice
    com.company.appname.service.internal.jmsclientservice
    com.company.appname.service.internal.xmlclientservice
    com.company.appname.service.productservice
    ...
    

    整合层分为'技术'和访问对象。

    com.company.appname.integration.jmsgateway
    com.company.appname.integration.internal.mqjmsgateway
    com.company.appname.integration.productdao
    com.company.appname.integration.internal.dbproductdao
    com.company.appname.integration.internal.mockproductdao
    ...
    

    像这样分离软件包的优点是管理复杂性更容易,并且增加了可测试性和可重用性。 虽然看起来像是一个很大的开销,但根据我的经验,它实际上是非常自然的,每个在这个结构(或类似的)上工作的人都可以在几天内完成它。

    为什么我认为垂直方法不太好?

    在分层模型中,几个不同的高级模块可以使用相同的低级模块。 例如:您可以为同一个应用程序构建多个视图,多个应用程序可以使用相同的服务,多个服务可以使用同一个网关。 这里的诀窍是,当在层中移动时,功能级别会发生变化。 更具体的图层中的模块不会映射来自更一般层的模块1-1,因为它们表达的功能级别不映射1-1。

    当您使用垂直方式进行包装设计时,即先按功能划分,然后再将所有具有不同功能级别的构建块强制到同一个“功能外套”中。 您可以为更具体的模块设计您的通用模块。 但是这违反了更一般的层不应该了解更多特定层的重要原则。 例如服务层不应该在应用层的概念之后建模。


    我发现自己坚持着Bob的包装设计原则。 简而言之,应该将同时重用并且一起更改的类(出于同样的原因,例如依赖性更改或框架更改)放在同一个包中。 国际海事组织认为,在大多数应用中,功能分解比实施垂直/商业分解更有可能实现这些目标。

    例如,不同类型的前端甚至应用程序都可以重用域对象的水平片段,并且当底层Web框架需要更改时,Web前端的水平片段可能会一起改变。 另一方面,如果跨不同功能区域的类被分组到这些程序包中,则可以很容易地想象出这些更改在多个程序包中的连锁反应。

    很明显,并非所有类型的软件都是相同的,并且在某些项目中,纵向细分可能是有意义的(在实现可重用性和可接近性的目标方面)。


    目前通常有两种级别的分工。 从顶部开始,有部署单元。 这些被命名为“逻辑”(就您的角度而言,请考虑Eclipse功能)。 在部署单元内部,您可以对软件包进行功能划分(可以使用Eclipse插件)。

    例如,feature是com.feature ,它由com.feature.clientcom.feature.corecom.feature.ui插件组成。 在内部插件中,我对其他软件包的划分很少,尽管这并不罕见。

    更新:顺便说一下,Juergen Hoeller在InfoQ有关代码组织的很好的演讲:http://www.infoq.com/presentations/code-organization-large-projects。 Juergen是Spring的建筑师之一,对这个东西非常了解。

    链接地址: http://www.djcxy.com/p/84173.html

    上一篇: What strategy do you use for package naming in Java projects and why?

    下一篇: Can you find all classes in a package using reflection?