代码之家  ›  专栏  ›  技术社区  ›  Tim Visher

在Java项目中使用什么策略来命名包?为什么?[关闭]

  •  86
  • Tim Visher  · 技术社区  · 15 年前

    作为介绍,我看到了两种主要的包命名策略。(很明显,我指的不是整个“domain.company.project”部分,我指的是下面的包约定。)总之,我看到的包命名约定如下:

    1. 功能性:根据软件包的功能架构而不是业务领域来命名软件包。 另一个术语可能是根据“layer”命名。因此,您将拥有一个*.ui包和一个*.domain包以及一个*.orm包。您的包是水平切片而不是垂直切片。

      这是 许多的 比逻辑命名更常见。事实上,我从未见过或听说过这样的项目。当然,这让我很犹豫(有点像是在想你已经想出了一个NP问题的解决方案),因为我不是很聪明,我认为每个人都有很好的理由这样做。另一方面,我不反对人们只是在房间里丢了大象 我从没听过真正的争论 对于 以这种方式命名包。这似乎是事实上的标准。

    2. 逻辑:根据业务域标识命名包 并将与垂直功能部分相关的每个类放入该包中。

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

      1. 我倾向于垂直而不是水平地接近系统。我想进去开发订单处理系统,而不是数据访问层。很明显,在系统开发过程中,我很有可能会接触到数据访问层,但关键是我并不是这么想的。当然,这意味着,当我收到变更单或者想要实现一些新特性时,最好不要为了找到所有相关类而在一堆包中到处寻找。相反,我只是查看X包,因为我所做的一切都与X有关。

      2. 从开发的角度来看,我认为让您的包记录您的业务领域而不是您的体系结构是一个重大的胜利。我觉得域几乎总是系统的一部分,这很难理解,因为系统的体系结构,特别是在这一点上,在实现上几乎变得平淡无奇。事实上,我可以使用这种命名约定来创建一个系统,并立即从包的命名中了解到它处理订单、客户、企业、产品等,这似乎非常方便。

      3. 这似乎可以让您更好地利用Java的访问修饰符。这允许您更清晰地将接口定义为子系统,而不是系统的层。因此,如果您有一个希望透明持久化的orders子系统,那么在理论上,您可以不必在dao层中为其持久化类创建公共接口,而只将dao类与它处理的类打包在一起,从而永远不让其他任何东西知道它是持久的。显然, 如果

      4. 我想我能看到的一个缺点是,它确实使剥离层变得有点困难。不只是删除或重命名一个包,然后使用其他技术将一个新的包放置在适当的位置,而是必须进入并更改所有包中的所有类。不过,我觉得这没什么大不了的。这可能是因为缺乏经验,但我不得不想象,与进入系统并在系统中编辑垂直要素切片的次数相比,换掉技术的次数相形见绌。

    所以我想这个问题会问你,你怎么命名你的包裹,为什么?请理解,我不一定认为我在这里碰到了金鹅什么的。我对这一切都很陌生,基本上都有学术经验。然而,我不能找出我的推理中的漏洞,所以我希望你们都能,这样我才能继续前进。

    15 回复  |  直到 9 年前
        1
  •  32
  •   eljenso    15 年前

    对于包设计,我首先按层划分,然后按其他一些功能划分。

    1. 层从最一般(底部)到最具体(顶部)堆叠
    2. 每一层都有一个公共接口(抽象)
    3. 一个层只能依赖于另一个层的公共接口(封装)
    4. 层只能依赖于更一般的层(自上而下的依赖关系)

    因此,对于web应用程序,您可以在应用程序层中包含以下层(从上到下):

    • 应用层:包含特定于应用程序的逻辑,有状态的
    • 服务层:按域、无状态分组功能
    • 集成层:提供对后端层(数据库、jms、电子邮件等)的访问

    • 每个包名的根是 <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。

    当使用垂直方法进行包设计时,即首先按功能划分,然后强制使用不同的 在同一个“功能夹克”中的功能。您可以为更具体的模块设计通用模块。但这违反了更一般的层不应该知道更具体的层的重要原则。例如,服务层不应该按照应用层的概念建模。

        2
  •  18
  •   Buu    8 年前

    package design principles . 简而言之,要一起重用和一起更改的类(出于相同的原因,例如依赖项更改或框架更改)应该放在同一个包中。IMO认为,在大多数应用程序中,功能分解比垂直/业务分解更有可能实现这些目标。

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

    显然,并非所有类型的软件都是相同的,在某些项目中,垂直分解可能是有意义的(就实现可重用性和可更改性的目标而言)。

        3
  •  5
  •   Peter Å tibraný    15 年前

    通常有两个级别的部门。从上面,有部署单位。它们被命名为“逻辑上的”(用你的话说,想想Eclipse的特性)。在部署单元中,有包的功能划分(想想Eclipse插件)。

    例如,功能是 com.feature ,它包括 com.feature.client , com.feature.core com.feature.ui 插件。在插件中,我对其他包的划分很小,尽管这也不罕见。

    更新: 顺便说一句,Juergen Hoeller在InfoQ上对代码组织进行了大量的讨论: http://www.infoq.com/presentations/code-organization-large-projects . Juergen是Spring的设计师之一,对这方面的知识非常了解。

        4
  •  4
  •   Ben Hardy    15 年前

    我研究过的大多数java项目都是先从功能上对java包进行切片,然后从逻辑上进行切片。

    通常,部件足够大,可以分解成单独的构建工件,可以将核心功能放在一个jar中,将api放在另一个jar中,将web前端内容放在warfile中,等等。

        5
  •  3
  •   erickson    15 年前

    软件包将作为一个单元进行编译和分发。在考虑包中属于哪些类时,关键条件之一是其依赖关系。这个类依赖于哪些其他包(包括第三方库)。一个组织良好的系统将在一个包中对具有类似依赖关系的类进行集群。这限制了一个库中更改的影响,因为只有少数定义良好的包将依赖于它。

    听起来您的逻辑、垂直系统可能倾向于在大多数包中“抹黑”依赖关系。也就是说,如果每个特性都打包为一个垂直切片,那么每个包都将依赖于您使用的每个第三方库。对库的任何更改都可能波及整个系统。

        6
  •  3
  •   mP.    15 年前

    我个人更喜欢在逻辑上对类进行分组,然后在其中包括每个功能参与的子包。

    包毕竟是关于将事物组合在一起的——作为相关类的思想是彼此紧密地联系在一起的。如果他们住在同一个包中,他们可以利用包私有来限制可见性。问题是,将所有视图和分散性的内容集中到一个包中可能会导致许多类混合到一个包中。下一个明智的做法是相应地创建视图、持久性、util子包和重构类。幸运的是,保护不足和包私有作用域不支持当前包和子包的概念,因为这有助于强制执行此类可见性规则。

    我现在看到了通过功能分离的价值,因为有什么价值可以将所有与视图相关的东西分组。此命名策略中的内容与视图中的某些类断开连接,而其他类则处于持久性等状态。

    我的逻辑包装结构示例

    为了说明的目的,让我们命名两个模块-我将使用名称模块作为一个概念,将类分组在包树的特定分支下。

    苹果.model 香蕉.model 香蕉店

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

    其他逻辑v功能优势

    向上越接近根,范围就越广,属于一个包的东西开始表现出对属于她的模块的东西越来越多的依赖。例如,如果要检查“香蕉”模块,那么大多数依赖项都将限制在该模块内。事实上,“banana”下的大多数helpers根本不会在这个包范围之外被引用。

    为什么是功能?

    一个人通过基于功能的分类来实现什么价值。在这种情况下,大多数类彼此独立,几乎不需要或根本不需要利用包私有方法或类。将它们重构成自己的子包并没有什么好处,但确实有助于减少混乱。

    开发人员对系统的更改

        7
  •  3
  •   Jim Birchall    7 年前

    两者 (建筑)和 (特写)包装方法有一席之地。许多示例应用程序(在教科书中找到的应用程序等)遵循将表示、业务服务、数据映射和其他架构层放置到单独包中的功能方法。在示例应用程序中,每个包通常只有几个或只有一个类。

    这种最初的方法是好的,因为一个虚构的例子通常有以下作用:1)从概念上描绘出所呈现框架的架构,2)这样做是出于单一的逻辑目的(例如从诊所添加/删除/更新/删除宠物)。问题是许多读者把它当作一个没有界限的标准。

    随着“业务”应用程序扩展到包含越来越多的功能,请遵循 接近成为一种负担。尽管我知道在哪里可以根据体系结构层(例如“web”或“ui”包下的web控制器等)查找类型,但是开发一个 功能开始需要在许多包之间来回跳转。这至少很麻烦,但比那更糟。

    如果我正在构建一个框架库,那么我的包将遵循一种功能/架构打包方法。我的API用户甚至可能会意识到,他们的import语句包含以体系结构命名的直观包。

    相反,在构建业务应用程序时,我将按功能打包。我可以将Widget、WidgetService和WidgetController放在同一个位置。” 小工具。 “包,然后利用默认可见性(并且具有较少的导入语句和包间依赖关系)。

    com.myorg.common.service网站。 “包裹。还有一个很好的机会是,我创建类的目的是可以跨功能重用,并最终得到如下包 com.myorg.common.ui.helpers。 “和” com.myorg.common.util。

        8
  •  2
  •   JeeBee    15 年前

    这取决于逻辑过程的粒度?

    如果它们是独立的,则通常在源代码管理中为它们创建一个新项目,而不是一个新包。

    目前我正在进行的项目正朝着逻辑拆分的方向发展,有一个jython方面的包,一个规则引擎的包,foo、bar、binglewozzle等的包。我正在考虑为该包中的每个模块提供特定于XML的解析器/编写器,而不是有一个XML包(我以前做过),尽管共享逻辑仍然存在一个核心XML包。然而,这样做的一个原因是,它可能是可扩展的(插件),因此每个插件还需要定义其XML(或数据库等)代码,因此集中处理可能会在以后带来问题。

    最后,这似乎是对特定项目来说最明智的做法。不过,我认为很容易按照典型的项目分层图进行打包。你最终会得到一个逻辑和功能性包装的混合体。

    或者我在抽搐。

        9
  •  2
  •   levand    15 年前

    对我来说,在垂直命名系统中维护和可视化要比在水平命名系统中容易得多。如果component1.display具有对component2.dataaccess的引用,则会比display.component1具有对dataaccess的引用发出更多警告。组件2。

        10
  •  2
  •   thSoft    14 年前

    我完全遵循并提出逻辑(“按功能”)组织!一个包应该尽可能地遵循“模块”的概念。功能性组织可以将模块分散到项目上,从而减少封装,并且容易更改实现细节。

    让我们以Eclipse插件为例:将所有视图或操作放在一个包中会很混乱。相反,特性的每个组件都应该转到特性的包中,或者如果有很多,则转到子包中(feature a.handlers、featureA.preferences等)

        11
  •  1
  •   Varkhan    15 年前

    我个人喜欢功能命名。原因很简单:它避免了代码重复或依赖性噩梦。

    让我详细说明一下。当使用外部jar文件及其自己的包树时,会发生什么情况?您可以有效地将(编译的)代码导入到您的项目中,并使用一个(功能上分离的)包树。同时使用这两个命名约定有意义吗?不,除非你藏起来了。如果你的项目足够小并且只有一个组件的话。但是如果您有几个逻辑单元,那么您可能不想重新实现数据文件加载模块。您希望在逻辑单元之间共享它,而不是在逻辑无关的单元之间具有人工依赖关系,也不必选择要将特定共享工具放入哪个单元。

    我想这就是为什么函数命名在达到或打算达到一定大小的项目中最常用的原因,并且在类命名约定中使用逻辑命名来跟踪特定角色(如果包中的每个类都有)。

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

    1. 如果在计划发生更改时,您不得不在旧类中寻找修改功能的方法,这是一个糟糕抽象的迹象:您应该构建提供定义良好的功能的类,这些功能可以用一句简短的话定义。只有少数顶级类应该将所有这些集合起来,以反映您的商业智能。这样,您将能够重用更多的代码、更容易维护、更清晰的文档和更少的依赖性问题。

    2. 这主要取决于你对项目的探索方式。当然,逻辑和功能视图是正交的。因此,如果使用一个命名约定,则需要将另一个应用于类名以保持某种顺序,或者在某种程度上从一个命名约定分叉到另一个命名约定。

    3. 处理 进入你们班的内部。逻辑关系并不意味着理解算法或并发约束。功能性的可能,尽管不是。我对公共和私有之外的访问修饰符感到厌烦,因为它们常常隐藏着缺乏适当的架构和类抽象。

    4. 在大型商业项目中,不断变化的技术比你想象的要频繁。例如,我已经更改了3次XML解析器、2次缓存技术和2次地理本地化软件。幸好我把所有的细节都藏在了一个专门的包裹里。。。

        12
  •  1
  •   user53682    14 年前

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

    然后出现的问题是,何时以及为什么引入包是有意义的。大概,答案会与项目开始时的答案不同。

    我想你的问题已经出现了,因为包就像一个类别,有时很难决定是一个还是另一个。有时,标记更适合用来传达类在许多上下文中都是可用的。

        13
  •  0
  •   Bill Michell    15 年前

    protected default 可见性,以及 public

    我不经常在其他地方使用这些受保护的或默认的方法-除了可能在类的单元测试中-但是当我这样做时,它是 总是 来自同一层的类

        14
  •  0
  •   quant_dev    15 年前

    这要看情况。在我的工作中,我们有时按功能(数据访问、分析)或资产类别(信贷、股票、利率)拆分包。只需选择最适合您的团队的结构。

        15
  •  -3
  •   Raghu Kasturi    11 年前

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