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 MyAwesomeClass
figure 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 MyAwesomeClass
probablement 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
, D4
que vous devez refactoriser utiliser moins. Lorsque nous identifions les méthodes qui font C
appel 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 D1
et D2
sont 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 D4
besoins 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 D1
pourrait faire référence à D2
.
Group 2
- L’autre responsabilité a besoin d’un objet pour en appeler un autre. Vous ne pouvez pas D4
gérer D3
au lieu de votre classe? Ensuite, nous pouvons probablement éliminer D3
de la classe C
en laissant D4
faire 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 Customer
classe, vous pouvez toujours grouper ses propriétés dans d'autres classes pour simplifier les choses. Par exemple, une Address
classe avec des rues et une Zipcode
classe 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 .