Il principio di responsabilità singola riguarda il fatto che il tuo codice faccia solo 1 cosa e puoi dividere tutte le funzionalità in diverse classi che sono tutte pensate per fare 1 cosa specifica. Un esempio è una classe specifica per la convalida, la logica aziendale, l'arricchimento di un modello, il recupero dei dati, l'aggiornamento dei dati, la navigazione, ecc.
La separazione delle preoccupazioni riguarda il fatto che il codice non sia strettamente associato ad altre classi / sistemi. L'uso delle interfacce nel tuo codice aiuta molto, in questo modo puoi associare liberamente le classi / i sistemi al tuo codice. Un aspetto negativo di questo è che è più facile testare anche il tuo codice. Ci sono molti framework (IoC) che possono aiutarti a raggiungere questo obiettivo, ma puoi anche implementare una cosa del genere, ovviamente.
Un esempio di qualcosa di 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 associato a classi o altri sistemi, perché usa solo alcune interfacce per fare cose. Questo è buono 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 inseriti in alcune classi a parte. 2 di loro fanno qualcosa con la navigazione, che rientrerebbe in una classe di INavigation. L'altro esegue alcuni calcoli elaborati su un oggetto, questo potrebbe probabilmente essere inserito in una classe IBusinessLogic.
Avendo qualcosa del genere, entrambi avete il SoC e SRP a 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();
}
}
Ovviamente potresti discutere se tutta questa logica debba essere inserita nel GetDataAndNavigateSomewhereIfValid
metodo. Questo è qualcosa che dovresti decidere da solo. A me sembra che questo metodo stia facendo troppe cose.