0
votes

Comment supprimer une dépendance circulaire entre deux classes dans un même paquet?

Nous faisons un peu de refactoring et nous avons frappé le mur ici. Il existe deux classes de service Aserfice et BService fait différentes œuvres ont une dépendance circulaire. Auparavant, ils se trouvaient dans différentes bibliothèques, il n'y avait donc aucune dépendance circulaire existante. Mais maintenant, après avoir refactoring, nous les avons déplacés dans une seule bibliothèque dans un seul paquet. Le travail d'Aservision est de persister certaines données dans une base de données NOSQL pour un cas d'utilisation séparé. Bservice Job est complètement différent du travail d'Aserfice. Aserfice a besoin de BService pour obtenir des données. BService a besoin d'une assistance pour écrire des données à la base de données NOSQL et se lit en arrière.

Comment résoudre le problème de la dépendance circulaire avec la fabrication de chacun d'entre eux. Tout modèle de conception pour de tels problèmes? P>

class AService {
@Autowired
private BService bService;

}

class BService {
@Autowired
private AService aService;

}


2 commentaires

Peut-être pourriez-vous déplacer le code de service qui nécessite BService de service et d'avoir un service comme un service pour écrire dans le magasin de données.


Peut-être que vos services sont trop gros et font trop? Envisagez d'avoir des services plus petits mais plus simples.


4 Réponses :


-3
votes

Essayez de mettre la mise au point automatique sur Setter à la place:

@Autowired
public void setServiceB(ServiceB service){
 this.serviceB = service;
 }


1 commentaires

Cela pourrait corriger l'erreur du conteneur d'injection de dépendance (ressort), car les dépendances de réglage sont désormais satisfaites après l'instance est construite. Mais cela ne résout pas les défauts de conception du graphique de dépendance lui-même. Par conséquent, ce n'est pas une solution au problème initial, mais un hack pour tromper le cadre du CIO.



4
votes

La vraie question est la raison pour laquelle votre conception rendant A et B dépendent de chaque fois?

  • Cela pourrait être que A et B représentent le même concept et peuvent être fusionnés.
  • Cela pourrait également être qu'une partie de votre service A ou B doit être extraite dans un nouveau service C.

    Dans ce dernier cas, vous devez savoir quelle méthode dépend de quel service et que l'on pourrait être extrait dans un nouveau service. Sans avoir le contexte et la liste des méthodes, il est difficile de proposer une solution. Dans le cas des méthodes de récursives indirectes, vous devrez peut-être réduire certaines méthodes.

    Note latérale: N'essayez pas de trouver une solution de contournement pour le faire fonctionner avec des dépendances circulaires, il montre un problème de conception, vous devez faire le refactoring.


5 commentaires

Aservice et BService travaillent pour un cas d'utilisation séparé. Ils ne devraient pas être dans une seule bibliothèque, mais à cause d'un autre problème avec les appels de réseautage, nous devons les fusionner afin qu'ils puissent s'appeler mutuellement. Nous ne pouvons donc pas les fusionner comme concept est différent. L'extraction n'aurait pas de sens que les deux représentent des concepts différents.


@Avinashkharche Vous devez comprendre que le fait que plusieurs classes dépendent de l'autre indique qu'ils partageaient des responsabilités communes (elles se complètent) ou dépendent des informations partagées (opération). Vous devez évaluer votre conception de classe en utilisant le principe de responsabilité unique. Vous devrez réécrire vos cours et introduire de nouveaux. En regardant le motif de médiateur peut également vous aider.


@AvinashkHarche Vous avez raison que la fusion n'est pas la solution (principe de responsabilité unique). Mais l'extraction du code commun fait logique et n'est pas Changer de concepts. Dans votre cas particulier, tout ce que vous avez à faire est d'extraire les accès à la base de données à une nouvelle classe: Databaseconnector . Cette classe est responsable de la lecture et de la rédaction de la base de données et de l'écriture de toutes les opérations de CRUD. Maintenant, au lieu de cela, en fonction de l'autre, les deux services dépendent de Databasonnector ?


@AvinashkHarche Si vous refusez toujours d'accepter votre défaut de conception, vous pouvez introduire des interfaces pour soulever les dépendances circulaires. Rechercher un principe d'inversion de dépendance.


@AvinashkHarche Voir ma réponse.



0
votes

Le moyen le plus simple consiste à déplacer la méthode que le service B utilise hors service A et en service B ou dans une classe complètement différente.

La conception indique que vous avez probablement trop de méthodes de service A quel service A n'utilise pas réellement, mais qui sont des méthodes publiques qui devraient vraiment être statiques et dans une autre classe entièrement.


8 commentaires

Le déménagement de code partagé d'une classe à l'autre résoudra le problème mais n'améliore pas la conception. L'opération (s) qui ne fait pas partie de la classe d'une seule raison. L'ajout de cela ajoutera également la responsabilité qui n'était évidemment pas destinée. Cela peut également différer le problème cyclique jusqu'à ce que le code soit prolongé et que de nouvelles classes auront également besoin de cette fonctionnalité. Si vous le gardez réel avec le principe de responsabilité unique, vous êtes toujours en sécurité lors de l'introduction de nouvelles classes.


La réponse n'est donc pas "Faire du code statique" mais "encapsuler le code partagé (ou les responsabilités partagées)". L'encapsulation nécessiterait d'introduire une nouvelle classe qui prend soin de la responsabilité partagée.


C'est vrai, je suppose que vous pouvez également créer une classe abstraite qui est mise en œuvre par le service A et le service B et déplacer des méthodes courantes vers la classe abstraite.


Pour que les deux services héritent de cette classe de base abstraite? Cela introduirait de nouveaux problèmes et une relation entre les services qui ne sont pas naturels: un type de service est un connecteur de base de données. Il serait plus naturel de le concevoir comme: un service a ou utilise un connecteur de base de données. Composition sur l'héritage. De cette façon, vous pouvez modifier la mise en œuvre concrète du connecteur de base de données, même au moment de l'exécution. L'héritage nécessiterait de réécrire ou de modifier la classe de base (ES) pour échanger dans un connecteur différent. Vous devez ensuite ajuster chaque endroit dans le code où vous avez référencé l'ancienne classe de base.


J'entends ce que vous dites. Je suppose que j'ai mal compris le service A et le service B comme étant des classes similaires qui touchent la base de données SQL et qui ont des méthodes qui se chevauchent, mais que vous dites essentiellement qu'il devrait y avoir une seule classe appelée «sqldatabaseconnector» (ou quelque chose du genre) dans lequel Le service A et le service B utilise et que, si nécessaire, le service A et le service B pourrait remplacer.


Oui les deux sont des services qui dépendent de la base de données. Il pourrait y avoir plus de services qui dépendent également de la base de données. Lorsque chaque service a sa propre implémentation pour accéder à la base de données, vous devez modifier beaucoup de code lorsque vous souhaitez migrer vers une base de données différente. Vous auriez aussi beaucoup de code dupliqué. Pour contourner ce problème, vous donneriez à toutes les classes de service un constructeur paramétré qui prendrait un idatabaseconnector , qu'ils peuvent utiliser sans connaître aucun détail sur la base de données elle-même.


Aujourd'hui, nous passons dans un sqldatabaseconnector , mais demain, nous pouvons facilement le changer en mysqldatabseconnector sans modifier les classes de service, car ils ne connaissent que idatabaseconnector , qui les deux connecteurs implémentent. L'héritage ne peut pas faire ça. Si chaque classe de service hérite de sqldatabaseconnector , vous devez modifier et redéployer chaque service pour créer hériter du mysqldatabaseconnector et modifier toutes les références de la classe de base et les tests d'unité. S'il y a plus de méthodes partagées à côté de l'accès à la base de données, j'introduisais une autre classe (ES) pour encapsuler ces méthodes.


Si vous ne connaissez pas le motif de référentiel, je vous recommande de lire à ce sujet. Il est parfait car il vous aide à découpler des services de la base de données sous-jacente.



1
votes

solution 1 (recommandé): strong> la refonte des responsabilités de la classe. Adhérant au principe de responsabilité unique et selon les détails de la classe que vous avez révélés, nous pouvons corriger votre conception de classe en extrayant au moins une nouvelle classe: le Databasonnector code>. Cette classe encapsule toutes les opérations liées à la base de données (CRUD) et soulève donc la dépendance circulaire des classes de service (sans modifier les conceptions originales des classes).

interface IAService {    
}

interface BService {
}

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

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


0 commentaires