如何删除同一包中两个类之间的循环依赖关系?

时间:2019-06-21 14:38:44

标签: java design-patterns circular-dependency

我们正在做一些重构,我们在这里遇到了麻烦。 有两个服务类AService和BService做不同的工作具有循环依赖关系。 先前它们位于不同的库中,因此不存在循环依赖关系。但是现在,在重构之后,我们已经将它们移到一个库中并移到一个包中。 AService的工作是将一些数据持久保存在NoSQL数据库中,以用于单独的用例。 BService作业与AService作业完全不同。 AService需要BService来获取一些数据。 BService需要AService才能将一些数据写入NoSQL数据库并进行回读。

如何通过使彼此依赖来解决循环依赖问题。有针对此类问题的设计模式吗?

class AService {
@Autowired
private BService bService;

}

class BService {
@Autowired
private AService aService;

}

4 个答案:

答案 0 :(得分:3)

真正的问题是,为什么您的构想使A和B相互依赖?

  • A和B可能代表相同的概念,并且可以合并。
  • 也有可能您的服务A或B的一部分应提取到新的服务C中。

在最后一种情况下,您需要知道哪种方法取决于哪种服务,以及哪种可以在新服务中提取。没有上下文和方法列表,很难提出解决方案。 对于间接递归方法,您可能还必须削减一些方法。

旁注:不要试图找到一种解决方法以使其与循环依赖项一起使用,它显示了概念问题,您必须进行重构。

答案 1 :(得分:1)

解决方案1(推荐): 重新设计班级职责。遵循“单一责任原则”,并根据您揭示的课程详细信息,我们可以通过提取至少一个新课程:DatabaseConnector来修正您的课程设计。此类封装了所有与数据库相关的操作(CRUD),因此提升了服务类的循环依赖关系(无需更改类的原始概念)。

// This is just a raw template to express the intention
class DatabaseConnector {
  void createInDatabase(Object data) {
  }

  Object readFromDatabase(Object args) {
  }

  void updateInDatabase(Object data) {
  }

  void deleteFromDatabase(Object data) {
  }
}

class AService {
  @Autowired
  private DatabaseConnector dbConnector;    
}

class BService {
  @Autowired
  private DatabaseConnector dbConnector;    
}

您可以向DatabaseConnector添加更多专门的方法以满足特殊要求(例如readName()readId()等)。

由于将来可能需要更多的类来访问数据库,因此您今天已经解决或阻止了新的循环依赖。封装解决了潜在的即将到来的问题。

解决方案2:依赖项倒置

interface IAService {    
}

interface BService {
}

class AService implements IAService {
  @Autowired
  private IBService bService;    
}

class BService implements IBService {
  @Autowired
  private IAService aService;    
}

循环依赖始终是不良类设计的指标。在大多数情况下,起源是违反单一责任原则(SOLID中的 S )。错误的构图概念也可能导致此设计错误。引入接口以反转所有依赖关系(SOLID中的 D )将始终有帮助,但不能解决类责任的概念缺陷。认真对待SOLID Principles可以节省大量时间和工作,并且总会带来更好的代码(尽管您引入了更高的代码复杂性)。

中介器模式还可以通过封装两个或多个对象的双向交互来帮助解除循环依赖。

当前代码的缺点是(除了循环依赖关系之外),每当类A更改以及数据持久性更改时,您都必须触摸和修改类B。此更改可能破坏使用相同持久性操作的类B。 。对于一个班级与另一班级分担责任的所有情况,都是如此。如果没有共享的代码,则这两个类将根本不认识。在特殊情况下,依赖关系是循环的,您也会在另一个依赖关系方向上添加此缺陷:每当B需要调整或扩展读取数据的方式时,都必须修改类A,这可能会破坏A。使用单元测试,那么您还必须重构两个类的测试。 A和B的这种紧密(循环)耦合将导致错误或错误。扩展代码变得很危险。但是好消息是循环依赖项永远不会编译(因为依赖项解析会导致无限递归)。

答案 2 :(得分:0)

最简单的方法是将服务B使用的方法移出服务A并移入服务B或移到完全不同的类中。

该设计表明,服务A中可能没有太多方法,而服务A实际上并未使用这些方法,但是这些方法实际上应该是静态的,并且完全在另一个类中是公用方法。

答案 3 :(得分:-3)

尝试将自动接线放置在设置器上:

Configure annotations