【问题标题】:How should I structure a Java application, where do I put my classes?我应该如何构建一个 Java 应用程序,我应该把我的类放在哪里?
【发布时间】:2010-09-05 16:12:27
【问题描述】:

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

我自己也一直有问题

  • 命名,
  • 放置

所以,

  1. 您将域特定常量放在哪里(以及此类类的最佳名称是什么)?
  2. 对于基础设施和特定领域的东西,您在哪里放置类(例如,我有一个 FileStorageStrategy 类,它将文件存储在数据库中,或者存储在数据库中)?
  3. 在哪里放置异常?
  4. 有什么标准可以参考吗?

【问题讨论】:

  • 显然没有明确的答案,但在使用 maven2 一段时间后,我开始了解给定的结构,因此我宣布 maven 答案为一个。 (这并不意味着其他错误,或任何东西)我刚刚意识到不必考虑构建的初始阶段是多么容易,您只需将源代码和资源放在那些给定的目录中并编译无需创建 ant 文件和东西。

标签: java architecture


【解决方案1】:

我真的开始喜欢 Maven 的Standard Directory Layout

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

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

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

优点:

  • 您的测试对您的测试类具有包(或“默认”)级别的访问权限。
  • 您可以通过将 src/test/java 作为源根目录轻松地仅将生产源打包到 JAR 中。

关于类放置和包的一条经验法则:

一般来说,结构良好的项目将免费使用circular dependencies。了解它们什么时候不好(以及什么时候是 not),并考虑使用像 JDependSonarJ 这样的工具来帮助您消除它们。

【讨论】:

    【解决方案2】:

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

    /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
    

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

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

    【讨论】:

      【解决方案3】:

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

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

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

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

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

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

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

      这种方法有很多优点:

      • 清晰的关注点分离
      • 每个模块都可以打包为一个 jar(或者 web 模块的一个 war),因此可以更轻松地重用代码(例如,我们可以将模块安装在 maven 存储库中,然后在另一个项目中重用它)李>
      • 项目各部分的最大独立性

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

      【讨论】:

        【解决方案4】:

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

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

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

        【讨论】:

          【解决方案5】:

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

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

          异常通常与抛出它们的类在同一个包中,所以如果你有FileStorageException,它会与FileStorageStrategy在同一个包中。同样,定义常量的接口也会在同一个包中。

          其实并没有这样的标准,只要使用常识,如果太乱了,就重构吧!

          【讨论】:

            【解决方案6】:

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

            【讨论】:

              【解决方案7】:

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

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

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

              • 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'使用)

              在这些层中的每一层中,如果有很多类,自然会按类型对类进行分组。这里常见的反模式是不必要地引入过多的包和子包的级别,从而使每个包中只有几个类。

              【讨论】:

                【解决方案8】:

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

                【讨论】:

                  【解决方案9】:

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

                  【讨论】:

                    【解决方案10】:

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

                    例如: 模型用于数据库相关调用

                    查看类处理你所看到的

                    控制核心功能类

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

                    等等

                    【讨论】:

                      猜你喜欢
                      • 2020-08-02
                      • 2014-01-30
                      • 2020-09-30
                      • 1970-01-01
                      • 2019-07-11
                      • 1970-01-01
                      • 2016-08-25
                      • 2013-08-15
                      • 1970-01-01
                      相关资源
                      最近更新 更多