code design – Principios GRASP parte 1

Principios Grasp
Principios Grasp

Artículos relacionados:

Introducción

Son un conjunto de principios de diseño orientado a objetos para asignar responsabilidades a clases. Este acronimo fue acuñado por Craig Larman en su libro Appliying UML Patterns. El acronimo GRASP significa General Responsability Assignment Software Principle / Patterns o en español Patrones/Principios generales de asignación de responsabilidades en software, estos patrones o principios son 9 y los vemos a continuación:

  • Expert
  • Creator
  • Low Coupling
  • High Cohesion
  • Controller
  • Polymorphism
  • Pure fabrication
  • Indirection
  • Controlled variation

Créditos: La verdad hay poca información en español y con ejemplos así que tome de referencia el documento que creo Juan García Carmona que lo pueden descargar de este enlace, la verdad esta muy bueno se los recomiendo.

1.Expert

1.1 Definición:

Principio básico de asignación de responsabilidades, el cual define que la responsabilidad de crear un objeto o la implementación de un método la debe tener la clase que tenga la información necesaria para hacerlo. Con esto obtenemos un diseño con mejor cohesión debido a que la información se mantiene encapsulada y disminuye el acoplamiento. Este principio tiene relación con la S(ingle Responsability) de SOLID ya que habla de responsabilidades.

1.2 Ejemplo:
Antes
public class ArchivoAdjunto
{
    public string Ruta { get; set; }
    public string Mime { get; set; }
}

public class Pedido
{
    //Campos
    public ArchivoAdjunto ArchivoAdjunto { get; set; }
    public int Cantidad { get; set; }
    public decimal Total { get; set; }
    //Campos

    public ResultadoValidacion ValidarDatos()
    {
        return new ResultadoValidacion(ObtenerReglasNoCumplidas());
    }

    private IEnumerable<string> ObtenerReglasNoCumplidas()
    {
        if (Cantidad <= 0)
            yield return "La cantidad de elementos debe ser mayor  0";
        if (Total <= 0)
            yield return "El total debe ser mayor a 0";            
        if(!ExisteArchivo(ArchivoAdjunto.Ruta))
            yield return "El archivo adjunto no existe";
    }

    private bool ExisteArchivo(string ruta)
    {
        return File.Exists(ruta);
    }
}

En este ejemplo no se cumple con este principio ya que la clase Pedido se encarga de validar si el archivo existe, el que tiene toda la información necesaria para implementar ese método es la clase ArchivoAdjunto que sería la experta en realizar esa validación debido a que posee la propiedad Ruta.

Después
public class ArchivoAdjunto
{
    public string Ruta { get; set; }
    public string Mime { get; set; }

    public bool Existe()
    {
        return File.Exists(Ruta);
    }
}

public class Pedido
{
    //Campos
    public ArchivoAdjunto ArchivoAdjunto { get; set; }
    public int Cantidad { get; set; }
    public decimal Total { get; set; }
    //Campos

    public ResultadoValidacion ValidarDatos()
    {
        return new ResultadoValidacion(ObtenerReglasNoCumplidas());
    }

    private IEnumerable<string> ObtenerReglasNoCumplidas()
    {
        if (Cantidad <= 0)
            yield return "La cantidad de elementos debe ser mayor  0";
        if (Total <= 0)
            yield return "El total debe ser mayor a 0";            
        if(!ArchivoAdjunto.Existe())
            yield return "El archivo adjunto no existe";
    }
}

En este enlace pueden volver a leer el artículo de Responsabilidad Única que escribí hace unos meses para reforzar este concepto.

2.Creator

2.1 Definición:

Este patrón nos ayuda a identificar quien es el responsable de la creación de nuevos objetos. La nueva clase podrá ser creada por una clase que cumpla con lo siguiente:

  • Contiene o agrega a la clase
  • Almacena o maneja varias instancias de la clase
  • Tiene toda la información necesaria para la creación del objeto, en otras palabras es Experta.
  • Usa directamente las instancias creadas del objeto
2.2 Ejemplo:
Antes
public class Producto
{
    public string Codigo { get; set; }
}

public class Cabecera
{
    public List<Detalle> Detalles { get; set; }   
}

public class Detalle
{
    public Producto Producto { get; set; }
    public int Cantidad { get; set; }
}

public class CabeceraServicio
{
    public void Registrar()
    {
        var producto = new Producto {Codigo = "PROD001"};
        var detalle = new Detalle {Producto = producto, Cantidad = 4};

        var cabecera = new Cabecera {Detalles = new List<Detalle> {detalle}};
        //Codigo
    }
}

En este caso vemos que la clase CabeceraServicio se encarga de agregar el detalle a la cabecera, siguiendo lo que nos dice este principio el encargado de agregar el detalle debe ser la clase Cabecera ya que contiene a la clase Detalle.

Después
public class Producto
{
    public string Codigo { get; set; }
}

public class Cabecera
{
    public Cabecera()
    {
        Detalles = new List<Detalle>();
    }
    public List<Detalle> Detalles { get; private set; }

    public void AgregarDetalle(string producto, int cantidad)
    {
        var detalle = new Detalle(new Producto{Codigo = producto}, cantidad);
        Detalles.Add(detalle);
    }
}

public class Detalle
{
    public Producto Producto { get; private set; }
    public int Cantidad { get; private set; }

    public Detalle(Producto producto, int cantidad)
    {
        Producto = producto;
        Cantidad = cantidad;
    }
}

public class CabeceraServicio
{
    public void Registrar()
    {
        var cabecera = new Cabecera();
        cabecera.AgregarDetalle("PROD001",4);
        //Codigo
    }
}

3.Low Coupling

3.1 Definición:

Indica que debemos tener a nuestras clases lo menos ligadas entre sí para disminuir las dependencias que existen, con el objetivo de mejorar la reutilización y cuando ocurra alguna modificación el impacto del cambio sea mínimo. Existen 3 tipos de acoplamiento:

  1. Acoplamiento de contenido: Cuando un módulo referencia directamente a otro. (En lenguajes de alto nivel es muy raro)
  2. Acoplamiento común: Cuando dos módulos acceden y afectan a un mismo valor global.
  3. Acoplamiento de control: Cuando un módulo envía a otro un elemento de control que determina la lógica de ejecución del mismo.

Para conocer más acerca del acoplamiento les dejo el siguiente enlace.

3.2 Ejemplo:
Antes
public class SeguridadController : Controller
{
    private static Logger logger = LogManager.GetCurrentClassLogger();

    [HttpPost]
    public void Autenticar(Credencial credencial)
    {
        //Log
        var json = credencial.ToJson();
        logger.Trace(string.Format("Inicio : {0} Data : {1}", DateTime.Now, json);
        //Log

        //Cifrado
        byte[] inputBytes = Encoding.ASCII.GetBytes(credencial.Password);
        byte[] encripted;
        var cripto = new RijndaelManaged();
        using (var ms = new MemoryStream(inputBytes.Length))
        {
            using (var objCryptoStream = 
                 new CryptoStream(ms, cripto.CreateEncryptor(Clave, IV)))
            {
                //Codigo
            }
            encripted = ms.ToArray();
        }
        credencial.Password = Convert.ToBase64String(encripted);
        //Cifrado

        //Autenticacion BD
        using (var conexion = new SqlConnection())
        {
            using (var comando = new SqlCommand(""))
            {
                //Codigo
            }
        }
        //Autenticacion BD

        //Notificacion 
        MailMessage mail = new MailMessage("you@yourcompany.com", "user@hotmail.com");
        SmtpClient client = new SmtpClient();
        client.Host = "smtp.google.com";
        mail.Subject = "Inicio de sesion";
        mail.Body = "";
        client.Send(mail);
        //Notificacion 

        //Log
        logger.Trace("Fin");
        //Log
    }
}

El problema con esta clase es que tiene muchas responsabilidades y se encuentra acoplada a al menos a unas 12 clases, para arreglar esto podemos aplicar los principios Single responsability y Dependency Inversion. Aplicando el principio Single responsability tendremos como resultado clases pequeñas que cumplan una sola responsabilidad por lo tanto el acoplamiento se distribuye entre todas estas. Luego aplicando el principio Dependency inversion romperemos la dependencia entre la controladora y las clases concretas haciendo uso de la Inyección de dependencias y una librería de IoC.

Después
public class SeguridadController : Controller
{
    private readonly IServicioLog servicioLog;
    private readonly IServicioCriptografia servicioCripto;
    private readonly IServicioNotificacion servicioNotificacion;
    private readonly IServicioAutenticacion servicioAutenticacion;

    public SeguridadController(IServicioLog servicioLog, 
        IServicioCriptografia servicioCripto, 
        IServicioNotificacion servicioNotificacion,
        IServicioAutenticacion servicioAutenticacion)
    {
        this.servicioAutenticacion = servicioAutenticacion;
        this.servicioCripto = servicioCripto;
        this.servicioLog = servicioLog;
        this.servicioNotificacion = servicioNotificacion;
    }

    [HttpPost]
    public void Autenticar(Credencial credencial)
    {
        servicioLog.RegistrarInicio(credencial);
        credencial.Password = servicioCripto.Cifrar(credencial.Password);

        servicioNotificacion.Autenticar(credencial);

        servicioNotificacion.NotificarInicioSesion(credencial);

        servicioLog.RegistrarFin();
    }
}

4.High Cohesion

4.1 Definición:

Indica que la información que almacena una clase debe ser coherente y relacionada a la clase. Existen 7 tipos de cohesión:

  1. Cohesión coincidente: El módulo realiza muchas tareas pero sin ninguna relación entre ellas.
  2. Cohesión lógica: El módulo realiza muchas tareas relacionadas pero en tiempo de ejecución solo una de ellas será llevada a cabo.
  3. Cohesión temporal: Las tareas llevadas a cabo por un módulo tienen como única relación “que deben ser ejecutadas al mismo tiempo”.
  4. Cohesión de procedimiento: La única relación que guardan las tareas de un módulo es que corresponden a una secuencia de pasos propia del “producto”.
  5. Cohesión de comunicación: Las tareas corresponden a una secuencia de pasos propia del “producto” y todas afectan a los mismos datos.
  6. Cohesión de información: Las tareas llevadas a cabo por un módulo tienen su propio punto de arranque, su codificación independiente y trabajan sobre los mismos datos.
  7. Cohesión funcional: Cuando el módulo ejecuta una y sólo una tarea, teniendo un único objetivo a cumplir, se dice que tiene Cohesividad Funcional.

Para conocer más acerca de la cohesión les dejo el siguiente enlace.

4.2 Ejemplo:
Antes
public class Helper
{
    public void Encriptar(){}
    public void Desencriptar() { }

    public void EnviarCorreo() { }
    public void LogMensaje() { }
    public void FormatearCadena() { }
    public bool ExisteArchivo() { }
    public void CrearPdf() { }
    //Codigo
}

En este caso la clase Helper es muy genérica no tiene una funcionalidad clara y sus métodos no tienen relación entre sí, como vemos esta clase se encarga de: cifrar, crear pdf, log, notificar, etc. Para arreglar esto podemos aplicar el principio Single responsability. 

Después
public class CifradoMd5 : ICifrado
{
    public void Encriptar() { }
    public void Desencriptar() { }
}

public class NotificacionEmail : INofiticacion
{
    public void Enviar() { }
}

public class ArchivoTxt : IArchivo
{
    public bool Existe() { }
}

public class ReportePedido : IReporte
{
    public void Crear() { }
}
Conclusión

El acrónimo GRASP fue acuñado por Craig Larman en su libro Appliying UML Patterns que significa General Responsability Assignment Software Principle / Patterns estos patrones o principios. Estos principios tienen como finalidad el asignar responsabilidades a clases y son 9: Expert, Creator, Low Coupling, High Cohesion, Controller, Polymorphism, Pure fabrication, Indirection y Controlled variation. En la segunda parte veremos los 5 principios restantes.

Referencias:
Metal Tip:

Este artículo lo escribí escuchando la canción La luz del norte de la banda de WarCry de España, les comparto el enlace.

Happy coding and Stay Heavy lml

Anuncios

Un comentario en “code design – Principios GRASP parte 1

  1. En Low Coupling:
    “Luego aplicando el principio Dependency inversion romperemos la dependencia entre la controladora y las clases concretas”

    Según lo tienes has añadido una dependencia extra en forma de Controller de tu Controller. ¿Alguien tendrá que instanciar tu controller no? Además, alguien tendrá que instanciar e inicializar los 4 servicios nuevos, ¿no? Van por cinco dependencias nuevas… y además… las que había siguen ahí… no se han roto, tu clase sigue siendo un Controller y ha de llamar a sus clases cliente.

    Es un punto de vista. Dependency Inversion no disminuye el número de dependencias, las centraliza en otros puntos. Si tu servicioNotificaciones hoy ha de mandarlas a un sistema con el que se integra en vez de mandarlas por e-mail cambias la config del servicio y de repente todo funciona de otra manera. Pero no es un caso común, no se da apenas, no merece la pena el esfuerzo por la complicación añadida… y es que solo veo una aplicación que justifique la cancamusa (hype) de IoC…. TDD. Unit testing y que tu capa de testing pueda fácilmente suplantar a tu bbdd.

    Buen blog! Lo acabo de descubrir y chrome va a petar de la de pestañas que te he abierto.

    Me gusta

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s