57

最近我遇到了这个 javalobby 帖子http://java.dzone.com/articles/how-chang-java-package按功能打包 java 代码。

我喜欢这个主意,但我对这种方法几乎没有疑问。我问了我的问题,但没有得到满意的答复。我希望 StackOverflow 上的人可以澄清我的问题。

我喜欢按功能打包的想法,它大大减少了在编码时跨包移动的时间,所有相关的东西都将放在一个地方(包)。但是不同包中的服务之间的交互呢?

假设我们正在构建一个博客应用程序,并且我们将所有与用户相关的操作(控制器/服务/存储库)放在com.mycompany.myblog.users包中。以及包中所有与博客文章相关的操作(控制器/服务/存储库)com.mycompany.myblog.posts

现在我想显示用户个人资料以及他发布的所有帖子。我应该打电话myblog.posts.PostsService.getPostsByUser(userId)myblog.users.UserController.showUserProfile()

包之间的耦合呢?

同样,无论我在哪里阅读按功能打包的信息,每个人都说这是一个很好的做法。那么为什么很多书籍作者甚至框架都鼓励分层分组呢?只是想知道:-)

4

3 回答 3

36

Take a look at uncle Bob's Package Design Principles. He explains reasons and motivations behind those principles, which I have elaborated on below:

Classes that get reused together should be packaged together so that the package can be treated as a sort of complete product available for you. And those which are reused together should be separated away from the ones those are not reused with. For example, your Logging utility classes are not necessarily used together with your file io classes. So package all logging them separately. But logging classes could be related to one another. So create a sort of complete product for logging, say, for the want of better name commons-logging package it in a (re)usable jar and another separate complete product for io utilities, again for the want of better name, say commons-io.jar. If you update say commons-io library to say support java nio, then you may not necessarily want to make any changes to the logging library. So separating them is better.

Now, let's say you wanted your logging utility classes to support structured logging for say some sort of log analysis by tools like splunk. Some clients of your logging utility may want to update to your newer version; some others may not. So when you release a new version, package all classes which are needed and reused together for migration. So some clients of your utility classes can safely delete your old commons-logging jar and move to commons-logging-new jar. Some other clients are still ok with older jar. However no clients are needed to have both these jars (new and old) just because you forced them to use some classes for older packaged jar.

Avoid cyclic dependencies. a depend on b; b on c; c on d; but d depends on a. The scenario is obviously deterring as it will be very difficult to define layers or modules, etc and you cannot vary them independly relative to each other.

Also, you could package your classes such that if a layer or module changes, other module or layers do not have to change necessarily. So, for example, if you decide to go from old MVC framework to a rest APIs upgrade, then only view and controller may need changes; your model does not.

于 2012-07-31T07:04:40.350 回答
16

除了包装设计的耦合之外,我建议查看 OOAD 原则,尤其是包装设计原则,例如

REP 发布重用等效原则 重用的粒度就是发布的粒度。

CCP 共同封闭原则 一起变化的类被打包在一起。

CRP 将 一起使用的公共重用原则类打包在一起。

ADP 非循环依赖原则 包的依赖图必须没有循环。

SDP 稳定依赖原则依赖于稳定的方向。

SAP 稳定抽象原则抽象随着稳定性而增加。

有关更多信息,您可以阅读敏捷软件开发、原则、模式和实践》一书

于 2012-07-31T04:58:40.453 回答
15

I personally like the "package by feature" approach, although you do need to apply quite a lot of judgement on where to draw the package boundaries. It's certainly a feasible and sensible approach in many circumstances.

You should probably achieve coupling between packages and modules using public interfaces - this keeps the coupling clean and manageable.

It's perfectly fine for the "blog posts" package to call into the "users" package as long as it uses well designed public interfaces to do so.

One big piece of advice though if you go down this approach: be very thoughtful about your dependencies and in particular avoid circular dependencies between packages. A good design should looks like a dependency tree - with the higher level areas of functionality depending on a set of common services which depend upon libraries of utility functions etc. To some extent, this will start to look like architectural "layers" with front-end packages calling into back-end services.

于 2012-07-31T05:02:50.940 回答