我应该如何构建 Java 应用程序,将类放在哪里?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7596/
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-10-29 10:44:02  来源:igfitidea点击:

How should I structure a Java application, where do I put my classes?

javaarchitecture

提问by Mauli

First of all, I know how to build a Java application. But I have always been puzzled about where to put my classes. There are proponents for organizing the packages in a strictly domain oriented fashion, others separate by tier.

首先,我知道如何构建 Java 应用程序。但我一直对把课程放在哪里感到困惑。有人支持以严格面向领域的方式组织包,其他人按层分开。

I myself have always had problems with

我自己一直有问题

  • naming,
  • placing
  • 命名,
  • 放置

So,

所以,

  1. Where do you put your domain specific constants (and what is the best name for such a class)?
  2. Where do you put classes for stuff which is both infrastructural and domain specific (for instance I have a FileStorageStrategy class, which stores the files either in the database, or alternatively in database)?
  3. Where to put Exceptions?
  4. Are there any standards to which I can refer?
  1. 您将域特定常量放在哪里(以及此类类的最佳名称是什么)?
  2. 你把基础设施​​和特定领域的东西的类放在哪里(例如我有一个 FileStorageStrategy 类,它将文件存储在数据库中,或者在数据库中)?
  3. 在哪里放置异常?
  4. 有什么标准可以参考吗?

采纳答案by Brian Laframboise

I've really come to like Maven's Standard Directory Layout.

我真的很喜欢 Maven 的标准目录布局

One of the key ideas for me is to have two source roots - one for production code and one for test code like so:

对我来说,一个关键的想法是有两个源代码根——一个用于生产代码,一个用于测试代码,如下所示:

MyProject/src/main/java/com/acme/Widget.java
MyProject/src/test/java/com/acme/WidgetTest.java

(here, both src/main/java and src/test/java are source roots).

(这里, src/main/java 和 src/test/java 都是源根)。

Advantages:

好处:

  • Your tests have package (or "default") level access to your classes under test.
  • You can easily package only your production sources into a JAR by dropping src/test/java as a source root.
  • 您的测试对您的测试类具有包(或“默认”)级别的访问权限。
  • 通过将 src/test/java 作为源根目录,您可以轻松地将生产源代码打包到 JAR 中。

One rule of thumb about class placement and packages:

关于课程安排和课程包的一项经验法则:

Generally speaking, well structured projects will be free of circular dependencies. Learn when they are bad (and when they are not), and consider a tool like JDependor SonarJthat will help you eliminate them.

一般来说,结构良好的项目将没有循环依赖。了解它们什么时候变坏(什么时候不是),并考虑使用JDependSonarJ 之类的工具帮助您消除它们。

回答by cringe

I'm a huge fan of organized sources, so I always create the following directory structure:

我非常喜欢有组织的资源,所以我总是创建以下目录结构:

/src - for your packages & classes
/test - for unit tests
/docs - for documentation, generated and manually edited
/lib - 3rd party libraries
/etc - unrelated stuff
/bin (or /classes) - compiled classes, output of your compile
/dist - for distribution packages, hopefully auto generated by a build system

In /src I'm using the default Java patterns: Package names starting with your domain (org.yourdomain.yourprojectname) and class names reflecting the OOP aspect you're creating with the class (see the other commenters). Common package names like util, model, view, eventsare useful, too.

在 /src 中,我使用默认的 Java 模式:以您的域 (org.yourdomain.yourprojectname) 开头的包名和反映您使用该类创建的 OOP 方面的类名(请参阅其他评论者)。utilmodelviewevents等常用包名称也很有用。

I tend to put constants for a specific topic in an own class, like SessionConstantsor ServiceConstantsin the same package of the domain classes.

我倾向于将特定主题的常量放在自己的类中,例如SessionConstantsServiceConstants放在同一个域类包中。

回答by Sébastien D.

Where I'm working, we're using Maven 2 and we have a pretty nice archetype for our projects. The goal was to obtain a good separation of concerns, thus we defined a project structure using multiple modules (one for each application 'layer'): - common: common code used by the other layers (e.g., i18n) - entities: the domain entities - repositories: this module contains the daos interfaces and implementations - services-intf: interfaces for the services (e.g, UserService, ...) - services-impl: implementations of the services (e.g, UserServiceImpl) - web: everything regarding the web content (e.g., css, jsps, jsf pages, ...) - ws: web services

在我工作的地方,我们使用 Maven 2,我们的项目有一个非常好的原型。目标是获得良好的关注点分离,因此我们使用多个模块(每个应用程序“层”一个)定义了一个项目结构: - 公共:其他层(例如,i18n)使用的公共代码 - 实体:域实体 - 存储库:该模块包含 daos 接口和实现 - services-intf:服务的接口(例如 UserService,...) - services-impl:服务的实现(例如,UserServiceImpl) - web:关于Web 内容(例如,css、jsps、jsf 页面等)- ws:Web 服务

Each module has its own dependencies (e.g., repositories could have jpa) and some are project wide (thus they belong in the common module). Dependencies between the different project modules clearly separate things (e.g., the web layer depends on the service layer but doesn't know about the repository layer).

每个模块都有自己的依赖项(例如,存储库可以有 jpa),有些是项目范围的(因此它们属于公共模块)。不同项目模块之间的依赖关系清楚地将事物分开(例如,web 层依赖于服务层但不知道存储库层)。

Each module has its own base package, for example if the application package is "com.foo.bar", then we have:

每个模块都有自己的基础包,例如如果应用程序包是“com.foo.bar”,那么我们有:

com.foo.bar.common
com.foo.bar.entities
com.foo.bar.repositories
com.foo.bar.services
com.foo.bar.services.impl
...

Each module respects the standard maven project structure:

每个模块都遵循标准的 maven 项目结构:

   src\
   ..main\java
     ...\resources
   ..test\java
     ...\resources

Unit tests for a given layer easily find their place under \src\test... Everything that is domain specific has it's place in the entities module. Now something like a FileStorageStrategy should go into the repositories module, since we don't need to know exactly what the implementation is. In the services layer, we only know the repository interface, we do not care what the specific implementation is (separation of concerns).

给定层的单元测试很容易在 \src\test 下找到它们的位置......所有特定于域的东西都放在实体模块中。现在像 FileStorageStrategy 这样的东西应该进入 repositories 模块,因为我们不需要确切地知道实现是什么。在服务层,我们只知道repository接口,不关心具体的实现是什么(关注点分离)。

There are multiple advantages to this approach:

这种方法有很多优点:

  • clear separation of concerns
  • each module is packageable as a jar (or a war in the case of the web module) and thus allows for easier code reuse (e.g., we could install the module in the maven repository and reuse it in another project)
  • maximum independence of each part of the project
  • 明确的关注点分离
  • 每个模块都可以打包成一个 jar(或者在 web 模块的情况下是一个 war),从而允许更容易的代码重用(例如,我们可以在 maven 存储库中安装模块并在另一个项目中重用它)
  • 项目每个部分的最大独立性

I know this doesn't answer all your questions, but I think this could put you on the right path and could prove useful to others.

我知道这并不能回答您的所有问题,但我认为这可以让您走上正确的道路,并且可以证明对其他人有用。

回答by graham.reeds

Class names should always be descriptive and self-explanatory. If you have multiple domains of responsibility for your classes then they should probably be refactored.

类名应该总是描述性的和不言自明的。如果你的类有多个责任域,那么它们可能应该被重构。

Likewise for you packages. They should be grouped by domain of responsibility. Every domain has it's own exceptions.

同样适用于您的包裹。它们应按职责范围进行分组。每个域都有它自己的例外。

Generally don't sweat it until you get to a point where it is becoming overwhelming and bloated. Then sit down and don't code, just refactor the classes out, compiling regularly to make sure everything works. Then continue as you did before.

通常不要出汗,直到它变得不堪重负和臃肿。然后坐下来不要编码,只需将类重构出来,定期编译以确保一切正常。然后像以前一样继续。

回答by Kieron

Use packages to group related functionality together.

使用包将相关功能组合在一起。

Usually the top of your package tree is your domain name reversed (com.domain.subdomain) to guarantee uniqueness, and then usually there will be a package for your application. Then subdivide that by related area, so your FileStorageStrategymight go in, say, com.domain.subdomain.myapp.storage, and then there might be specific implementations/subclasses/whatever in com.domain.subdomain.myapp.storage.fileand com.domain.subdomain.myapp.storage.database. These names can get pretty long, but importkeeps them all at the top of files and IDEs can help to manage that as well.

通常你的包树的顶部是你的域名倒转 ( com.domain.subdomain) 以保证唯一性,然后通常会有你的应用程序的包。然后按相关区域细分,因此您FileStorageStrategy可能会进入,比如说,,com.domain.subdomain.myapp.storage然后可能会有特定的实现/子类/任何在com.domain.subdomain.myapp.storage.file和 中com.domain.subdomain.myapp.storage.database。这些名称可能会很长,但import将它们全部放在文件的顶部,IDE 也可以帮助进行管理。

Exceptions usually go in the same package as the classes that throw them, so if you had, say, FileStorageExceptionit would go in the same package as FileStorageStrategy. Likewise an interface defining constants would be in the same package.

异常通常与抛出它们的类放在同一个包中,所以如果你有,比如说,FileStorageException它会和FileStorageStrategy. 同样,定义常量的接口将位于同一个包中。

There's not really any standard as such, just use common sense, and if it all gets too messy, refactor!

没有真正的任何标准,只需使用常识,如果一切都变得太乱,请重构!

回答by Kieron

One thing that I found very helpful for unit tests was to have a myApp/src/ and also myApp/test_src/ directories. This way, I can place unit tests in the same packages as the classes they test, and yet I can easily exclude the test cases when I prepare my production installation.

我发现对单元测试非常有帮助的一件事是拥有一个 myApp/src/ 和 myApp/test_src/ 目录。通过这种方式,我可以将单元测试放在与它们测试的类相同的包中,但我可以在准备生产安装时轻松排除测试用例。

回答by Peter Hilton

Short answer: draw your system architecture in terms of modules, drawn side-by-side, with each module sliced vertically into layers (e.g. view, model, persistence). Then use a structure like com.mycompany.myapp.somemodule.somelayer, e.g. com.mycompany.myapp.client.viewor com.mycompany.myapp.server.model.

简短回答:根据模块绘制您的系统架构,并排绘制,每个模块垂直划分为多个层(例如视图、模型、持久性)。然后使用类似com.mycompany.myapp.somemodule.somelayer的结构,例如com.mycompany.myapp.client.viewcom.mycompany.myapp.server.model

Using the top level of packages for application modules, in the old-fashioned computer-science sense of modular programming, ought to be obvious. However, on most of the projects I have worked on we end up forgetting to do that, and end up with a mess of packages without that top-level structure. This anti-pattern usually shows itself as a package for something like 'listeners' or 'actions' that groups otherwise unrelated classes simply because they happen to implement the same interface.

模块化编程的老式计算机科学意义上,将顶级包用于应用程序模块应该是显而易见的。然而,在我参与的大多数项目中,我们最终忘记了这样做,最终得到一堆没有顶层结构的包。这种反模式通常将自己显示为诸如“侦听器”或“操作”之类的包,它们将其他不相关的类分组,只是因为它们碰巧实现了相同的接口。

Within a module, or in a small application, use packages for the application layers. Likely packages include things like the following, depending on the architecture:

在模块内或小型应用程序中,为应用程序层使用包。可能的包包括以下内容,具体取决于架构:

  • com.mycompany.myapp.view
  • com.mycompany.myapp.model
  • com.mycompany.myapp.services
  • com.mycompany.myapp.rules
  • com.mycompany.myapp.persistence(or 'dao' for data access layer)
  • com.mycompany.myapp.util(beware of this being used as if it were 'misc')
  • com.mycompany.myapp.view
  • com.mycompany.myapp.model
  • com.mycompany.myapp.services
  • com.mycompany.myapp.rules
  • com.mycompany.myapp.persistence(或数据访问层的“dao”)
  • com.mycompany.myapp.util(当心它被当作'misc'使用)

Within each of these layers, it is natural to group classes by type if there are a lot. A common anti-pattern here is to unnecessarily introduce too many packages and levels of sub-package so that there are only a few classes in each package.

在这些层中的每一个中,如果有很多类,按类型对类进行分组是很自然的。这里一个常见的反模式是不必要地引入太多的包和子包的层次,以至于每个包中只有几个类。

回答by Michael Neale

I think keep it simple and don't over think it. Don't over abstract and layer too much. Just keep it neat, and as it grows, refactoring it is trivial. One of the best features of IDEs is refactoring, so why not make use of it and save you brain power for solving problems that are related to your app, rather then meta issues like code organisation.

我认为保持简单,不要想太多。不要过度抽象和分层。只要保持整洁,随着它的增长,重构它是微不足道的。IDE 的最佳功能之一是重构,所以为什么不利用它并节省您的脑力来解决与您的应用程序相关的问题,而不是像代码组织这样的元问题。

回答by bpapa

One thing I've done in the past - if I'm extending a class I'll try and follow their conventions. For example, when working with the Spring Framework, I'll have my MVC Controller classes in a package called com.mydomain.myapp.web.servlet.mvc If I'm not extending something I just go with what is simplest. com.mydomain.domain for Domain Objects (although if you have a ton of domain objects this package could get a bit unwieldy). For domain specific constants, I actually put them as public constants in the most related class. For example, if I have a "Member" class and have a maximum member name length constant, I put it in the Member class. Some shops make a separate Constants class but I don't see the value in lumping unrelated numbers and strings into a single class. I've seen some other shops try to solve this problem by creating SEPARATE Constants classes, but that just seems like a waste of time and the result is too confusing. Using this setup, a large project with multiple developers will be duplicating constants all over the place.

我过去做过的一件事 - 如果我要扩展一个类,我会尝试遵循他们的约定。例如,在使用 Spring Framework 时,我会将我的 MVC 控制器类放在一个名为 com.mydomain.myapp.web.servlet.mvc 的包中。如果我不扩展某些东西,我只会使用最简单的东西。com.mydomain.domain 用于域对象(尽管如果您有大量域对象,这个包可能会变得有点笨拙)。对于特定于域的常量,我实际上将它们作为公共常量放在最相关的类中。例如,如果我有一个“Member”类并且有一个最大成员名称长度常量,我将它放在Member 类中。一些商店制作了一个单独的 Constants 类,但我没有看到将不相关的数字和字符串集中到一个类中的价值。一世' 我已经看到其他一些商店试图通过创建 SEPARATE Con​​stants 类来解决这个问题,但这似乎是在浪费时间,而且结果太混乱了。使用此设置,具有多个开发人员的大型项目将到处复制常量。

回答by Bryan Denny

I like break my classes down into packages that are related to each other.

我喜欢将我的课程分解成相互关联的包。

For example: ModelFor database related calls

例如: Model用于数据库相关调用

ViewClasses that deal with what you see

查看处理您所见内容的类

ControlCore functionality classes

控制核心功能类

UtilAny misc. classes that are used (typically static functions)

Util任何杂项。使用的类(通常是静态函数)

etc.

等等。