28

我一直在我的 Java 项目上运行一些指标,显然包之间存在很多依赖循环。我真的不知道如何将东西组织成包,所以我只是做了对我有意义的事情,这显然是错误的。

我的项目是一个神经网络框架。神经网络具有神经元,它们通过连接相互连接。他们需要相互依赖。但是,也有不同类型的神经元,所以我认为将它们都放在自己的“神经元”包中是个好主意。显然,连接不是神经元,所以它不应该在包中,但由于它们相互引用,我现在有一个循环依赖。

这只是一个例子,但我还有更多这样的情况。你如何处理这些情况?

另外,我读到包层次结构中较高的包中的类不应该引用更深的包中的类。这意味着包“nn”中的 NeuralNetwork 类不能引用包“nn.neurons”中的神经元。你们遵守这个原则吗?如果我将 NeuralNetwork 移动到 'nn.networks' 或其他什么?在这种情况下,它将引用同级包而不是子包。这是更好的做法吗?

4

5 回答 5

13

antcontrib VerifyDesign 任务将帮助您做您想做的事:

例如,如果一个源代码树中有三个包

* biz.xsoftware.presentation
* biz.xsoftware.business
* biz.xsoftware.dataaccess

并且自然呈现应该只依赖于业务包,而业务应该依赖于数据访问。如果您以这种方式定义您的设计并且违反了该设计,则在调用 verifydesign ant 任务时构建将失败。例如,如果我在 biz.xsoftware.presentation 中创建了一个类,并且该类依赖于 biz.xsoftware.dataaccess 中的一个类,则构建将失败。这确保了设计实际上遵循记录的内容(至少在某种程度上)。这对于自动构建特别好

因此,一旦您决定了事物的组织方式,您就可以在编译时强制执行要求。您还可以获得细粒度的控制,因此您可以允许某些情况打破这些“规则”。所以你可以允许一些循环。

根据您想要做的事情,您可能会发现“utils”包是有意义的。

对于您引用的特定情况......我可能会做这样的事情:

  • 包 nn 包含 Nueron 和 Connection
  • 包 nn.neurons 包含 Nueron 的子类

Neuron 和 Connection 都是 NeuralNetowrk 中使用的高级概念,因此将它们放在一起是有意义的。Neuron 和 Connection 类可以相互引用,而 Connection 类不需要了解 Neuron 子类。

于 2009-03-22T16:17:02.327 回答
9

首先,您有理由担心,因为包之间的循环依赖关系很糟糕。随着项目规模的扩大,由此产生的问题变得越来越重要,但没有理由及时解决这种情况。

您应该通过将您一起重用的类放在同一个包中来组织您的类。因此,如果您有 AbstractNeuron 和 AbstractConnection,您会将它们放在同一个包中。如果您现在有实现 HumanNeuron 和 HumanConnection,您可以将它们放在同一个包中(例如称为 *.network.human)。或者,您可能只有一种类型的连接,例如 BaseConnection 和许多不同的神经元。原理保持不变。您将 BaseConnection 与 BaseNeuron 放在一起。HumanNeuron 与 HumanSignal 等一起在其自己的包中。 VirtualNeuron 与 VirtualSignal 等一起。您说:“显然,连接不是神经元,因此它不应该在包中..”。这不是那么明显,准确地说也不正确。

你说你把所有的神经元放在同一个包里。这都不正确,除非您一起重用所有实现。再次,看看我上面描述的方案。要么你的项目太小以至于你把所有的东西都放在一个包中,要么你开始按照描述组织包。有关更多详细信息,请查看通用重用原则

包中的类可以一起重用。如果你重用一个包中的一个类,你就重用它们。

于 2009-03-22T15:43:04.547 回答
5

我不认为像你描述的那样的循环依赖一定是坏的。只要相互依赖的概念处于相同的抽象级别并且与架构的相同部分相关,则可能没有必要将它们相互隐藏。在我的理解中,神经元和连接符合这个要求。

减少这种耦合的一个常见方法是提取接口,甚至可能将它们放在一个单独的模块中。简单地在单个项目中按包进行组织并不能充分隐藏实现细节。允许您真正隐藏实现的常见模式如下:

客户端代码---->接口<---实现

在这种模式中,您从客户端代码中隐藏了“实现”模块,这意味着“客户端代码”模块中的代码甚至看不到实现代码。

包的嵌套有几个目的: 一些项目可能有一个以包组织的域模型。在这种情况下,包反映了域的某些分组,并且引用可能会向上/向下包。当涉及到诸如服务实现之类的事情时,您建议的模式很常见,而且是一件好事。您获得的包层次结构越深,该类被认为越具体。

于 2009-03-22T12:52:30.277 回答
5

我们在谈论什么样的代码大小?如果你只有 10-20 个类,你可能不需要(也不应该)仅仅为了它而将代码过度组织到包中。

随着项目的增长,您要做的第一个区别是将用户界面代码与底层数据模型和逻辑分开。为了能够进行适当的单元测试,拥有干净分离的层是至关重要的。

如果您在摆脱循环依赖方面遇到困难,则可能是这些类实际上是相互依赖的,并且应该驻留在同一个包中。

在设计整体代码结构时,正确设置抽象层可能是最重要的方面之一。

于 2009-03-22T12:53:27.137 回答
5

你如何处理这些情况?

循环依赖本质上并不是坏事。事实上,这有时可能是“治疗不如疾病”的情况:提取接口会增加代码的复杂程度并增加另一层间接性。对于非常简单的关系,这可能不值得。

于 2009-03-22T12:55:41.813 回答