Ce n’est pas parce qu’un système est complexe que vous devez le rendre compliqué . Si vous avez une classe qui a trop de dépendances (ou de collaborateurs) comme celle-ci:
public class MyAwesomeClass {
public class MyAwesomeClass(IDependency1 _d1, IDependency2 _d2, ... , IDependency20 _d20) {
// Assign it all
}
}
... alors c'est devenu trop compliqué et vous ne suivez pas vraiment SRP , n'est-ce pas? Je parierais que si vous écrivez ce qui MyAwesomeClassfigure sur une carte CRC, cela ne rentre pas dans une fiche ou vous devez écrire en minuscules lettres illisibles.
Ce que vous avez ici, c'est que vos gars ont seulement suivi le principe de séparation des interfaces et l'ont peut-être poussé à l'extrême, mais c'est une toute autre histoire. Vous pourriez faire valoir que les dépendances sont des objets de domaine (ce qui se produit), mais le fait d’avoir une classe qui gère 20 objets de domaine en même temps l’étire un peu trop.
TDD vous fournira un bon indicateur de ce que fait une classe. Brutalement mis; Si une méthode de test a un code de configuration qui prend du temps à écrire (même si vous refactorisez les tests), vous avez MyAwesomeClassprobablement trop de choses à faire.
Alors, comment résolvez-vous cette énigme? Vous déplacez les responsabilités vers d'autres classes. Vous pouvez prendre certaines mesures pour un cours présentant ce problème:
- Identifiez toutes les actions (ou responsabilités) que votre classe fait avec ses dépendances.
- Regroupez les actions en fonction de dépendances étroitement liées.
- Redéléguer! C'est-à-dire refactoriser chacune des actions identifiées en nouvelles classes ou (plus important encore).
Un exemple abstrait sur les responsabilités de refactoring
Laissez - Cêtre une classe qui a plusieurs dépendances D1, D2, D3, D4que vous devez refactoriser utiliser moins. Lorsque nous identifions les méthodes qui font Cappel aux dépendances, nous pouvons en faire une simple liste:
D1- performA(D2),performB()
D2 - performD(D1)
D3 - performE()
D4 - performF(D3)
En regardant la liste, nous pouvons voir cela D1et D2sont liés les uns aux autres car la classe en a besoin d'une manière ou d'une autre. Nous pouvons également voir que les D4besoins D3. Nous avons donc deux groupes:
Group 1- D1<->D2
Group 2- D4->D3
Les regroupements indiquent que la classe a maintenant deux responsabilités.
Group 1- Un pour gérer l'appelant deux objets qui ont besoin l'un de l'autre. Vous pouvez peut-être laisser votre classe Céliminer le besoin de gérer les deux dépendances et laisser l’une d’elles gérer ces appels à la place. Dans ce groupe, il est évident que l'on D1pourrait faire référence à D2.
Group 2- L’autre responsabilité a besoin d’un objet pour en appeler un autre. Vous ne pouvez pas D4gérer D3au lieu de votre classe? Ensuite, nous pouvons probablement éliminer D3de la classe Cen laissant D4faire les appels à la place.
Ne prenez pas ma réponse comme une pierre dans le marbre, car cet exemple est très abstrait et repose sur de nombreuses hypothèses. Je suis à peu près sûr qu'il y a plus de moyens de reformuler cela, mais au moins, les étapes pourraient vous aider à mettre en place un processus permettant de transférer les responsabilités au lieu de diviser les classes.
Modifier:
Parmi les commentaires, @Emmad Karem dit:
"Si votre classe a 20 paramètres dans le constructeur, il ne semble pas que votre équipe sache ce qu'est SRP. Si vous avez une classe qui ne fait qu'une chose, en quoi a-t-elle 20 dépendances?" - Je pense que avoir une classe client, il n’est pas étrange d’avoir 20 paramètres dans le constructeur.
Il est vrai que les objets DAO ont généralement beaucoup de paramètres, que vous devez définir dans votre constructeur, et que ces paramètres sont généralement de types simples, tels que chaîne. Cependant, dans l'exemple d'une Customerclasse, vous pouvez toujours grouper ses propriétés dans d'autres classes pour simplifier les choses. Par exemple, une Addressclasse avec des rues et une Zipcodeclasse contenant le code postal et gérant la logique métier telle que la validation des données:
public class Address {
private String street1;
//...
private Zipcode zipcode;
// easy to extend
public bool isValid() {
return zipcode.isValid();
}
}
public class Zipcode {
private string zipcode;
public bool isValid() {
// return regex match that zipcode contains numbers
}
}
Ce sujet est traité plus en détail dans l'article de blog "Jamais, jamais, n'utilisez jamais String en Java (ou du moins souvent)" . Au lieu d'utiliser des constructeurs ou des méthodes statiques pour faciliter la création des sous-objets, vous pouvez utiliser un modèle de générateur de fluide .