O Princípio de responsabilidade única é que seu código está fazendo apenas uma coisa e você pode dividir todas as funcionalidades em várias classes, todas destinadas a fazer uma coisa específica. Um exemplo é uma classe específica para validação, executando alguma lógica de negócios, enriquecendo um modelo, recuperando dados, atualizando dados, navegação, etc.
A Separação de Preocupações refere-se ao fato de seu código não estar totalmente acoplado a outras classes / sistemas. O uso de interfaces no seu código ajuda bastante, dessa forma você pode associar classes / sistemas livremente ao código. Um ponto positivo disso é que é mais fácil testar seu código também. Existem muitas estruturas (IoC) que podem ajudá-lo a conseguir isso, mas você também pode implementar isso também.
Um exemplo de algo SoC, mas sem ter 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();
}
}
Como você pode ver, esse código não está totalmente acoplado a classes ou outros sistemas, porque usa apenas algumas interfaces para fazer as coisas. Isso é bom do ponto de vista do SoC.
Como você pode ver, essa classe também contém três métodos particulares, que fazem algumas coisas sofisticadas. Do ponto de vista do SRP, esses métodos provavelmente devem ser colocados em algumas classes próprias. 2 deles fazem algo com a navegação, o que caberia em alguma classe de navegação. O outro faz alguns cálculos sofisticados em um item, provavelmente isso pode ser colocado dentro de uma classe IBusinessLogic.
Tendo algo parecido com isto, vocês dois têm o SoC e o SRP no lugar:
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();
}
}
Claro que você pode debater se toda essa lógica deve ser colocada no GetDataAndNavigateSomewhereIfValid
método. Isso é algo que você deve decidir por si mesmo. Para mim, parece que esse método está fazendo coisas demais.