我应该如何构建 Java 应用程序,我的类应该放在哪里?
-
08-06-2019 - |
题
首先,我知道如何构建一个Java应用程序。但我一直很困惑该把课放在哪里。有些支持者以严格面向领域的方式组织包,其他人则按层分开。
我自己也一直有这样的问题
- 命名,
- 放置
所以,
- 您将域特定常量放在哪里(以及此类的最佳名称是什么)?
- 您将基础设施和特定领域的类放在哪里(例如我有一个 FileStorageStrategy 类,它将文件存储在数据库中,或者存储在数据库中)?
- 异常应该放在哪里?
- 有什么标准可以参考吗?
解决方案
我真的开始喜欢Maven了 标准目录布局.
对我来说,关键想法之一是拥有两个源代码根 - 一个用于生产代码,另一个用于测试代码,如下所示:
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 中。
关于课程安排和课程包的一条经验法则:
一般来说,结构良好的项目不会 循环依赖. 。学习他们什么时候表现不好(以及什么时候他们表现不好) 不是),并考虑像这样的工具 J依赖 或者 声纳J 这将帮助您消除它们。
其他提示
我非常喜欢有组织的资源,所以我总是创建以下目录结构:
/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 方面的类名称(请参阅其他评论者)。常见的包名称如 实用程序, 模型, 看法, 事件 也很有用。
我倾向于将特定主题的常量放在自己的类中,例如 会话常量 或者 服务常数 在域类的同一包中。
在我工作的地方,我们使用 Maven 2,并且我们的项目有一个非常好的原型。目标是获得良好的关注点分离,因此我们使用多个模块定义了一个项目结构(每个应用程序“层”一个):- 常见的:其他层(例如I18N)使用的常见代码 - 实体:域实体 - 存储库:该模块包含DAOS接口和实现-Services -Intf:服务的接口(例如,UserService,...) - 服务-IMPL:服务的实现(例如,UserviceImpl) - 网络:有关Web内容的所有内容(例如CSS,JSP,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 est 下找到它们的位置...特定于域的所有内容都在实体模块中占有一席之地。现在像 FileStorageStrategy 这样的东西应该进入存储库模块,因为我们不需要确切地知道实现是什么。在服务层,我们只知道存储库接口,我们不关心具体实现是什么(关注点分离)。
这种方法有很多优点:
- 明确的关注点分离
- 每个模块都可以打包为 jar(或者 web 模块中的 war),因此可以更轻松地重用代码(例如,我们可以将模块安装在 Maven 存储库中,然后在另一个项目中重用它)
- 项目各部分的最大独立性
我知道这并不能回答您的所有问题,但我认为这可以让您走上正确的道路,并且对其他人可能有用。
类名应该始终具有描述性且不言自明。如果您的类有多个责任域,那么它们可能应该被重构。
同样对于你的包裹。他们应该按职责范围进行分组。每个域都有它自己的例外。
一般来说,不要出汗,直到你变得难以承受和臃肿。然后坐下来,不要编码,只需重构类,定期编译以确保一切正常。然后像以前一样继续。
使用包将相关功能组合在一起。
通常你的包树的顶部是你的域名反转(com.domain.subdomain
)以保证唯一性,然后通常会有一个适合您的应用程序的包。然后按相关领域细分,这样你的 FileStorageStrategy
可能会进去说, com.domain.subdomain.myapp.storage
, ,然后可能有特定的实现/子类/无论什么 com.domain.subdomain.myapp.storage.file
和 com.domain.subdomain.myapp.storage.database
. 。这些名字可能会很长,但是 import
将它们全部保留在文件顶部,IDE 也可以帮助管理它们。
异常通常与引发异常的类位于同一个包中,因此如果您有,比如说, FileStorageException
它将与以下内容放在同一个包中 FileStorageStrategy
. 。同样,定义常量的接口也将位于同一个包中。
实际上并没有任何标准,只需使用常识,如果一切变得太混乱,请重构!
我发现对单元测试非常有帮助的一件事是拥有 myApp/src/ 和 myApp/test_src/ 目录。这样,我可以将单元测试放置在与它们测试的类相同的包中,而且在准备生产安装时可以轻松排除测试用例。
简短回答:根据模块绘制系统架构,并排绘制,每个模块垂直分成几层(例如视图、模型、持久性)。然后使用类似的结构 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 (注意这个被当作“杂项”使用)
在每一层中,如果类很多,很自然地按类型对类进行分组。这里的一个常见的反模式是不必要地引入太多的包和子包级别,使得每个包中只有几个类。
我认为保持简单,不要想太多。不要过度抽象和分层。只要保持它整洁,随着它的增长,重构它是微不足道的。IDE 的最佳功能之一是重构,所以为什么不利用它来节省您的脑力来解决与应用程序相关的问题,而不是像代码组织这样的元问题。
我过去做过的一件事 - 如果我要扩展一门课程,我会尝试遵循他们的惯例。例如,在使用弹簧框架时,我将在一个名为com.mydomain.myapp.web.servlet.mvc的软件包中使用我的MVC控制器类。com.mydomain.domain 用于域对象(尽管如果您有大量域对象,这个包可能会有点笨拙)。对于特定于领域的常量,我实际上将它们作为公共常量放在最相关的类中。例如,如果我有一个“Member”类并且有一个最大成员名称长度常量,我将其放入 Member 类中。有些商店创建了一个单独的 Constants 类,但我没有看到将不相关的数字和字符串集中到一个类中的价值。我见过其他一些商店试图通过创建单独的常量类来解决这个问题,但这似乎是浪费时间,而且结果太混乱。使用此设置,具有多个开发人员的大型项目将在各处复制常量。
我喜欢将我的类分解成彼此相关的包。
例如:模型 对于数据库相关的调用
看法 处理你所看到的内容的类
控制 核心功能类
效用 任何杂项。使用的类(通常是静态函数)
ETC。