Il principio di responsabilità unica riguarda il fatto che il tuo codice fa solo 1 cosa e puoi suddividere tutte le funzionalità in diverse classi che sono tutte pensate per fare una cosa specifica.
Un esempio è una classe specifica per la convalida, l'esecuzione di alcune logiche di business, l'arricchimento di un modello, il recupero di dati, l'aggiornamento di dati, la navigazione, ecc.
The Separation of Concerns riguarda il fatto che il tuo codice non è strettamente associato ad altre classi / sistemi. L'utilizzo di interfacce nel codice aiuta molto, in questo modo è possibile associare liberamente classi / sistemi al codice. Un punto cruciale su questo è che è più facile testare il tuo codice anche in unità. Esistono molti framework (IoC) che possono aiutarti a raggiungere questo obiettivo, ma puoi implementare anche tu una cosa del genere.
Un esempio di qualcosa SoC, ma senza SRP
public class Foo
{
private readonly IValidator _validator;
private readonly IDataRetriever _dataRetriever;
public Foo(IValidator validator, IDataRetriever dataRetriever)
{
_validator = validator;
_dataRetriever = dataRetriever;
}
public NavigationObject GetDataAndNavigateSomewhereIfValid()
{
var data = _dataRetriever.GetAllData();
if(_validator.IsAllDataValid(data))
{
object b = null;
foreach (var item in data.Items)
{
b = DoSomeFancyCalculations(item);
}
if(_validator.IsBusinessDataValid(b))
{
return ValidBusinessLogic();
}
}
return InvalidItems();
}
private object DoSomeFancyCalculations(object item)
{
return new object();
}
private NavigationObject ValidBusinessLogic()
{
return new NavigationObject();
}
private NavigationObject InvalidItems()
{
return new NavigationObject();
}
}
Come puoi vedere, questo codice non è strettamente accoppiato a classi o altri sistemi, perché usa solo alcune interfacce per fare cose. Questo è positivo dal punto di vista del SoC.
Come puoi vedere questa classe contiene anche 3 metodi privati che fanno cose fantasiose.
Da un punto di vista SRP, questi metodi dovrebbero probabilmente essere collocati all'interno di alcune classi. 2 di loro fanno qualcosa con la navigazione, che si adatterebbe in qualche classe di INavigation. L'altro fa alcuni calcoli elaborati su un oggetto, questo potrebbe probabilmente essere collocato all'interno di una classe IBusinessLogic.
Avendo qualcosa del genere, entrambi hanno SoC e SRP al loro posto:
public class Foo
{
private readonly IValidator _validator;
private readonly IDataRetriever _dataRetriever;
private readonly IBusinessLogic _businessLogic;
private readonly INavigation _navigation;
public Foo(IValidator validator, IDataRetriever dataRetriever, IBusinessLogic businessLogic, INavigation navigation)
{
_validator = validator;
_dataRetriever = dataRetriever;
_businessLogic = businessLogic;
_navigation = navigation;
}
public NavigationObject GetDataAndNavigateSomewhereIfValid()
{
var data = _dataRetriever.GetAllData();
if(_validator.IsAllDataValid(data))
{
object b = null;
foreach (var item in data.Items)
{
b = _businessLogic.DoSomeFancyCalculations(item);
}
if(_validator.IsBusinessDataValid(b))
{
return _navigation.ValidBusinessLogic();
}
}
return _navigation.InvalidItems();
}
}
Naturalmente potresti discutere se tutta questa logica dovrebbe essere inserita nel metodo GetDataAndNavigateSomewhereIfValid
. Questo è qualcosa che dovresti decidere per te stesso. A me sembra che questo metodo stia facendo troppe cose.