6

我使用 Sonar 代码质量管理平台已经有一段时间了,在大多数情况下,我发现它对于揭示代码库中隐藏的设计缺陷非常有帮助。

然而,有一条规则比帮助更让我烦恼,那就是它检查“循环包引用”违规行为。

我想我完全理解包之间的这种依赖关系在哪里是一件坏事。例如,在典型的 3 层表示/服务/持久性分层设计中,让数据库处理代码引用回 UI 相关类几乎总是一个坏主意。我称其为“违规”没有问题。

但让我们考虑其他情况,例如设计类似 IDE 的应用程序。比如说,我们有一个包含Application接口的主包,它定义List<View> Application.getViews()了引用应用程序视图的方法。

然而,当View接口有一个Application getApplication()方法来引用它的父应用程序时,我相信这是一个很常见的设计,它会引入一个循环引用,前提是每个接口分别用com.myapp.ui, 和分隔com.myapp.ui.view

当然,你也可以直接把View接口放进com.myapp.ui去打破循环。但是,当您在 中拥有各种其他与视图相关的 API 时com.myapp.ui.view,其中许多是另一个抽象 API,例如AbstractViewContentViewAbstractContentView等。我想知道是否出于管理目的将它们放在单独的包中更为可取。

并且考虑到上述应用程序还有许多其他类似的情况,例如com.myapp.ui.action,等,如果我们要将它们都放在那里com.myapp.ui.perspective,这真的会使包变得拥挤。com.myapp.ui

那么,你建议用什么方法来处理这种情况呢?真的每个循环包都引用了一件坏事吗?或者如果我必须与他们一起生活,您如何配置 Sonar 以仅检查真实的、有问题的循环?

4

2 回答 2

6

每一个绝对的——除了这个;)——有时都会出错。那么,每个循环引用都是坏的吗?不,你必须使用你的判断。

但是如果你确实引入了循环依赖,那么值得问问你是否真的需要它,以及为什么。tl; dr 通常,您可能会发现打破循环可以提高您的模块化,特别是您单独测试组件的能力。

要使用您的示例,视图是否真的需要 a getApplication(),它可能返回一个相对“重”的对象(即,一个本身需要数据库、网络等的对象)?也许……但也许不是。如果您真正需要的getApplication是带有一些回调的东西(例如当用户启动某些操作时),那么在该回调的某个通用包中创建一个接口可能会很有用。所以,而不是:

com.foo.app.Application
com.foo.view.View
    Application getApplication()

你会有:

com.foo.common.Callback // maybe just a Callable, Runnable, etc?
com.foo.app.Application
    provides a Callback for some action foo
com.foo.view.View
    Callback getFooCallback()

你应该问的问题是:这给了我什么?可能是你必须存根太多以至于它没有给你太多——尽管这可能表明你可以把你的类分开一些。但它实际上可能更容易测试您的视图,因为现在您的单元测试可以 (1) 在不启动整个应用程序的情况下测试视图,并且 (b) 提供一个“虚拟”回调来执行类似保存描述操作的字符串,然后您的单元测试断言它保存了正确的字符串。

于 2013-05-14T05:24:16.870 回答
0

确实有一个开放的 JIRA 票可以防止将父/子包之间的循环视为质量缺陷:http: //jira.codehaus.org/browse/SONAR-3452

于 2013-05-26T09:21:07.750 回答