28 de abril de 2011

Principio de Separación de Interfaces - SOLID

Siguiendo con los principios SOLID, en esta oportunidad vamos a ver el principio ISP, Interface Segregation Principle.

Comencemos por la definición:

Una clase cliente no debe ser forzada a depender de interfaces que no usa.

Vamos a explicar el concepto de ISP utilizando el ejemplo de Daos de sólo lectura del post anterior sobre LSP.

Supongamos que tenemos una interfaz IDao como la siguiente:

public interface IDao
{
void Insert(object entity);
void Update(object id, object entity);
void Delete(object id);
object[] GetAll();
object GetById(object id);
}


Y una clase FacturaDao que implementa IDao

public class FacturaDao : IDao
{
public void Insert(object entity)
{
//se inserta una factura
}

public void Update(object id, object entity)
{
//se actualiza una factura
}

public void Delete(object id)
{
//se elimina una factura
}

public object[] GetAll()
{
//se obtienen todas las facturas
}

public object GetById(object id)
{
//se obtiene una factura por id
}
}


Las clases que actúen como clientes de FacturaDao, dependerán de la interfaz IDao.

En el caso que quisiéramos crear una capa de acceso a datos de solo lectura, podríamos definir este otro Dao:

public class FacturaDaoReadOnly : IDao
{
public void Insert(object entity)
{
throw new DaoReadOnlyException();
}

public void Update(object id, object entity)
{
throw new DaoReadOnlyException();
}

public void Delete(object id)
{
throw new DaoReadOnlyException();
}

public object[] GetAll()
{
//se obtienen todas las facturas
}

public object GetById(object id)
{
//se obtiene una factura por id
}
}


De esta manera, las clases cliente de FacturaDaoReadOnly, también estarán dependiendo de la interfaz IDao.

Aquí es donde no se cumple por primera vez el principio ISP, ya que una capa de solo lectura no tiene porqué depender de una interfaz IDao que contiene métodos como Insert, Update y Delete. Esto no es recomendable ya que le quita claridad a nuestro diseño.

Por otra parte, también esto trae aparejado el problema de que mi clase FacturaDaoReadOnly tuvo que implementar los métodos Insert, Update y Delete lanzando una excepción DaoReadOnlyException. Lo cual, nuevamente, no es recomendable ya que si a futuro se agregan nuevos métodos a IDao (ej. DeleteAll), si no lo implemento en FacturaDaoReadOnly la misma no va a compilar.

Cómo podemos solucionar esto? Separando la interfaz de lectura de la interfaz de lectura/escritura:

public interface IDaoLectura
{
object[] GetAll();
object GetById(object id);
}


public interface IDao : IDaoLectura
{
void Insert(object entity);
void Update(object id, object entity);
void Delete(object id);
}


public class FacturaDao : IDao
{
public void Insert(object entity)
{
//se inserta una factura
}

public void Update(object id, object entity)
{
//se actualiza una factura
}

public void Delete(object id)
{
//se elimina una factura
}

public object[] GetAll()
{
//se obtienen todas las facturas
}

public object GetById(object id)
{
//se obtiene una factura por id
}
}


public class FacturaDaoReadOnly : IDaoLectura
{
public object[] GetAll()
{
//se obtienen todas las facturas
return null;
}

public object GetById(object id)
{
//se obtiene una factura por id
return null;
}
}


De esta forma estaremos cumpliendo el principio de separación de interfaces.

En mi primer post sobre el principio SRP podrán encontrar varios enlaces a las lecturas recomendadas sobre principios SOLID.

Espero que sea de utilidad, cualquier comentario será bienvenido.

Posts anteriores sobre principios SOLID
Principio de responsabilidad única - SRP
Principio abierto-cerrado - OCP
Principio de Substitución de Liskov - LSP

No hay comentarios:

Publicar un comentario