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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/533102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 15:55:05  来源:igfitidea点击:

What strategy do you use for package naming in Java projects and why?

javanaming-conventionspackages

提问by Tim Visher

I thought about this awhile ago and it recently resurfaced as my shop is doing its first real Java web app.

不久前我曾考虑过这个问题,最近它重新出现,因为我的商店正在开发第一个真正的 Java Web 应用程序。

As an intro, I see two main package naming strategies. (To be clear, I'm not referring to the whole 'domain.company.project' part of this, I'm talking about the package convention beneath that.) Anyway, the package naming conventions that I see are as follows:

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

  1. Functional: Naming your packages according to their function architecturally rather than their identity according to the business domain.Another term for this might be naming according to 'layer'. So, you'd have a *.ui package and a *.domain package and a *.orm package. Your packages are horizontal slices rather than vertical.

    This is muchmore common than logical naming. In fact, I don't believe I've ever seen or heard of a project that does this. This of course makes me leery (sort of like thinking that you've come up with a solution to an NP problem) as I'm not terribly smart and I assume everyone must have great reasons for doing it the way they do. On the other hand, I'm not opposed to people just missing the elephant in the room andI've never heard a an actual argument fordoing package naming this way. It just seems to be the de facto standard.

  2. Logical: Naming your packages according to their business domain identityand putting every class that has to do with that vertical slice of functionality into that package.

    I have never seen or heard of this, as I mentioned before, but it makes a ton of sense to me.

    1. I tend to approach systems vertically rather than horizontally. I want to go in and develop the Order Processing system, not the data access layer. Obviously, there's a good chance that I'll touch the data access layer in the development of that system, but the point is that I don't think of it that way. What this means, of course, is that when I receive a change order or want to implement some new feature, it'd be nice to not have to go fishing around in a bunch of packages in order to find all the related classes. Instead, I just look in the X package because what I'm doing has to do with X.

    2. From a development standpoint, I see it as a major win to have your packages document your business domain rather than your architecture. I feel like the domain is almost always the part of the system that's harder to grok where as the system's architecture, especially at this point, is almost becoming mundane in its implementation. The fact that I can come to a system with this type of naming convention and instantly from the naming of the packages know that it deals with orders, customers, enterprises, products, etc. seems pretty darn handy.

    3. It seems like this would allow you to take much better advantage of Java's access modifiers. This allows you to much more cleanly define interfaces into subsystems rather than into layers of the system. So if you have an orders subsystem that you want to be transparently persistent, you could in theory just never let anything else know that it's persistent by not having to create public interfaces to its persistence classes in the dao layer and instead packaging the dao class in with only the classes it deals with. Obviously, ifyou wanted to expose this functionality, you could provide an interface for it or make it public. It just seems like you lose a lot of this by having a vertical slice of your system's features split across multiple packages.

    4. I suppose one disadvantage that I can see is that it does make ripping out layers a little bit more difficult. Instead of just deleting or renaming a package and then dropping a new one in place with an alternate technology, you have to go in and change all of the classes in all of the packages. However, I don't see this is a big deal. It may be from a lack of experience, but I have to imagine that the amount of times you swap out technologies pales in comparison to the amount of times you go in and edit vertical feature slices within your system.

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

    这是比逻辑命名更常见。事实上,我相信我从未见过或听说过这样做的项目。这当然让我怀疑(有点像认为你已经想出了一个 NP 问题的解决方案),因为我不是很聪明,而且我认为每个人都必须有很好的理由按照他们的方式去做。在另一方面,我不反对人们只是缺少在房间里的大象我从来没有听说过的实际参数做包命名这种方式。它似乎只是事实上的标准。

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

    正如我之前提到的,我从未见过或听说过这个,但这对我来说很有意义。

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

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

    3. 这似乎可以让您更好地利用 Java 的访问修饰符。这使您可以更清晰地将接口定义到子系统而不是系统层中。因此,如果您有一个希望透明持久化的订单子系统,那么理论上您可以永远不要让其他任何东西知道它是持久的,因为不必在 dao 层中为其持久化类创建公共接口,而是将 dao 类打包在只有它处理的类。显然,如果你想公开这个功能,你可以为它提供一个接口或公开它。通过将系统功能的垂直切片拆分到多个包中,您似乎失去了很多。

    4. 我想我可以看到的一个缺点是它确实使剥离图层变得更加困难。不是简单地删除或重命名一个包,然后使用替代技术放置一个新的包,您必须进入并更改所有包中的所有类。但是,我不认为这有什么大不了的。这可能是由于缺乏经验,但我不得不想象,与您在系统中进入并编辑垂直特征切片的次数相比,您更换技术的次数相形见绌。

So I guess the question then would go out to you, how do you name your packages and why? Please understand that I don't necessarily think that I've stumbled onto the golden goose or something here. I'm pretty new to all this with mostly academic experience. However, I can't spot the holes in my reasoning so I'm hoping you all can so that I can move on.

所以我想你会问这个问题,你如何命名你的包,为什么?请理解,我不一定认为我在这里偶然发现了金鹅或其他东西。我对这一切都很陌生,主要是学术经验。但是,我无法在我的推理中发现漏洞,所以我希望你们都能这样我可以继续前进。

回答by JeeBee

It depends on the granularity of your logical processes?

这取决于您的逻辑流程的粒度?

If they're standalone, you often have a new project for them in source control, rather than a new package.

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

The project I'm on at the moment is erring towards logical splitting, there's a package for the jython aspect, a package for a rule engine, packages for foo, bar, binglewozzle, etc. I'm looking at having the XML specific parsers/writers for each module within that package, rather than having an XML package (which I have done previously), although there will still be a core XML package where shared logic goes. One reason for this however is that it may be extensible (plugins) and thus each plugin will need to also define its XML (or database, etc) code, so centralising this could introduce problems later on.

我目前正在进行的项目在逻辑拆分方面犯了错误,有一个用于 jython 方面的包,一个用于规则引擎的包,用于 foo、bar、binglewozzle 等的包。我正在寻找特定于 XML 的解析器/writers 用于该包中的每个模块,而不是有一个 XML 包(我以前做过),尽管仍然会有一个核心 XML 包来共享逻辑。然而,这样做的一个原因是它可能是可扩展的(插件),因此每个插件还需要定义其 XML(或数据库等)代码,因此将其集中可能会在以后引入问题。

In the end it seems to be how it seems most sensible for the particular project. I think it's easy to package along the lines of the typical project layered diagram however. You'll end up with a mix of logical and functional packaging.

最后,它似乎是对特定项目最明智的方式。但是,我认为按照典型项目分层图的方式打包很容易。您最终会得到逻辑和功能包装的混合。

What's needed is tagged namespaces. An XML parser for some Jython functionality could be tagged both Jython and XML, rather than having to choose one or the other.

需要的是标记的命名空间。某些 Jython 功能的 XML 解析器可以同时标记 Jython 和 XML,而不必选择其中之一。

Or maybe I'm wibbling.

或者也许我在胡说八道。

回答by Ben Hardy

Most java projects I've worked on slice the java packages functionally first, then logically.

我参与过的大多数 Java 项目首先在功能上对 java 包进行切片,然后在逻辑上进行切片。

Usually parts are sufficiently large that they're broken up into separate build artifacts, where you might put core functionality into one jar, apis into another, web frontend stuff into a warfile, etc.

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

回答by Varkhan

I would personally go for functional naming. The short reason: it avoids code duplication or dependency nightmare.

我个人会选择函数式命名。简短的原因:它避免了代码重复或依赖噩梦。

Let me elaborate a bit. What happens when you are using an external jar file, with its own package tree? You are effectively importing the (compiled) code into your project, and with it a (functionally separated) package tree. Would it make sense to use the two naming conventions at the same time? No, unless that was hidden from you. And it is, if your project is small enough and has a single component. But if you have several logical units, you probably don't want to re-implement, let's say, the data file loading module. You want to share it between logical units, not have artificial dependencies between logically unrelated units, and not have to choose which unit you are going to put that particular shared tool into.

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

I guess this is why functional naming is the most used in projects that reach, or are meant to reach, a certain size, and logical naming is used in class naming conventions to keep track of the specific role, if any of each class in a package.

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

I will try to respond more precisely to each of your points on logical naming.

我将尝试更准确地回应您关于逻辑命名的每个观点。

  1. If you have to go fishing in old classes to modify functionalities when you have a change of plans, it's a sign of bad abstraction: you should build classes that provide a well defined functionality, definable in one short sentence. Only a few, top-level classes should assemble all these to reflect your business intelligence. This way, you will be able to reuse more code, have easier maintenance, clearer documentation and less dependency issues.

  2. That mainly depends on the way you grok your project. Definitely, logical and functional view are orthogonal. So if you use one naming convention, you need to apply the other one to class names in order to keep some order, or fork from one naming convention to an other at some depth.

  3. Access modifiers are a good way to allow other classes that understand your processingto access the innards of your class. Logical relationship does not mean an understanding of algorithmic or concurrency constraints. Functional may, although it does not. I am very weary of access modifiers other than public and private, because they often hide a lack of proper architecturing and class abstraction.

  4. In big, commercial projects, changing technologies happens more often than you would believe. For instance, I have had to change 3 times already of XML parser, 2 times of caching technology, and 2 times of geolocalisation software. Good thing I had hid all the gritty details in a dedicated package...

  1. 如果您在更改计划时不得不在旧类中钓鱼以修改功能,那么这是一种糟糕的抽象:您应该构建提供明确定义的功能的类,可以用一个简短的句子来定义。只有少数顶级类应该组合所有这些以反映您的商业智能。这样,您将能够重用更多代码、更容易维护、更清晰的文档和更少的依赖问题。

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

  3. 访问修饰符是允许其他理解您的处理的类访问您的类的内部的好方法。逻辑关系并不意味着理解算法或并发约束。功能可能,虽然它没有。我对 public 和 private 以外的访问修饰符非常厌烦,因为它们经常隐藏缺乏适当的架构和类抽象。

  4. 在大型商业项目中,更改技术的频率比您想象的要高。例如,我不得不更改 3 次 XML 解析器、2 次缓存技术和 2 次地理定位软件。好在我把所有的细节都藏在了一个专用的包里……

回答by Bill Michell

From a purely practical standpoint, java's visibility constructs allow classes in the same package to access methods and properties with protectedand defaultvisibility, as well as the publicones. Using non-public methods from a completely different layer of the code would definitely be a big code smell. So I tend to put classes from the same layer into the same package.

纯粹从实用的角度来看,Java的知名度结构允许在同一个包访问方法和与属性的类protecteddefault知名度,还有public的人。使用来自完全不同的代码层的非公共方法肯定会产生很大的代码味道。所以我倾向于将同一层的类放到同一个包中。

I don't often use these protected or default methods elsewhere - except possibly in the unit tests for the class - but when I do, it is alwaysfrom a class at the same layer

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

回答by erickson

Packages are to be compiled and distributed as a unit. When considering what classes belong in a package, one of the key criteria is its dependencies. What other packages (including third-party libraries) does this class depend on. A well-organized system will cluster classes with similar dependencies in a package. This limits the impact of a change in one library, since only a few well-defined packages will depend on it.

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

It sounds like your logical, vertical system might tend to "smear" dependencies across most packages. That is, if every feature is packaged as a vertical slice, every package will depend on every third party library that you use. Any change to a library is likely to ripple through your whole system.

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

回答by Buu Nguyen

I find myself sticking with Uncle Bob's package design principles. In short, classes which are to be reused together and changed together (for the same reason, e.g. a dependency change or a framework change) should be put in the same package. IMO, the functional breakdown would have better chance of achieving these goals than the vertical/business-specific break-down in most applications.

我发现自己坚持鲍勃叔叔的包装设计原则。简而言之,要一起重用和一起更改的类(出于相同的原因,例如依赖项更改或框架更改)应该放在同一个包中。IMO,在大多数应用程序中,功能分解比垂直/特定于业务的分解更有可能实现这些目标。

For example, a horizontal slice of domain objects can be reused by different kinds of front-ends or even applications and a horizontal slice of the web front-end is likely to change together when the underlying web framework needs to be changed. On the other hand, it's easy to imagine the ripple effect of these changes across many packages if classes across different functional areas are grouped in those packages.

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

Obviously, not all kinds of software are the same and the vertical breakdown may make sense (in terms of achieving the goals of reusability and closeability-to-change) in certain projects.

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

回答by levand

I try to design package structures in such a way that if I were to draw a dependency graph, it would be easy to follow and use a consistent pattern, with as few circular references as possible.

我尝试以这样的方式设计包结构,如果我要绘制依赖关系图,它会很容易遵循并使用一致的模式,尽可能少的循环引用。

For me, this is much easier to maintain and visualize in a vertical naming system rather than horizontal. if component1.display has a reference to component2.dataaccess, that throws off more warning bells than if display.component1 has a reference to dataaccess. component2.

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

Of course, components shared by both go in their own package.

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

回答by Peter ?tibrany

There are usually both levels of division present. From the top, there are deployment units. These are named 'logically' (in your terms, think Eclipse features). Inside deployment unit, you have functional division of packages (think Eclipse plugins).

通常存在两个级别的划分。从顶部开始,有部署单元。这些是“逻辑地”命名的(用您的话来说,想想 Eclipse 特性)。在部署单元中,您有包的功能划分(想想 Eclipse 插件)。

For example, feature is com.feature, and it consists of com.feature.client, com.feature.coreand com.feature.uiplugins. Inside plugins, I have very little division to other packages, although that's not unusual too.

例如,功能是com.feature,它由com.feature.clientcom.feature.corecom.feature.ui插件组成。在插件内部,我对其他包的划分很少,尽管这也很常见。

Update:Btw, there is great talk by Juergen Hoeller about code organization at InfoQ: http://www.infoq.com/presentations/code-organization-large-projects. Juergen is one of architects of Spring, and knows a lot about this stuff.

更新:顺便说一句,在 InfoQ:http: //www.infoq.com/presentations/code-organization-large-projects 上有 Juergen Hoeller 关于代码组织的精彩演讲。Juergen 是 Spring 的架构师之一,对这些东西非常了解。

回答by quant_dev

It depends. In my line of work, we sometimes split packages by functions (data access, analytics) or by asset class (credit, equities, interest rates). Just select the structure which is most convenient for your team.

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

回答by eljenso

For package design, I first divide by layer, then by some other functionality.

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

There are some additional rules:

还有一些额外的规则:

  1. layers are stacked from most general (bottom) to most specific (top)
  2. each layer has a public interface (abstraction)
  3. a layer can only depend on the public interface of another layer (encapsulation)
  4. a layer can only depend on more general layers (dependencies from top to bottom)
  5. a layer preferably depends on the layer directly below it
  1. 层从最一般(底部)到最具体(顶部)堆叠
  2. 每层都有一个公共接口(抽象)
  3. 一层只能依赖另一层的公共接口(封装)
  4. 一个层只能依赖更通用的层(从上到下的依赖)
  5. 一个层最好依赖于它正下方的层

So, for a web application for example, you could have the following layers in your application tier (from top to bottom):

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

  • presentation layer: generates the UI that will be shown in the client tier
  • application layer: contains logic that is specific to an application, stateful
  • service layer: groups functionality by domain, stateless
  • integration layer: provides access to the backend tier (db, jms, email, ...)
  • 表示层:生成将在客户端层显示的 UI
  • 应用层:包含特定于应用程序的逻辑,有状态
  • 服务层:按域分组功能,无状态
  • 集成层:提供对后端层(db、jms、email 等)的访问

For the resulting package layout, these are some additional rules:

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

  • the root of every package name is <prefix.company>.<appname>.<layer>
  • the interface of a layer is further split up by functionality: <root>.<logic>
  • the private implementation of a layer is prefixed with private: <root>.private
  • 每个包名的根是 <prefix.company>.<appname>.<layer>
  • 层的接口按功能进一步拆分: <root>.<logic>
  • 层的私有实现以私有为前缀: <root>.private

Here is an example layout.

这是一个示例布局。

The presentation layer is divided by view technology, and optionally by (groups of) applications.

表示层按视图技术划分,并可选择按(组)应用程序划分。

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

The application layer is divided into use cases.

应用层分为用例。

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

The service layer is divided into business domains, influenced by the domain logic in a backend tier.

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

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

The integration layer is divided into 'technologies' and access objects.

集成层分为“技术”和访问对象。

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
...

Advantages of separating packages like this is that it is easier to manage complexity, and it increases testability and reusability. While it seems like a lot of overhead, in my experience it actually comes very natural and everyone working on this structure (or similar) picks it up in a matter of days.

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

Why do I think the vertical approach is not so good?

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

In the layered model, several different high-level modules can use the same lower-level module. For example: you can build multiple views for the same application, multiple applications can use the same service, multiple services can use the same gateway. The trick here is that when moving through the layers, the level of functionality changes. Modules in more specific layers don't map 1-1 on modules from the more general layer, because the levels of functionality they express don't map 1-1.

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

When you use the vertical approach for package design, i.e. you divide by functionality first, then you force all building blocks with different levelsof functionality into the same 'functionality Hymanet'. You might design your general modules for the more specific one. But this violates the important principle that the more general layer should not know about more specific layers. The service layer for example shouldn't be modeled after concepts from the application layer.

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