Решение 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;
}
Круговая зависимость всегда является показателем плохого проектирования классов.В большинстве случаев причиной является нарушение принципа единой ответственности ( S в SOLID).Неправильная концепция композиции также может привести к этой ошибке проектирования.Что всегда поможет, но не исправит концептуальные недостатки классовой ответственности, это введение интерфейсов для инвертирования всех зависимостей ( D в SOLID).Принятие серьезных принципов SOLID может сэкономить много времени и работы и всегда приведет к улучшению кода (хотя вы представили более высокую сложность кода).
Шаблон посредника также может помочь поднять циклические зависимости путем инкапсуляции двунаправленного взаимодействия.из двух или более объектов.
Недостатком вашего текущего кода является (помимо циклической зависимости), что всякий раз, когда изменяется класс A, а также изменяется постоянство данных, вы должны коснуться и изменить класс B. Эти изменения могут нарушитькласс B, который использует те же самые операции сохранения.Это верно для всех случаев, когда один класс имеет общие обязанности с другим классом.Если бы не было общего кода, оба класса не знали бы друг друга вообще.В вашем особом случае, когда зависимость циклическая, вы добавляете этот недостаток и к другому направлению зависимости: когда B нужно настроить или расширить способ чтения данных, вам придется изменить класс A, что может нарушить A. Если выиспользуя модульные тесты, вам придется провести рефакторинг тестов обоих классов тоже.Эта тесная (и циклическая) связь A и B приведет к ошибкам или ошибкам.Расширение кода стало опасным.Но хорошая новость заключается в том, что циклические зависимости никогда не компилируются (поскольку разрешение зависимости приводит к бесконечной рекурсии).