Solo perché un sistema è complesso non significa che devi renderlo complicato . Se hai una classe con troppe dipendenze (o collaboratori) come questa:
public class MyAwesomeClass {
public class MyAwesomeClass(IDependency1 _d1, IDependency2 _d2, ... , IDependency20 _d20) {
// Assign it all
}
}
... poi è diventato troppo complicato e non stai seguendo veramente SRP , vero? Scommetto che se scrivessi cosa MyAwesomeClass
fa su una scheda CRC non andrebbe bene su una scheda indice o devi scrivere in lettere minuscole illeggibili.
Quello che hai qui è che i tuoi ragazzi hanno solo seguito il principio di segregazione dell'interfaccia e potrebbero averlo portato ad un estremo ma questa è tutta un'altra storia. Si potrebbe sostenere che le dipendenze sono oggetti di dominio (cosa che accade), tuttavia avere una classe che gestisce 20 oggetti di dominio allo stesso tempo lo sta allungando un po 'troppo.
TDD ti fornirà un buon indicatore di quanto fa una classe. Mettere senza mezzi termini; se un metodo di test ha un codice di installazione che richiede sempre una scrittura (anche se si refactoring i test), il MyAwesomeClass
probabilmente ha troppe cose da fare.
Quindi, come risolvi questo enigma? Sposti le responsabilità ad altre classi. Ci sono alcuni passaggi che puoi seguire in una classe che presenta questo problema:
- Identifica tutte le azioni (o responsabilità) che la tua classe fa con le sue dipendenze.
- Raggruppa le azioni in base a dipendenze strettamente correlate.
-
Redelegate! I.e. refactoring ciascuna delle azioni identificate a nuove o (più importante) altre classi.
Un esempio astratto sulle responsabilità di refactoring
Lascia che C
sia una classe che ha diverse dipendenze D1
, D2
, D3
, D4
che devi rifattorizzare per usare meno. Quando identifichiamo quali metodi che C
chiama sulle dipendenze, possiamo fare un semplice elenco di essi:
-
D1
- performA(D2)
, performB()
-
D2
- performD(D1)
-
D3
- performE()
-
D4
- performF(D3)
Guardando l'elenco possiamo vedere che D1
e D2
sono correlati l'uno all'altro in quanto la classe li richiede in qualche modo. Possiamo anche vedere che D4
ha bisogno di D3
. Quindi abbiamo due gruppi:
-
Group 1
- D1
< - > %codice%
-
D2
- Group 2
- > %codice%
I raggruppamenti sono un indicatore del fatto che la classe ora ha due responsabilità.
-
D4
- Uno per gestire i due oggetti chiamanti che hanno bisogno l'uno dell'altro. Forse puoi lasciare che la tua classe D3
elimini la necessità di gestire entrambe le dipendenze e lasciare che uno di loro gestisca quelle chiamate. In questo raggruppamento, è ovvio che Group 1
potrebbe avere un riferimento a C
.
-
D1
- L'altra responsabilità ha bisogno di un oggetto per chiamarne un altro. Impossibile D2
gestire Group 2
invece della tua classe? Quindi probabilmente possiamo eliminare D4
dalla classe D3
lasciando che D3
faccia invece le chiamate.
Non prendere la mia risposta come se fossi scolpito nella pietra, dato che l'esempio è molto astratto e fa molte supposizioni. Sono abbastanza sicuro che ci siano altri modi per refactoring, ma almeno i passaggi potrebbero aiutarti a ottenere una sorta di processo per spostare le responsabilità in giro invece di dividere le classi.
Modifica:
Tra i commenti @Emmad Karem dice:
"If your class has 20 parameters in the constructor, it doesn't sound like your team quite knows what SRP is. If you have a class that does only one thing, how does it have 20 dependencies? " - I think that If you have a Customer class, it is not strange to have 20 parameters in the constructor.
È vero che gli oggetti DAO tendono ad avere molti parametri, che devi impostare nel costruttore e i parametri sono in genere semplici tipi come stringa. Tuttavia, nell'esempio di una classe C
, puoi comunque raggruppare le sue proprietà all'interno di altre classi per semplificare le cose. Ad esempio avere una classe D4
con strade e una classe Customer
che contiene il codice di avviamento postale e gestirà la logica aziendale come la convalida dei dati:
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
}
}
Questa cosa è discussa ulteriormente nel post del blog "Mai, mai, mai usare String in Java (o almeno spesso)" . In alternativa all'utilizzo di costruttori o metodi statici per semplificare la creazione degli oggetti secondari, è possibile utilizzare un modello di generatore di fluidi .