El Principio de responsabilidad única se trata de que su código solo haga 1 cosa y puede dividir toda la funcionalidad en varias clases, todas ellas destinadas a hacer 1 cosa específica. Un ejemplo es una clase específica para validación, hacer algo de lógica de negocios, enriquecer un modelo, recuperar datos, actualizar datos, navegación, etc.
La separación de inquietudes se trata de que su código no esté estrechamente vinculado a otras clases / sistemas. El uso de interfaces en su código ayuda mucho, de esta manera puede acoplar libremente clases / sistemas a su código. Una ventaja de esto es que también es más fácil hacer una prueba unitaria de su código. Hay muchos marcos de trabajo (IoC) que pueden ayudarlo a lograr esto, pero puede implementarlo usted mismo, por supuesto.
Un ejemplo de algo SoC, pero no tener 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 puede ver, este código no está estrechamente acoplado a clases u otros sistemas, porque solo usa algunas interfaces para hacer cosas. Esto es bueno desde el punto de vista del SoC.
Como puede ver, esta clase también contiene 3 métodos privados que hacen algunas cosas elegantes. Desde el punto de vista de SRP, esos métodos probablemente deberían ubicarse dentro de algunas clases propias. 2 de ellos hacen algo con la navegación, que encajaría en alguna clase de INavigation. El otro hace algunos cálculos sofisticados sobre un elemento, esto probablemente podría ubicarse dentro de una clase IBusinessLogic.
Al tener algo como esto, ambos tienen SoC y SRP en su 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();
}
}
Por supuesto, podría debatir si toda esta lógica debería colocarse en el GetDataAndNavigateSomewhereIfValid
método. Esto es algo que debes decidir por ti mismo. Para mí, parece que este método está haciendo demasiadas cosas.