Opinión: De los atributos y su uso…

Sí, ya sé: estamos en Agosto y lo que más seduce ahora mismo es darse un bañito en la playa y salir de copas a rebentar los mojitos del bar, así que los que podáis hacedlo sin dudar… Total, este post tampoco se largará a ninguna parte luego… 🙂

Los que no estéis de vacaciones o bien prefiráis leer geeks.ms en pleno Agosto (hay de todo en la viña del señor) a ver que os parece este post… es mi opinión sobre el uso que se da a los atributos y los “problemas” que a mi parecer conlleva dicho uso. Dado que es un post de opinión vuestros comentarios sobre vuestras opiniones serán muy bien recibidos (de hecho siempre lo son, simplemente en este post lo serán más si cabe).

He estado observando en determinados casos un uso de los atributos que no me termina de convencer… Para aclarar conceptos entiendo por atributos aquellas clases que derivan de System.Attribute y que usamos para decorar nuestras clases, métodos, propiedades…

Voy a centrar este artículo de opinión en Data Annotations, pero no es el único caso que tiene este “defecto”…

En mi opinión los atributos deberían representar sólamente metadatos, es decir mera información, sin comportamiento alguno definido… O dicho de otro modo no deberían tener ningún método, sólo propiedades.

Cojamos el siguiente código de ejemplo, que es lo más simple del mundo: una clase, con una sola propiedad decorada con [Required]. Dicho atributo indica que la propiedad que se decora con él debe tener un valor:

class Foo
{
[Required]
public string Bar { get; set; }
}

Bien, ahora dado el siguiente código:

Foo foo = new Foo();
foo.Bar = string.Empty;
bool b = ((RequiredAttribute)foo.GetType().
GetProperty("Bar").GetCustomAttributes(false)[0]).
IsValid(foo.Bar);

Cuál es el valor de b, en este punto?

La respuesta rápida es… depende de la implementación del método IsValid que está en RequiredAttribute. En este caso el valor de IsValid es false, porque string.Empty asume que no es un valor válido.

Y aquí es donde, a mi, me chirría todo un poco… Yo estoy marcando un valor como requerido (usando [Required]) pero no entiendo porque la implementación de Required decide que significa ser requerido dentro de mi aplicación. Es decir el mero de hecho de declararlo como requerido me obliga a una implementación concreta de que signfica “ser requerido”.

En mi lógica de negocio requerido puede significar que no valga null (siendo string.Empty un valor válido, cosa que no acepta el RequiredAttribute). Me diréis que me puedo crear mis propios atributos y tendréis toda la razón del mundo pero entonces si aplico a la propiedad Bar de la clase Foo un atributo mío, estoy cambiando los metadatos de dicha clase… que pasa si la clase es externa o bien quiero usarla en distintos proyectos (donde el concepto de requerido puede ser diferente)?

No se si sería mejor separar RequiredAttribute en dos clases: una que sea el atributo que marque el elemento como requerido y otro que sea “que significa que un elemento sea requerido (es decir el método IsValid)” (dejadme llamar AttributeHandler a estas clases por ponerles nombre). Obviamente el framework debería proporcionar un AttributeHandler asociado a cada atributo y debería darme un mecanismo para que yo pudiese indicar que en mi proyecto el AttributeHandler para la clase RequiredAttribute es la clase que yo quiera…

No sé si me entiende por donde voy… 🙂

Un (caluroso) saludo a todos!

PD: Un efecto colateral de tener atributos con cierto comportamiento es que estos empiezan a tener dependencias a terceros componentes (imaginaros un atributo tipo [Log]… que usa para generar los ficheros de log?). Y dado que los atributos los crea el CLR eso hace difícil inyectarles dependencias… Si el código que tiene el comportamiento (p.ej. el que hace log) estuviese en otras clases que no se creasen automáticamente por el CLR sería más fácil inyectarles dependencias).

2 comentarios sobre “Opinión: De los atributos y su uso…”

  1. Hola Eduard,
    primero, no es que haya de todo en la viña del señor sino que existe este otro emisferio en el que no solo no estamos de vacaciones sino que además estamos trabajando y abrigados hasta las orejas. Bueno, dicho esto pasemos a lo de esta entrada.
    Parece lógico lo que decis (sobre todo eso de que no deberían tener lógica) pero en realidad (al menos para mi) los atributos son creados en función de las necesidades del componente que los va a consultar mediante reflection. Es decir, imagina que yo creo un atributo llamado [DeleteAfterFirstUseAttribute] para limpiar automáticamente los valores de las propiedades marcadas después de ser usadas, ese atributo lo creo solo porque le hace falta a mi framework y no espero que nadie quiera cambiarle el significado.
    De todos modos lo que vos decis es claro, pero si vos queres poner tu propia validación podés hacerlo con el atributo CustomValidation que si bien carece de la elegancia de un [Required], te permite hacer lo que planteas como ejemplo.
    Saludos.

  2. Hola Lucas,
    Sí, se que en el “Sur” estáis abrigaditos y sin vacaciones, cuando decía que hay de todo en la viña del señor me refería a aquellos que estando de vacaciones prefieren leer geeks.ms a tomarse una cerveza bien fresca en la playa. No es ninguna crítica, eh? yo mismo estoy de vacaciones y aquí me tienes: leyendo, escribiendo y comentando… 😛

    Tienes toda la razón en lo que dices: si un atributo entra dentro de tu aplicación puedes hacer con él lo que quieras, si te está bien el acople de funcionalidades del atributo con la clase que lo decora, perfecto. No tengo nada que decir a esto.
    Me refería a atributos “públicos”, vamos que pertenecen o bien a .NET en general (como los de DataAnnotations) o bien a los que puedan pertenecer a un framework de amplio uso (p.ej. MVC). Una de las tareas de mi trabajo es crear librerías de clases que luego usa otra gente, por lo que me encuentro en tesituras de ese tipo a menudo…

    Muchas gracias por tu comentario!
    Un saludo!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *