简体   繁体   English

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

[英]How to organize packages (and prevent dependency cycles)?

I've been running some metrics on my Java project and apparently there are a lot of dependency cycles between packages. 我一直在我的Java项目上运行一些指标,显然包之间有很多依赖循环。 I didn't really know how to organize stuff into packages, so I just did what made sense to me, which is apparently wrong. 我真的不知道如何将东西组织成包裹,所以我只是做了对我有意义的事情,这显然是错误的。

My project is a neural network framework. 我的项目是一个神经网络框架。 Neural networks have Neurons, which are connected to each other with Connections. 神经网络具有神经元,它们通过连接相互连接。 They need to depend on each other. 他们需要相互依赖。 However, there are also different types of Neurons, so I thought it'd be a good idea to put them all in there own 'neurons' package. 然而,也有不同类型的神经元,所以我认为将它们全部放在自己的'神经元'包中是个好主意。 Obviously a Connection isn't a Neuron so it shouldn't be in the package, but since they refer to each other, I now have a circular dependency. 显然,Connection不是神经元,所以它不应该在包中,但由于它们彼此引用,我现在有一个循环依赖。

This is just an example, but I have more situations like this. 这只是一个例子,但我有更多这样的情况。 How do you handle these kinds of situations? 你如何处理这些情况?

Also, I read that classes in a package higher up in the package hierarchy are not supposed to refer to classes in packages that are deeper. 另外,我读到包层次结构中更高层的包中的类不应该引用更深层的包中的类。 This would mean that a NeuralNetwork class in package 'nn' can not refer to the Neuron in package 'nn.neurons'. 这意味着包'nn'中的NeuralNetwork类不能引用包'nn.neurons'中的Neuron。 Do you guys follow this principle? 你们遵循这个原则吗? And what if I would move NeuralNetwork to 'nn.networks' or something? 如果我将NeuralNetwork转移到'nn.networks'或其他什么怎么办? In that case, it would refer to a sibling package instead of a child. 在这种情况下,它将指代一个兄弟包而不是一个孩子。 Is that better practice? 这是更好的做法吗?

The antcontrib VerifyDesign task will help you do what you want: antcontrib VerifyDesign任务将帮助您做您想做的事:

For example, if there are three packages in one source tree 例如,如果一个源树中有三个包

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

and naturally presentation should only depend on business package, and business should depend on dataaccess. 自然呈现应该只依赖于业务包,业务应该依赖于dataaccess。 If you define your design this way and it is violated the build will fail when the verifydesign ant task is called. 如果以这种方式定义设计并且违反了,则在调用verifydesign ant任务时,构建将失败。 For example, if I created a class in biz.xsoftware.presentation and that class depended on a class in biz.xsoftware.dataaccess, the build would fail. 例如,如果我在biz.xsoftware.presentation中创建了一个类,并且该类依赖于biz.xsoftware.dataaccess中的类,则构建将失败。 This ensures the design actually follows what is documented(to some degree at least). 这确保了设计实际遵循记录的内容(至少在某种程度上)。 This is especially nice with automated builds 这对于自动构建尤其有用

So once you have decided how things should be organized you can enforce the requirements at compile time. 因此,一旦确定了应该如何组织事物,您就可以在编译时强制执行这些要求。 You also get fine-granied control so you can allow certain cases to break these "rules". 您还可以获得精细控制,以便您可以允许某些情况打破这些“规则”。 So you can allow some cycles. 所以你可以允许一些周期。

Depending on how you want to do things, you might find that "utils" package makes sense. 根据您的工作方式,您可能会发现“utils”包有意义。

For the particular case that you cite... I might do something like this: 对于你引用的特殊情况......我可能会这样做:

  • package nn contains Nueron and Connection package nn包含Nueron和Connection
  • package nn.neurons contains the subclasses of Nueron package nn.neurons包含Nueron的子类

Neuron and Connection are both high-level concepts used in the NeuralNetowrk, so putting them all together makes sense. Neuron和Connection都是NeuralNetowrk中使用的高级概念,因此将它们放在一起是有意义的。 The Neuron and Connection classes can refer to each other while the Connection class has no need to know about the Neuron subclasses. Neuron和Connection类可以互相引用,而Connection类不需要知道Neuron子类。

First of all, you are rightfully concerned because circular dependencies between packages are bad. 首先,你是理所当然的,因为包之间的循环依赖是坏的。 Problems that come out of it grow in importance with the size of the project, but no reason to tackle this situation on time. 随之而来的问题随着项目的规模而变得越来越重要,但没有理由按时解决这种情况。

You should organize your classes by placing classes that you reuse together in the same package. 您应该通过将在同一个包中一起重用的类放在一起来组织您的类。 So, if you have for example AbstractNeuron and AbstractConnection, you'd place them in the same package. 因此,如果您有例如AbstractNeuron和AbstractConnection,则将它们放在同一个包中。 If you now have implementations HumanNeuron and HumanConnection, you'd place these in the same package (called for example *.network.human). 如果您现在已经实现了HumanNeuron和HumanConnection,那么您可以将它们放在同一个包中(例如* .network.human)。 Or, you might have only one type of connection, for example BaseConnection and many different Neurons. 或者,您可能只有一种类型的连接,例如BaseConnection和许多不同的神经元。 The principle stays the same. 原则保持不变。 You place BaseConnection together with BaseNeuron. 您将BaseConnection与BaseNeuron一起放置。 HumanNeuron in its own package together with HumanSignal etc. VirtualNeuron together with VirtualSignal etc. You say: “Obviously a Connection isn't a Neuron so it shouldn't be in the package..”. HumanNeuron与HumanSignal以及VirtualSignal等一起使用自己的包。您说:“显然连接不是神经元,所以它不应该在包中......”。 This is not that obvious, nor correct to be exact. 这不是那么明显,也不准确。

You say you placed all your neurons in the same package. 你说你把所有的神经元放在同一个包里。 Neither this is correct, unless you reuse all your implementations together. 除非您重复使用所有实现,否则这两者都不正确。 Again, take a look at scheme I described above. 再次,看看我上面描述的方案。 Either your project is so small you place all in the single package, or you start organizing packages as described. 您的项目非常小,您可以将所有项目放在单个包中,或者按照描述开始组织包。 For more details take a look at The Common Reuse Principle : 有关更多详细信息,请参阅共同重用原则

THE CLASSES IN A PACKAGE ARE REUSED TOGETHER. 包装中的类别一起重新使用。 IF YOU REUSE ONE OF THE CLASSES IN A PACKAGE, YOU REUSE THEM ALL. 如果你重新使用一个套餐中的一个,你可以重新使用它们。

I do not think cyclic dependencies like the ones you describe have to be bad. 我不认为像你描述的那样的循环依赖必须是坏的。 As long as the concepts that are interdependent are at the same level of abstraction and relate to the same parts of the architecture, it may not be necessary to hide these from each other. 只要相互依赖的概念处于相同的抽象级别并且与架构的相同部分相关,则可能没有必要将它们彼此隐藏。 Neurons and Connections fit this bill in my understanding. 神经元和连接符合我的理解。

A common to reduce such couplings is to extract interfaces, and possibly even put these in a separate module. 减少这种耦合的一个常见方法是提取接口,甚至可能将它们放在一个单独的模块中。 Simply organizing by packages inside a single project does not allow you to hide implementation details sufficiently. 简单地通过单个项目内的包进行组织不允许您充分隐藏实现细节。 A common pattern that allows you to really hide implementations is as follows: 允许您真正隐藏实现的常见模式如下:

Client Code ----> Interfaces <--- Implementation 客户端代码---->接口<---实现

In this pattern, you hide the "Implementation" module from the client code, which means the code in the "Client code" module doesn't even see the implementation code. 在此模式中,您从客户端代码隐藏“实现”模块,这意味着“客户端代码”模块中的代码甚至看不到实现代码。

The nesting of packages serves several purposes: Some projects may have a domain model which is organized in packages. 包的嵌套有多种用途:某些项目可能具有以包的形式组织的域模型。 In this case the packages reflect some grouping of the domain, and references may go up/down packages. 在这种情况下,包反映了域的一些分组,并且引用可以上/下包。 When it comes to things like implementation of services, your suggested pattern is quite common and a good thing to follow. 当涉及服务实施等事情时,您建议的模式非常普遍,并且是一件好事。 The deeper in the package hierarchy you get the more specific the class is believed to be. 包层次结构越深,您认为该类就越具体。

What kind of code size are we talking about? 我们在谈论什么样的代码大小? If you only have 10-20 classes, you probably don't need to (and shouldn't) over-organize your code into packages just for the sake of it. 如果您只有10-20个类,那么您可能不需要(也不应该)将代码过度组织到包中,只是为了它。

As your project grows, the first distinction you want to make is to separate user-interface code from the underlying data model and the logic. 随着项目的增长,您要做的第一个区别是将用户界面代码与底层数据模型和逻辑分开。 Having cleanly separated layers is crucial in order to be able to do proper unit testing. 清洁分离的层对于能够进行适当的单元测试至关重要。

If you're having trouble in getting rid of the circular dependencies, it is probably the case the the classes are actually interdependent, and should reside in the same package. 如果您在摆脱循环依赖关系时遇到困难,那么类可能就是这些类实际上是相互依赖的,并且应该位于同一个包中。

Getting the abstraction layers right is probably one of the most important aspects when designing the overall code structure. 在设计整体代码结构时,获得正确的抽象层可能是最重要的方面之一。

How do you handle these kinds of situations? 你如何处理这些情况?

Circular dependencies aren't inherently bad. 循环依赖本身并不坏。 In fact, this can sometimes be a case of the "cure being worse than the disease": extracting an interface increases the level of complexity of your code and adds another layer of indirection. 实际上,这有时可能是“治愈比疾病更糟糕”的情况:提取界面会增加代码的复杂程度并增加另一层间接性。 That's probably not worth it for very simple relationships. 对于非常简单的关系来说,这可能不值得。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在gwt中组织软件包? - How to organize packages in gwt? 如何在没有包的情况下组织 Java 项目? - How to organize Java-Project without packages? 如何在包装之间用空白组织进口? - How to organize imports with blanks between packages? 如何在Java中正确组织和嵌套软件包 - How to properly organize and nest packages in Java 如何正确组织一组在不同包中实现接口的具体类? - How to correctly organize a set of concrete classes implementing an interface in different packages? 在Eclipse中组织Maven依赖 - Organize Maven Dependency in eclipse 如何防止循环绑定依赖 - How to prevent circular binding dependency Eclipse将包组织到文件夹层次结构中 - Eclipse Organize Packages into Folder Hierarchy 它是什么意思以及如何修复SonarQube Java问题“应该删除包之间的循环”(squid:CycleBetweenPackages) - What does it mean and how to fix SonarQube Java issue “Cycles between packages should be removed” (squid:CycleBetweenPackages) 如何更好地组织框架中的类/包,以便我的应用程序的客户端可以轻松扩展它们? - How to better organize classes/packages in a framework so that the client of my application can easily extend them?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM