关于体系结构:我应该如何构建Java应用程序,将类放在哪里?

关于体系结构:我应该如何构建Java应用程序,将类放在哪里?

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

首先,我知道如何构建Java应用程序。 但是我总是对在哪里上课感到困惑。 支持以严格面向领域的方式组织软件包的支持者,其他支持者则按层分开。

我本人一直有问题

  • 命名,
  • 放置

所以,

  • 您将域特定的常量放在哪里(以及此类的最佳名称是什么)?
  • 您在哪里放置基础结构和领域特定的东西的类(例如,我有一个FileStorageStrategy类,该文件将文件存储在数据库中,或者存储在数据库中)?
  • 在哪里放置例外?
  • 有什么我可以参考的标准吗?

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

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

    1
    2
    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中。

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

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


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

    1
    2
    3
    4
    5
    6
    7
    /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方面的类名称(请参见其他注释器)。诸如util,model,view,events之类的通用软件包名称也很有用。

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


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

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

    每个模块都有其自己的基本包,例如,如果应用程序包为" com.foo.bar",则我们具有:

    1
    2
    3
    4
    5
    6
    com.foo.bar.common
    com.foo.bar.entities
    com.foo.bar.repositories
    com.foo.bar.services
    com.foo.bar.services.impl
    ...

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

    1
    2
    3
    4
    5
    6
    7
       src\\
       ..main\\java
         ...\
    esources
       ..test\\java
         ...\
    esources

    给定层的单元测试可以很容易地在\ src \ test下找到它们的位置。所有特定于领域的东西都位于实体模块中。现在,应该将诸如FileStorageStrategy之类的内容放入存储库模块中,因为我们不需要确切地知道实现是什么。在服务层,我们只知道存储库接口,我们不在乎具体的实现是什么(关注点分离)。

    这种方法有多个优点:

    • 明确分离关注点
    • 每个模块都可以打包为jar(对于Web模块而言则是战争),因此可以简化代码重用(例如,我们可以将模块安装在maven存储库中,然后在另一个项目中重用)
    • 项目各部分的最大独立性

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


    类名应始终是描述性的,并且不言自明。如果您对类负责多个领域,则应该将其重构。

    对于您的包裹也是如此。它们应按责任范围分组。每个域都有其自己的例外。

    通常不要流汗,直到它变得势不可挡和肿胀为止。然后坐下来,不编写代码,只重构类,定期编译以确保一切正常。然后像以前一样继续。


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

    在老式的计算机科学模块化编程意义上,将顶层软件包用于应用程序模块应该是显而易见的。但是,在我从事的大多数项目中,我们最终都忘记了这样做,并且最终导致一堆没有该顶层结构的软件包。这种反模式通常将自己显示为"侦听器"或"动作"之类的程序包,这些程序将原本无关的类分组,仅仅是因为它们碰巧实现了相同的接口。

    在模块内或在小型应用程序中,将包用于应用程序层。根据体系结构,可能的软件包包括以下内容:

    • 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"一样被使用)

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


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


    使用软件包将相关功能分组在一起。

    通常,包树的顶部是域名的反向名称(com.domain.subdomain),以确保唯一性,然后通常会为您的应用程序提供一个包。然后按相关区域细分,这样您的FileStorageStrategy可能会进入,例如com.domain.subdomain.myapp.storage,然后com.domain.subdomain.myapp.storage.filecom.domain.subdomain.myapp.storage.database中可能会有特定的实现/子类/其他内容。这些名称可能会很长,但是import会将它们全部保留在文件的顶部,而IDE也可以帮助管理它们。

    异常通常与引发它们的类放在同一个包中,因此,如果有FileStorageException,它将与FileStorageStrategy放在同一个包中。同样,定义常量的接口将位于同一包中。

    并没有真正的标准,只是使用常识,如果一切都太混乱了,那就重构吧!


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


    我喜欢将我的课程分解为相互关联的软件包。

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

    查看符合您所见内容的课程

    控制核心功能类

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

    等等


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


    推荐阅读