Las Buddy classes son una técnica para añadir metadatos a clases que son generadas automáticamente por herramientas, ya sean estas herramientas del Visual Studio o herramientas externas; también son útiles para la segregación de responsabilidades.
La introducción de clases parciales, que permiten ampliar los miembros de una clase creada por una herramienta sin que volver a crear la clase se machaque nuestra personalización, es una gran idea, pero tiene un problema a la hora de modificar un miembro ya declarado. Si en vez de añadir nuevos miembros a una clase queremos añadir un atributo a un miembro ya existente nos encontramos con un problema, puesto que el atributo añadido a un miembro de la clase se borraría al regenerar la clase de nuevo.
Para solventar este problema tenemos las buddy clases. Estas son clases que amplían los metadados de otra clase, pudiendo definir el miembro de la clase a modificar dentro de la buddy y añadiéndole el atributo que corresponda.
Imaginemos que tenemos la siguiente clase generada por una plantilla T4 en VS:
Partial Public Class Clasificacion Private _ID as String Private Descripcion as String Public Sub New(ByVal id As String) End Function Public Property ID() As String End Property Public Property Descripcion() As String End Property End Class
Queremos añadir un atributo de validación a la propiedad Descripcion, por lo que debemos crear una Buddy Class con esa propiedad sin implementación y asociar esa Buddy Class a la extensión de la clase parcial utilizando el atributo MetadataType.
Public Class ClasificacionMetadata <StringLength(50, ErrorMessage:="El campo descripcion de una Clasificacion no puede superar los 50 caracteres.")> _ Public Property Descripcion() As String Get End Get Set End Set End Property End Class <MetadataType(GetType(ClasificacionMetadata))> _ Partial Public Class Clasificacion Public Function tieneDescricion As Boolean Return not String.IsNullOrEmpty(Me.Descripcion) End Function End Class
Con esta definición, algunas tecnologías .NET recuperan el atributo MetadataType utilizando reflexión y obtendrán el atributo StringLengthAttribute de la Buddy Class cuando procesen la propiedad Descripcion de la clase generada.
Desgraciadamente, si nuestro código necesita procesar estos metadatos a través de la reflexión de tipos debemos programar explícitamente la posibilidad de que la clase parcial tenga asociado un MetadataType, recuperarlo y al procesar la clase parcial buscar en la clase Buddy su correspondencia para mirar si tiene algún atributo que extienda algún miembro. Esto es muy oscuro, aburrido y provoca que todo aquel que toque el codigo fuente de nuestro programa tenga conocimiento de la necesidad de este comportamiento. No me gusta.
Por suerte, .NET provee una manera de ampliar las descripciones de las clases administradas para que sean procesadas de forma transparente por el framework de reflexión de tipos.
Para esto tenemos en el framework unos proveedores de descripción de tipos. Simplemente debemos crear un nuevo proveedor y asociárselo al tipo Clasificacion.
Public Class BuddyClassesHelper Private Shared IsRegistered As Boolean = False Public Shared Sub RegisterBuddyClasses() If Not IsRegistered Then Dim descriptionProvider = New AssociatedMetadataTypeTypeDescriptionProvider(GetType(Clasificacion), GetType(ClasificacionMetadata)) TypeDescriptor.AddProvider(descriptionProvider, GetType(Clasificacion)) IsRegistered = True End If End Sub End Class
Esto se realizará en la lógica de inicialización de nuestra aplicación. Una vez asociado el proveedor, podremos listar, utilizando la reflexión de tipos, los atributos de la propiedad Descripcion de la Clase Clasificacion y obtendremos el StringLengthAttribute de forma transparente.
Para mi gusto esta solución que provee Microsoft es un poco fea, puesto que se carga toda las recomendaciones DRY (Don't Repeat Yourself) ya que hay que volver a declarar en la Buddy class todos los miembros a los que queramos extender metadatos, y en caso de que la clase parcial cambie, debemos cambar a mano la clase buddy.
Aun así, es útil y si se maneja con cuidado puede ser una solución cómoda en algunos casos.
No hay comentarios:
Publicar un comentario