如何组织包(并防止依赖循环)?

时间:2009-03-22 12:45:00

标签: java dependencies packages project-organization

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

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

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

另外,我读到包层次结构中更高层的包中的类不应该引用更深层的包中的类。这意味着包'nn'中的NeuralNetwork类不能引用包'nn.neurons'中的Neuron。你们遵循这个原则吗?如果我将NeuralNetwork转移到'nn.networks'或其他什么怎么办?在这种情况下,它将指代一个兄弟包而不是一个孩子。这是更好的做法吗?

5 个答案:

答案 0 :(得分:13)

antcontrib VerifyDesign task将帮助您按照自己的意愿行事:

  

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

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

并且只能自然地呈现   取决于业务包,和   业务应该依赖于dataaccess。   如果您以这种方式定义您的设计   违反了构建将失败   当verifydesign ant任务是   调用。例如,如果我创建了一个   biz.xsoftware.presentation中的类   那个班级取决于一个班级   biz.xsoftware.dataaccess,构建   会失败的。这确保了设计   实际上遵循记录的内容(to   至少在某种程度上)。这是   特别适合自动构建

因此,一旦您决定了应该如何组织事情,您就可以在编译时强制执行这些要求。您还可以获得精细控制,以便您可以允许某些情况打破这些“规则”。所以你可以允许一些周期。

根据您的工作方式,您可能会发现“utils”包有意义。

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

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

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

答案 1 :(得分:9)

首先,你是理所当然的,因为包之间的循环依赖是坏的。随之而来的问题随着项目的规模而变得越来越重要,但没有理由按时解决这种情况。

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

你说你把所有的神经元放在同一个包中。除非您重复使用所有实现,否则这两者都不正确。再次,看看我上面描述的方案。您的项目非常小,您可以将所有项目放在单个包中,或者按照描述开始组织包。 有关详细信息,请查看The Common Reuse Principle

包装中的课程一起重新使用。如果你 重新使用一个包中的一个类,你重新使用它们 ALL。

答案 2 :(得分:5)

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

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

客户代码---->接口< ---实施

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

包的嵌套有多种用途:某些项目可能具有以包的形式组织的域模型。在这种情况下,包反映了域的一些分组,并且引用可以上/下包。当涉及服务实施等事情时,您建议的模式非常普遍,并且是一件好事。包层次结构越深,您认为该类就越具体。

答案 3 :(得分:5)

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

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

如果你在摆脱循环依赖关系时遇到困难,那么这些类实际上可能是相互依赖的,并且应该位于同一个包中。

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

答案 4 :(得分:5)

  

你如何处理这些情况?

循环依赖本身并不坏。实际上,这有时可能是“治愈比疾病更糟糕”的情况:提取界面会增加代码的复杂程度并增加另一层间接性。 对于非常简单的关系来说,这可能不值得。