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

我在一段时间以前想过这个问题,最近又重新铺陈了一遍,因为我的商店正在开发第一个真正的Java Web应用程序。

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

  1. function:根据业务领域的架构而不是他们的身份,根据他们的function命名你的包。 另一个术语可能是根据“图层”命名的。 所以,你会有一个* .ui包和一个* .domain包和一个* .orm包。 你的软件包是水平切片而不是垂直的。

    这比逻辑命名更普遍。 事实上,我不相信我曾经见过或听说过这样的项目。 这当然会使我感到不安(有点像以为你已经想出了NP问题的解决scheme),因为我不是非常聪明,我认为每个人都应该有这样的理由。 另一方面,我并不反对人们只是在房间里忽略了大象我从来没有听说过这样做包命名的实际理由。 这似乎是事实上的标准。

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

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

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

    2. 从开发的angular度来看,我认为这是一个主要的胜利,让你的软件包logging你的业务领域,而不是你的架构。 我觉得这个领域几乎总是系统的一部分,这个系统的架构,尤其是在这个系统的架构,在实现中几乎变得平淡无奇。 事实上,我可以用这种命名规则来到系统,并立即从命名包知道它处理订单,客户,企业,产品等,似乎相当方便。

    3. 看起来这样可以让你更好地利用Java的访问修饰符。 这使您可以更清楚地定义子系统界面而不是系统层。 所以,如果你有一个命令子系统,你希望是透明持久的,那么理论上你可以永远不要让别的东西知道它是持久的,因为不必在dao层创build它的持久化类的公共接口,而是把dao类打包成只有它处理的类。 显然, 如果你想公开这个function,你可以为它提供一个接口或者公开它。 看起来,通过将系统function的垂直切片分割到多个软件包中,您会失去很多。

    4. 我认为我可以看到的一个缺点是,它确实使分层更难一些。 不要只是删除或重新命名一个包,然后用一种替代技术删除一个新的包,你必须进入并更改所有包中的所有类。 但是,我不认为这是一个大问题。 这可能是由于缺乏经验,但我必须想象,与您在系统中进入和编辑垂直要素切片的次数相比,您交换技术的次数会减less。

所以我想这个问题将会出现在你身上,你怎么给你的包装起名字,为什么? 请理解,我不一定认为我在这里偶然发现了金鹅。 我对所有这些大部分学术经验都很陌生。 然而,我不能在我的推理中发现漏洞,所以我希望你们都能这样,我可以继续前进。

对于包装devise,我首先分层,然后通过其他function。

还有一些额外的规则:

  1. 层从最普通的(底部)堆叠到最具体的(顶部)
  2. 每一层都有一个公共接口(抽象)
  3. 一层只能依靠另一层的公共接口(封装)
  4. 一个图层只能依赖更一般的图层(从上到下依赖)
  5. 一层最好取决于它正下方的层

因此,例如,对于Web应用程序,您可以在应用程序层中具有以下层(从上到下):

  • 表示层:生成将在客户层中显示的UI
  • 应用程序层:包含特定于应用程序的逻辑,有状态
  • 服务层:按域分组function,无状态
  • 集成层:提供对后端层的访问(db,jms,email,…)

对于生成的包装布局,这些是一些额外的规则:

  • 每个包名称的根目录是<prefix.company>.<appname>.<layer>
  • 一个图层的界面被function进一步分割: <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 ... 

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

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

在分层模型中,几个不同的高层模块可以使用相同的低层模块。 例如:可以为同一个应用程序构build多个视图,多个应用程序可以使用同一个服务,多个服务可以使用同一个网关。 这里的诀窍是,当在层中移动时,function级别改变。 更具体的层次中的模块不会映射1-1更一般的层次模块,因为他们expression的function级别不映射1-1。

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

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

例如,一个水平的域对象片可以被不同types的前端甚至应用程序重用,而当底层的web框架需要改变时,web前端的水平片可能会一起改变。 另一方面,如果跨越不同function区域的类被分组在这些包中,则很容易想象在许多包中这些变化的连锁反应。

显然,并不是所有types的软件都是相同的,在某些项目中,纵向细分可能是有意义的(在实现可重用性和可变性的目标方面)。

目前通常有两种级别的分工。 从顶部,有部署单位。 这些被命名为“逻辑”(就你而言,思考Eclipsefunction)。 在部署单元中,你有function分区的包(想想Eclipse插件)。

例如,feature是com.feature ,它由com.feature.clientcom.feature.corecom.feature.ui插件组成。 在内部插件中,我对其他软件包的划分很less,尽pipe这也不奇怪。

更新:顺便说一下,在InfoQ上,Juergen Hoeller就代码组织进行了很多讨论: http : //www.infoq.com/presentations/code-organization-large-projects 。 Juergen是Spring的架构师之一,对这个东西非常了解。

我所做的大多数Java项目都是先从function上分割Java包,然后再进行逻辑分割。

通常零件足够大,它们被分解成单独的构build工件,在那里你可以把核心function放到一个jar中,另一个web前端的东西放到warfile中等等。

软件包将作为一个单元进行编译和分发。 当考虑一个包中包含哪些类时,其中一个关键标准是它的依赖关系。 还有什么其他的软件包(包括第三方库)依赖于这个类。 一个组织良好的系统将把具有类似依赖关系的类集中在一个包中。 这限制了一个库变化的影响,因为只有less数定义明确的软件包依赖于它。

这听起来像你的逻辑,垂直系统可能倾向于“涂抹”大多数包的依赖性。 也就是说,如果每个function都打包成垂直分片,则每个软件包将依赖于您使用的每个第三方库。 对图书馆的任何改变都可能影响整个系统。

我个人比较喜欢逻辑上的分组,其中包括每个function参与的分组。

包装的目标

套餐毕竟是关于把事物分组在一起的 – 相关的分类彼此靠近的想法。 如果他们住在同一个包裹里,他们可以利用私人包裹来限制能见度。 问题是把所有的视图和持久性的东西放到一个包里,可能会导致很多类被混合成一个包。 下一个明智的做法是相应地创buildview,persistance,util子包和重构类。 在欠缺保护和包私人范围界定不支持当前包和子包的概念,因为这将有助于执行这种可见性规则。

我现在看到通过function分离的价值,因为有什么价值将所有视图相关的东西分组。 这个命名策略中的某些事物与视图中的某些类断开连接,而另一些则处于持续状态等等。

我的逻辑包装结构的一个例子

为了说明的目的,让两个模块名称 – 使用名称模块作为一个概念,在一个pacckage树的特定分支下分组类。

apple.model apple.store banana.model banana.store

优点

使用Banana.store.BananaStore的客户端仅暴露于我们希望提供的function。 hibernate版本是一个实现细节,他们不需要知道,也不应该看到这些类,因为他们添加混乱的存储操作。

其他逻辑vfunction优势

随着范围越来越大,属于一个包的东西越来越依赖于属于它的东西。 例如,如果要检查“香蕉”模块,大多数依赖关系将被限制在该模块内。 事实上,在“香蕉”下的大多数帮手在这个包的范围之外根本不会被引用。

为什么function?

通过基于function的东西来实现什么价值。 在这种情况下,大多数类是相互独立的,很less或不需要利用包私有方法或类。 将它们重构成它们自己的子包,收益不大,但有助于减less混乱。

开发人员更改系统

当开发人员的任务是做一些微不足道的改变时,似乎很可能他们有变化,包括来自包树所有区域的文件。 使用逻辑结构化的方法,他们的变化在包树的相同部分更加本地化,​​这似乎是正确的。

包装的function (build筑)和逻辑 (特征)方法都有一个地方。 许多示例应用程序(在教科书等中find的应用程序)遵循将演示文稿,业务服务,数据映射和其他架构层放入单独的包中的function方法。 在示例应用程序中,每个包通常只有less数或只有一个类。

这个初始的方法是好的,因为一个人为的例子经常用于:1)在概念上映射出正在呈现的框架的体系结构,2)以一个单一的逻辑目的(例如,从诊所增加/移除/更新/删除宠物) 。 问题在于,很多读者把它当作一个没有界限的标准。

随着“商业”应用扩展到包含越来越多的function,遵循function方法成为一种负担。 尽pipe我知道在哪里寻找基于体系结构层的types(例如,在“web”或“ui”包下的web控制器等),但是开发单个逻辑特性开始需要在多个包之间来回切换。 这至less是麻烦的,但是比这更糟糕。

由于逻辑上相关的types没有打包在一起,因此API过度公开; 逻辑上相关的types之间的交互被强制为“公共”,以便types可以相互导入和交互(最小化为默认/包可见性的能力丧失)。

如果我正在构build一个框架库,那么我的包将遵循一个function/build筑包装方法。 我的API消费者甚至可能会意识到他们的导入语句包含以架构命名的直观软件包。

相反,在构build业务应用程序时,我将按function进行打包。 将Widget,WidgetService和WidgetController全部放在同一个“ com.myorg.widget ”包中,然后利用默认可见性(以及较less的导入语句以及包间依赖关系),我没有任何问题。

但是,有交叉的情况。 如果我的WidgetService被许多逻辑域(function)使用,我可能会创build一个“ com.myorg.common.service。 ”包。 还有一个很好的机会,我创build了有意在各个function之间重复使用的类,最终得到了诸如“ com.myorg.common.ui.helpers ”和“ com.myorg.common.util。 ”之类的包。 我甚至可能最终将所有这些后来的“普通”类移到单独的项目中,并将其作为myorg-commons.jar依赖项包含在我的业务应用程序中。

这取决于你的逻辑进程的粒度?

如果他们是独立的,你通常在源代码控制中有一个新的项目,而不是一个新的包。

我目前正在进行的项目是错误的逻辑分裂,有一个jython方面的包,一个规则引擎的包,foo,bar,binglewozzle等包。我正在寻找具体的XMLparsing器/编写器,而不是拥有一个XML包(我之前已经做过),尽pipe在共享逻辑的基础上仍然会有一个核心的XML包。 其中一个原因是它可能是可扩展的(插件),因此每个插件也需要定义它的XML(或数据库等)代码,所以集中这个可能会在后面引入问题。

最后,似乎是这个特定项目似乎最明智的做法。 然而,我认为按照典型的项目分层图进行打包是很容易的。 最终你会得到一个逻辑和function包装的混合。

需要标记名称空间。 某些Jythonfunction的XMLparsing器可以标记为Jython和XML,而不必select其中一个。

或者,也许我在w </s>。

我会亲自去function命名。 简短的理由:它避免了代码重复或依赖性的噩梦。

让我详细说一下。 当你使用一个外部的jar文件时,会发生什么? 您正在有效地将(编译的)代码导入到您的项目中,并使用它(function上分离的)包树。 在同一时间使用两个命名约定是否有意义? 不,除非你隐藏起来。 而且,如果你的项目足够小,并有一个单一的组成部分。 但是,如果你有几个逻辑单元,你可能不想重新实现,比方说,数据文件加载模块。 你希望在逻辑单元之间共享它,而不是在逻辑上不相关的单元之间有人造的依赖关系,而不必select你要把那个特定的共享工具放入哪个单元。

我想这就是为什么函数命名是达到或达到一定规模的项目中最常用的命名,并且在类命名约定中使用逻辑命名来跟踪特定angular色,如果每个类中的任何一个包。

我将试着更准确地回应你关于逻辑命名的每一点。

  1. 如果你必须在旧课程中去钓鱼来修改function,那么这是一个糟糕的抽象标志:你应该build立一些能够提供明确定义的function的类,可以用一个短句来定义。 只有less数几个顶级的课程应该把所有这些集合起来,以反映您的商业智能。 这样,您将能够重复使用更多的代码,更轻松的维护,更清晰的文档和更less的依赖性问题。

  2. 这主要取决于你的方式你的项目。 当然,逻辑和function视图是正交的。 所以,如果你使用一个命名约定,你需要将另一个命名约定应用于类名,以便保持一定的顺序,或者从一个命名约定到另一个命名约定。

  3. 访问修饰符是允许其他理解你的处理的类访问你的类的内部的好方法。 逻辑关系并不意味着理解algorithm或并发约束。 function可能,但它不。 我非常厌倦公共和私有访问修饰符,因为他们经常隐藏缺乏适当的架构和类抽象。

  4. 在大型商业项目中,不断变化的技术发生得比你想象的要多。 比如,我已经换了3次XMLparsing器,2次caching技术,2次地理定位软件。 好东西,我已经把所有的细节都放在一个专门的包里。

我尝试devise包结构,如果我要绘制一个依赖关系图,很容易遵循和使用一致的模式,尽可能less的循环引用。

对我而言,在垂直命名系统中维护和可视化要容易得多,而不是水平。 如果component1.display具有对component2.dataaccess的引用,则会比display.component1引用数据访问时抛出更多的警告响铃。 COMPONENT2。

当然,两者共享的组件都在自己的包中。

我完全遵循并提出合乎逻辑的(“按function”)组织! 一揽子计划应尽可能地遵循“模块”的概念。 function性组织可能在一个项目上传播一个模块,导致较less的封装,并且容易改变实现细节。

以Eclipse插件为例:将所有视图或操作放在一个包中会是一团糟。 相反,function的每个组件都应该转到function的包中,或者如果有很多的话,则转移到子包中(featureA.handlers,featureA.preferences等)

当然,问题出在分层包系统(其中包括Java),这使得处理正交问题变得不可能,或者至less非常困难 – 尽pipe它们到处都是!

从纯粹的实践angular度来看,Java的可见性构造允许同一个包中的类访问具有protecteddefault可见性的方法和属性,以及public方法。 从完全不同的代码层使用非公开的方法肯定会是一个很大的代码味道。 所以我倾向于将来自同一图层的类放入同一个包中。

我不经常在别处使用这些保护或默认的方法 – 除了可能在类的unit testing中 – 但是当我这样做的时候,它总是来自同一层的一个类

这取决于。 在我的工作中,我们有时会按function(数据访问,分析)或资产类别(信贷,股票,利率)来分组。 只需select最适合您团队的结构。

根本不使用包是一个有趣的实验(除了根包)。

那么出现的问题是什么时候以及为什么介绍软件包是有意义的。 据推测,答案将不同于你在项目开始时会回答的问题。

我认为你的问题根本就出现了,因为软件包就像分类,有时很难决定其中的一个。 有时候,标签会更好地传达一个类在许多情况下都是可用的。

从我的经验来看,可重用性带来的问题比解决问题更多。 有了最新的廉价处理器和内存,我宁愿重复代码,而不是紧密集成以重用。