Table of Contents
Introduzione
Il Singolo Principio di Responsabilità (SRP) è uno dei cinque principi SOLID del design orientato agli oggetti, formulato da Robert C. Martin. Al suo centro, SRP afferma che ogni classe o modulo dovrebbe avere esattamente un motivo per cambiare. Quando una classe assume responsabilità multiple, le modifiche destinate a uno scopo possono introdurre bug nelle funzionalità non correlate.
Nonostante la sua semplicità, SRP è spesso violato nel codice del mondo reale. La pressione per la nave è rapida, combinata con i confini di dominio non chiari, spesso porta a classi di Dio[[]] che fanno tutto dall'accesso ai dati alla logica di presentazione. Questo articolo esplora i segni di narrativa delle violazioni SRP, strategie di rilevamento pratico e tecniche di rifattori provate per ripristinare la separazione pulita delle preoccupazioni.
Comprendere il principio di responsabilità unica
Che cosa è esattamente una responsabilità?
Secondo Martin, una responsabilità è un motivo per cambiare. Se si può descrivere una classe utilizzando più di un “e” – per esempio, “questa classe gestisce l’autenticazione e] logging” – probabilmente ha più responsabilità.
SRP non è una limitazione della dimensione della classe o di metodi di eliminazione. Si tratta di garantire che ogni classe abbia un focus ben definito. Una grande classe con una responsabilità unica e coerente (ad esempio, una transazione aziendale complessa) è migliore di una piccola classe che si occupa di compiti non correlati. Il principio si allinea al concetto più ampio di alta coesione] – elementi all'interno di un modulo dovrebbero essere funzionali.
Perché SRP Matters
- Maintainability:[] Quando ogni classe ha un motivo per cambiare, le modifiche sono isolate.
- Testabilità:[[] Le classi di responsabilità singola sono più facili da testare in isolamento. È possibile mock dependencies senza dover impostare un contesto complesso che esercita comportamenti non correlati.
- Riusabilità:[] I componenti focalizzati possono essere riutilizzati in diverse parti del sistema o anche in altri progetti.
- Parallel Development:[] Le squadre possono lavorare su responsabilità separate contemporaneamente a conflitti di fusione minimi quando le classi sono chiaramente delineate.
Segni comuni di violazioni SRP
Le violazioni SRP si manifestano spesso attraverso odori di codice osservabili, che non sono prove assolute, ma suggeriscono fortemente che una classe abbia assunto troppe preoccupazioni.
1. Classi complesse e grandi
Una classe che abbraccia centinaia o migliaia di linee, contiene molti campi e metodi, e ha un'alta complessità ciclomatica è un candidato principale per la violazione di SRP. Quando si apre un file e si vede un mix di accesso ai dati, regole aziendali, logica dell'interfaccia utente e gestione degli errori, la classe sta quasi certamente facendo più di una cosa. Ad esempio, un ] che entrambi interroga il database, convalida password, invia meno e-mail di conferma.
2. Motivi distinzione multipli per cambiare
Chiediti: “Che cosa potrebbe causare la modifica di questa classe?” Se l’elenco include più di una ragione non correlata – un nuovo schema di database, un cambiamento nella formattazione delle email, un quadro di registrazione diverso – allora la classe viola SRP. Ogni ragione dovrebbe corrispondere a una preoccupazione separata che dovrebbe essere incapsulata nella sua classe.
3. Duplicazione del codice tra i metodi
Quando la stessa logica appare in più metodi all'interno della stessa classe, spesso indica che questi metodi appartengono a responsabilità diverse. Ad esempio, se entrambi i metodi "salva" e "esporta" contengono codice di validazione identico, che la validazione è una responsabilità separata che deve essere estratta nella propria classe di validatore.
4. Test di unità difficili o impossibili
Se si scrive un test unitario per una classe richiede l'impostazione di un dispositivo elaborato – il mocking di un database, un file system, un server e-mail e un API di terze parti – la classe è probabile che si tratti di troppe responsabilità. Un vero test unitario dovrebbe essere in grado di testare un singolo comportamento prendendo solo una o due dipendenze.
5. Modifiche frequenti e imprevedibili
Le classi che vengono modificate ogni iterazione, spesso per diversi motivi, soffrono di “cambiamento accoppiamento”. Un cambiamento a una caratteristica colpisce accidentalmente un altro. Questa instabilità è un segno distintivo delle violazioni SRP. Traccia la cronologia delle versioni dei file; se una singola classe appare in molti commit che affrontano diverse storie degli utenti, è una bandiera rossa.
6. Liste lunghe del parametro o metodi di setter eccessivi
Le classi che hanno bisogno di molti parametri da configurare prima dell'uso spesso indicano che stanno cercando di gestire più contesti. Allo stesso modo, una classe con numerosi metodi di setter pubblici che devono essere chiamati in un ordine specifico (accoppiamento temporale) suggerisce che diverse responsabilità sono mescolate insieme.
Strategie per identificare le violazioni
Codice manuale Recensioni con una lista di controllo
Se il team non può concordare su una risposta concisa, la classe probabilmente ha bisogno di dividersi. Utilizzare una lista di controllo che include i segni sopra. Incoraggiare i recensori per contrassegnare qualsiasi metodo che sembra “fuori posto” – ad esempio, un metodo che esegue la rete I/O all’interno di una classe principalmente interessata alla trasformazione dei dati.
Strumenti di analisi del codice statico
Gli strumenti automatizzati possono rilevare molti odori SRP con regole configurabili. Ecco alcune metriche e strumenti da considerare:
- Complessità cyclomatica:[] I metodi con alta complessità spesso segnalano responsabilità multiple. Strumenti come SonarQube[]] funzioni di bandiera che superano una soglia (ad esempio, 10).
- Mancanza di coesione dei metodi (LCOM):[] Questo misura quanti metodi condividono i campi. Un alto valore LCOM indica che la classe contiene in realtà diversi gruppi di metodi che operano su dati diversi – una chiara violazione SRP. Molti analizzatori statici, tra cui PMD, segnalano LCOM.
- Class Fan-Out:[] Se una classe dipende da molte altre classi non correlate, può essere orchestrare troppe responsabilità. SonarQube può misurare “un giunto afferente” e “un giunto diverso”.
- Detezione della duplicazione del codice:[ Strumenti come [Simian o il rivelatore di duplicazione incorporato in SonarQube può evidenziare blocchi ripetuti che dovrebbero essere estratti.
Analisi del grafico a dipendenza
Se una classe di utilità di basso livello ha dipendenze su logica aziendale di alto livello (un ciclo di dipendenza o un “hub” con molte connessioni), SRP è probabilmente rotto. Strumenti come Structure101 o NDepend aiutano a vedere queste relazioni.
Rimborsamento di funzioni a secco
Prima di apportare modifiche, prova a riconfigurare mentalmente una classe sospetta. Identificare gruppi distinti di metodi e campi che sembrano appartenere insieme. Se si può nominare ogni gruppo con un solo sostantivo (ad esempio, “ReportFormatter”, “EmailSender”, “DatabaseAccessor”), allora la classe originale aveva molteplici responsabilità.
Rifattore per Ripristinare SRP
Una volta individuata una violazione, l'obiettivo è quello di decomporre la classe in classi più piccole e focalizzate, preservando il comportamento esistente.
Classe di estrazione
La tecnica più diretta: creare una nuova classe per ogni responsabilità identificata e spostare i metodi e i campi pertinenti in esso. La classe originale diventa poi una facciata che delega le nuove classi. Nel tempo, è possibile rimuovere la facciata e lasciare che i clienti interagiscano direttamente con le classi più piccole. Ad esempio, se un gestisce sia l'autenticazione che gli aggiornamenti del profilo, estrae e ]]]].
Sostituire condizionale con il polimorfismo
Quando una classe ha molte o ] affermazioni che selezionano il comportamento in base a un tipo o a una modalità, queste condizioni spesso rappresentano responsabilità diverse.
Comunicazione e elaborazione separate
Le classi che entrambi calcolano un risultato e lo inviano tramite un canale (rete, file, UI) stanno violando SRP. Il calcolo e la comunicazione sono due responsabilità separate. Utilizzare il modello Command/Query: un oggetto di servizio esegue il calcolo, e un presentatore separato o un mittente gestisce l'output.
Introdurre un sistema di eventi
Quando una classe ha bisogno di attivare gli effetti collaterali (logging, notifica, auditing) dopo un core operazione, SRP suggerisce di spostare gli effetti collaterali fuori. Un approccio orientato agli eventi consente alla classe core di pubblicare un evento, e i gestori separati sono responsabili per il log, e-mailing, ecc Questo mantiene la classe di base concentrata sulla sua logica aziendale primaria.
Esempio pratico: Refactoring a ReportGenerator Class
Considerate una classe che:
- Rettifiche di dati da un database
- Formatta i dati in HTML
- Invia il report a un elenco di destinatari
- Logs lo stato di invio a un file
Nel tempo, ogni responsabilità cambia per diversi motivi: nuove fonti di dati, nuovi formati di output, nuovi provider di posta elettronica, nuovi standard di registrazione. La classe diventa fragile.
Passo 1: Identificare le responsabilità
Elenca le ragioni per cambiare: modifiche dello schema del database, requisiti di formattazione, logica di consegna e-mail, formato di registrazione.
Fase 2: Estrarre l'accesso ai dati
Crea una classe che gestisce la querying. Spostare la connessione del database e la logica della query in esso. L'originale delegati a questo repository.
Passo 3: Estrarre la formattazione
Creare una classe che prende dati grezzi e restituisce una stringa HTML. ora chiama il repository per ottenere i dati, quindi il formatore per generare HTML.
Passo 4: Estrarre e-mail Inviare
Crea una classe responsabile per la preparazione e l'invio di messaggi e-mail. Questa classe dipende da una configurazione del server e-mail, non dai dati di report o dalla formattazione.
Passo 5: Estrarre logging
Creare un (o utilizzare un framework di registrazione standard) per registrare i risultati. Il servizio di posta elettronica potrebbe chiamare il logger, ma meglio ancora, utilizzare un evento: dopo aver inviato con successo, aumentare un evento che un gestore di registro separato raccoglie.
Risultato
L'originale diventa un coordinatore (o viene eliminato interamente). Ogni nuova classe è piccola, testabile e ha un solo motivo per cambiare. Ad esempio, è possibile un test unitario senza un database o un server di posta elettronica.
Strumenti e metriche per la vigilanza in corso
Integra il rilevamento SRP nel tuo pipeline di integrazione continua. Utilizza le seguenti metriche per monitorare le tendenze di qualità del codice:
- Complessità fisica per metodo:[] Mirare ai valori di meno di 10 per la maggior parte dei metodi.
- La profondità dell'albero dell'eritance (DIT): L'eredità molto profonda può nascondere responsabilità mista. Preferire la composizione sull'eredità per mantenere le classi concentrate.
- LCOM (La mancanza di coesione dei metodi):[ Molti analizzatori statici lo calcolano. Un valore superiore a 0,5 (su una scala normalizzata) suggerisce che la classe dovrebbe essere divisa.
- Numero delle dipendenze dirette:[] Se una classe dipende da più di una manciata di tipi non correlati, probabilmente si coordina su molte responsabilità.
]SonarQube[]] fornisce una dashboard completa con stima del debito tecnico. NDepend per .NET offre grafici di dipendenza e regole di codice [[Fint:4]PhpMetrics] per PHP.
Pitfalls comuni in refactoring
Il rifattore a SRP non è senza rischi:
- Immagineering:[ Le classi di divisione possono creare prematuramente un'astrazione inutile. Una classe con una chiara responsabilità che raramente i cambiamenti potrebbero non avere bisogno di rifatto anche se ha due preoccupazioni interne.
- Indirizione aumentata:[ Troppe classi piccole possono rendere il sistema difficile da navigare. L'equilibrio è fondamentale – ogni classe dovrebbe avere un nome e uno scopo chiaro.
- Performance Preoccupazioni:[] L'estrazione delle responsabilità spesso aggiunge uno strato extra di delegazione. Profilo prima e dopo; la testata è solitamente trascurabile rispetto al guadagno di manutenzione.
- Rifattore incompleto:[] Lasciando dietro una facciata legacy che dipende ancora da molte classi, la sfida è stata sconfitta.
Conclusioni
Identificare e fissare le violazioni del principio di responsabilità unica è una disciplina continua che paga i dividendi nella qualità del software. Osservando per i segni – classi grandi, molteplici ragioni per cambiare, componenti difficili da testare – si possono prendere i problemi presto.
Come scrive Martin Fowler Rifacente: Migliorare il Design del Codice esistente[[]], “Qualsiasi sciocco può scrivere il codice che un computer può capire. Buon programmatori scrivere il codice che gli esseri umani possono capire.” L'aderenza a SRP è uno dei modi più efficaci per scrivere codice leggibile dall'uomo che è la prova del tempo.