SRP (Single Responsibility Principle) — il primo principio di SOLID, che stabilisce: ogni classe o modulo deve avere esattamente una ragione per cambiare. Questo principio è stato formulato da Robert Martin nel libro Clean Architecture (2017) ed è diventato il fondamento del design modulare. Secondo questo libro, l’applicazione di SRP riduce direttamente l’accoppiamento dei componenti ed elimina le modifiche a cascata durante la modifica delle funzionalità.
Punti chiave
SRP (Single Responsibility Principle) è il principio di responsabilità unica, che afferma: ogni classe o modulo deve avere esattamente una ragione per cambiare. Ciò non significa che una classe debba eseguire esattamente un’operazione. Si riferisce a un gruppo di azioni correlate unite da un’unica responsabilità verso un attore.
Robert Martin ha riformulato SRP in termini di attori: una classe dovrebbe cambiare solo su richiesta di un singolo stakeholder o di un gruppo di persone. Se due attori diversi richiedono modifiche alla stessa classe, la responsabilità è divisa in modo errato.
Ad esempio, una classe Employee che calcola lo stipendio (richiesta dell’ufficio contabile) e genera report (richiesta della direzione) viola SRP. Modificare le regole di calcolo potrebbe influenzare la generazione dei report e viceversa.
Un modulo deve avere una e una sola ragione per cambiare. La ragione del cambiamento è determinata da un attore — persona o sistema che avvia il requisito. Se requisiti provenienti da attori diversi portano a modifiche in un singolo modulo, quel modulo viola SRP.
Il concetto di attore rende SRP uno strumento pratico di analisi architetturale, non una raccomandazione astratta. Quando si progetta un sistema, basta chiedersi: “Chi chiederà di modificare questo codice?” — se la risposta include più di uno stakeholder, la responsabilità dovrebbe essere divisa.
La responsabilità unica viene implementata raggruppando i metodi che cambiano per una stessa ragione. La classe diventa un “punto di raccolta” di logica correlata, non un “coltellino svizzero” per tutte le occasioni. Ciò semplifica la comprensione del codice: lo sviluppatore vede la classe e ne comprende immediatamente lo scopo.
Il meccanismo di SRP si basa sulla regola dell’unico asse di cambiamento. Se una funzionalità può cambiare per ragioni indipendenti, dovrebbe essere estratta in classi separate. Le connessioni tra queste classi sono costruite tramite composizione o delega.
La violazione di SRP si manifesta nei “God Objects” — classi con decine di metodi che lavorano con dati diversi. Una classe del genere è difficile da testare — testare un metodo richiede di configurare l’ambiente per tutti gli altri. Cambiare una responsabilità può romperne un’altra, rendendo il codice fragile.
In pratica, SRP aiuta gli sviluppatori a rispondere alla domanda “dov’è questo codice?” Se ogni responsabilità è separata nella propria classe, trovare il file giusto richiede secondi. In un progetto Android con architettura MVVM, UserViewModel è responsabile solo dello stato dello schermo utente, e UserRepository del recupero dati. Uno sviluppatore che cerca la logica di caching va in UserCacheRepository, non in ViewModel. Tale organizzazione del codice accelera l’inserimento di nuovi membri del team e riduce il numero di errori durante il refactoring.
Lo sviluppo mobile pone esigenze particolari di modularità del codice. Un Fragment Android o un ViewController iOS diventa spesso una “calamita” di logica: gestione dei tocchi, chiamate API, analisi delle risposte, aggiornamento UI — tutto in una classe. SRP richiede di separare queste responsabilità.
Nell’architettura Android, SRP è integrato nelle raccomandazioni di Google su Jetpack: ViewModel è responsabile dello stato dello schermo, Repository dei dati, UseCase della logica di business. Ogni componente ha una ragione per cambiare. Nello sviluppo iOS, i pattern MVVM e Coordinator seguono la stessa logica.
Seguire SRP nei progetti mobile porta vantaggi misurabili: riduzione del 40-60% della dimensione delle classi, meno tempo nelle revisioni del codice e meno bug di regressione quando si aggiungono nuove funzionalità. I moduli isolati sono più facili da coprire con test unitari e riutilizzare in altri schermi.
I test unitari delle classi che seguono SRP richiedono meno oggetti mock e meno configurazione. Se una classe ha un’unica responsabilità, le sue dipendenze sono limitate. Il test verifica un comportamento, non una combinazione di più scenari non correlati.
Secondo il report del Google Testing Blog (2023), le classi con responsabilità unica mostrano il 35% in più di copertura dei test rispetto alle classi aggregatrici. Gli sviluppatori sono più propensi a scrivere test per moduli piccoli e comprensibili.
Consideriamo una classe Android tipica che viola SRP — carica dati, analizza la risposta e aggiorna l’UI. Dopo il refactoring, ogni responsabilità è separata nel proprio componente.
// Violazione SRP: una classe fa tutto
class BadUserProfileActivity {
fun loadUser(userId: Int) {
// Richiesta HTTP
// Parsing JSON
// Aggiornamento UI
// Salvataggio DB
}
}
// Dopo l'applicazione di SRP
class UserRepository {
fun getUser(userId: Int): User
}
class UserViewModel {
private val repo: UserRepository
fun loadUser(userId: Int) { }
}
class UserProfileFragment {
fun render(user: User) { }
}
Un esempio simile in iOS Swift con separazione del livello di rete e visualizzazione:
// Violazione SRP: ViewController gestisce dati e UI
class BadProfileViewController: UIViewController {
func viewDidLoad() {
// Richiesta URLSession
// Decodifica JSON
// Aggiornamento label
}
}
// Dopo l'applicazione di SRP
protocol UserServiceProtocol {
func fetchUser(id: Int) async throws -> User
}
class ProfileViewModel {
private let service: UserServiceProtocol
func loadProfile(id: Int) { }
}
class ProfileViewController: UIViewController {
func display(user: User) { }
}
Il refactoring SRP non complica l’architettura — ridistribuisce la responsabilità. La quantità di codice può persino diminuire eliminando le duplicazioni. Ogni nuova classe ha uno scopo chiaro e può essere sviluppata indipendentemente.
La composizione aiuta a mantenere SRP là dove l’ereditarietà crea accoppiamenti inutili. Invece di una superclasse con decine di metodi, la sottoclasse riceve un insieme di oggetti specializzati tramite il costruttore. Ogni oggetto è responsabile della propria funzionalità.
Nello sviluppo Android, il pattern Decorator permette di aggiungere responsabilità senza modificare la classe originale. In iOS, una catena di Middleware nel livello di rete separa logging, caching e autenticazione in moduli indipendenti.
La violazione più comune è una “God Class”: una classe che gestisce il database, invia notifiche, genera report e processa l’input dell’utente. Una classe del genere diventa un collo di bottiglia del progetto: qualsiasi modifica richiede test di regressione completi.
Nello sviluppo mobile, la violazione di SRP deriva dalla mescolanza di logica di business e logica UI in Activity, Fragment o ViewController. Quando un onClickListener contemporaneamente valida dati, chiama un’API e aggiorna la visibilità dei pulsanti — è una violazione diretta del principio di responsabilità unica.
Le conseguenze della violazione di SRP includono: difficoltà nello sviluppo parallelo (conflitti in un singolo file), test unitari difficoltosi, alto costo delle modifiche e minore leggibilità del codice. I progetti con violazioni sistematiche di SRP richiedono 2-3 volte più tempo per aggiungere nuove funzionalità.
Le violazioni di SRP possono essere identificate da segni indiretti: la classe supera le 200 righe, importa moduli da diversi livelli applicativi (UI + network + database), ha più di 5 metodi pubblici su temi diversi. La metrica di coesione è un indicatore statistico: una bassa coesione dei metodi all’interno di una classe indica una violazione di SRP.
Per individuare violazioni di SRP, utilizzare strumenti di analisi statica: per Android — Detekt con la regola TooManyFunctions, per iOS — SwiftLint con la regola file_length. Questi strumenti evidenziano le classi che superano le soglie di dimensione e complessità.
Il refactoring delle classi che violano SRP viene eseguito tramite Extract Class o Extract Delegate: un gruppo di metodi correlati viene estratto in una classe separata, e la classe originale delega le chiamate ad essi. L’applicazione graduale di questi refactoring trasforma una “God Class” in un insieme di modoli debolmente accoppiati, ciascuno con un’unica responsabilità. Questo approccio permette di migliorare l’architettura senza fermare lo sviluppo — il refactoring viene eseguito in modo iterativo, un modulo alla volta.
Domande frequenti
No. SRP non riguarda il numero di metodi, ma il numero di ragioni per cambiare. Una classe può avere decine di metodi se servono tutti un’unica responsabilità verso un singolo attore. Un solo metodo è l’estremo opposto, che porta a un’eccessiva frammentazione del codice.
È lo stesso principio. Single Responsibility Principle si traduce sia come “responsabilità unica” che come “obbligo unico”. Il termine “responsabilità” riflette meglio l’essenza: si tratta della responsabilità verso un attore, non di una funzione tecnica.
Repository è un risultato diretto dell’applicazione di SRP al livello dati. Invece di spargere la logica di accesso ai dati in ViewModel o UseCase, Repository assume un’unica responsabilità: fornire dati con astrazione della fonte. È un’implementazione classica di SRP nell’architettura mobile.
Sì, SRP non vieta le dipendenze. Una classe con un’unica responsabilità può delegare parte del lavoro ad altre classi tramite composizione. L’importante è che questi compiti delegati facciano parte della stessa responsabilità, non una ragione indipendente per cambiare.
Fate la domanda: “Quali attori potrebbero richiedere modifiche a questa classe?” Se la risposta contiene più di un attore, SRP è violato. Inoltre: provate a descrivere lo scopo della classe in una frase senza la congiunzione “e”. Se non ci riuscite, la classe fa troppo.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche