Lβopen-closed principle Γ¨ il primo principio di SOLID.
Definizione
Il principio di single responsibility afferma che βuna classe deve avere uno e uno solo motivo per cambiareβ o anche βuna classe deve essere responsabile di un unico attoreβ. Genericamente una classe dovrebbe avere una sola responsabilitΓ completamente incapsulata al suo interno, dedicarsi esclusivamente a quella e conseguentemente cambiare solo se tale responsabilitΓ cambia e per nessun altro motivo.
Inoltre una classe dovrebbe rispondere ad un solo βattoreβ e per attore si intende una entitΓ astratta che ha delle necessitΓ allβinterno del mio software.
PerchΓ© implementarlo
Come insegna uncle Bob, i requisiti di un software continuano a cambiare nel tempo; conseguentemente devono essere modificare le responsabilitΓ di almeno una classe. Maggiori sono le responsabilitΓ della classe in questione piΓΉ spesso vi sarΓ il bisogno di cambiarla.
Il problema Γ¨ che se una classe ha piΓΉ di una responsabilitΓ , la modifica di parte del codice di una si riflette automaticamente sullβaltra, in quanto appartenente alla stessa classe.
Per esempio se la classe fa X e Y e per modificare X devo, per esempio, aggiungere un parametro al costruttore della classe, questa modifica porterΓ anche a dei cambiamenti sulle classi che la utilizzano per la responsabilitΓ Y che non centra nulla con X.
PiΓΉ una classe ha responsabilitΓ piΓΉ spesso dovrΓ essere modificata e di riflesso verranno modificate le classi che dipendono da questa ultima, quindi tutte le sue dipendenze.
Queste dovranno essere ricompilate e subiranno delle modifiche anche se non centrano nulla con la responsabilitΓ che Γ¨ effettivamente cambiata.
Per evitare tutti questi problemi Γ¨ indispensabile che ogni classe abbia una e una sola responsabilitΓ e, come dice uncle Bob, uno e un solo motivo per cambiare.
Non esagerare
Alcuni programmatori prendono il principio allβestremo e arrivano al punto di implementare classi con una sola funzione. Questo ovviamente porta ad una proliferazione di classi che porta solo confusione allβinterno del codice.
Il principio di singola responsabilitΓ deve essere usato con criterio secondo la propria esperienza.
Come capire se una classe viola il SRP
Ci sono varie euristiche per capire se una classe viola il SRP:
- Il nome della classe contiene parole come
Manager
oProcessor
o comunque nomi che indicano che tale classe gestisce varie cose; - Non Γ¨ semplice definire il nome della classe stessa (che dovrebbe invece descrivere lβunica responsabilitΓ della classe) o il nome che vorremmo dare contiene
and
; - Presenta dei metodi privati che si applicano solo ad un piccolo sottoinsieme di metodi della classe;
- La classe non Γ¨ coesa;
Esempi
Esempio 1
Assumiamo di avere una classe βDipendenteβ con tre metodi:
CalcolaPaga
: fornisce la paga da dare al dipendenteReportHours
: indica il numero di ore lavorateSave
: Salva i dati del dipendente sul database
Questi tre metodi sono responsabili di tre attori differenti: il primo metodo viene usato dal settore contabilitΓ , il secondo dal settore risorse umane e il terzo al direttore tecnico.
Avendo i tre metodi nella stessa classe che dipendono da attori diversi puΓ² capitare che una modifica delle esigenze dellβuno si rifletta, senza volerlo, anche sugli altri due.
Per esempio qualora il metodo ReportHours
e CalcolaPaga
utilizzino lo stesso metodo sottostante per calcolare il numero di ore lavorate, una modifica a questo metodo richiesta dallβufficio paghe porta, di riflesso, ad una modifica non richiesta al metodo ReportHours
gestito dallβufficio risorse umane.
La soluzione Γ¨ porre ogni metodo in una sua classe dedicata e la classe Dipendente conterrΓ solo le interfacce di queste classi, senza quindi alcuna riga di codice al suo interno. Il calcolo delle ore lavorate sarΓ quindi completamente scorporato.
Esempio 2
// Violates SRP
public class UserService
{
public void AddUser(string userName)
{
// Add user to the database
}
public void SendEmail(string email)
{
// Send an email to the user
}
}
// Adheres to SRP
public class UserService
{
public void AddUser(string userName)
{
// Add user to the database
}
}
public class EmailService
{
public void SendEmail(string email)
{
// Send an email
}
}