Características de la programación orientada a aspectos
El método de cada marco para aplicar puntos de referencia, consejos o aspectos es único, pero el propósito y el concepto son los mismos. En el ejemplo de este artículo, lo que realmente hace Encaser al empaquetar un objeto es generar código MSIL a través de clases en el espacio de nombres System.Reflection.Emit, creando así nuevos tipos de contacto en cualquier momento. El nuevo tipo de Contacto se deriva de la clase Contacto, que aún comparte el tipo, pero el nuevo objeto envuelto también contiene una referencia al objeto ContactSave, que mezclamos. El método ISavable.Save se implementa en el nuevo objeto Contact, por lo que cuando se llama a Save, en realidad delega la llamada al objeto ContactSave combinado. Esto tiene la ventaja de poder convertir el nuevo objeto Contact a cualquier interfaz implementada en cualquier objeto mixin.
Figura 2. Diagrama UML de un objeto contenedor.
Quizás estés pensando que usando la característica de lenguaje de clase parcial de .NET Framework 2.0, puedes agregar el comportamiento Guardar a otra clase parcial. Esto es posible, pero este artículo no adopta este enfoque para mantener el código compatible con otras versiones de .NET Framework 1.x. Dado que existen algunas funciones del lenguaje, en circunstancias normales, el ejemplo anterior no necesita usar mixins. Pero los mixins siguen siendo valiosos porque permiten a los desarrolladores mezclar el comportamiento de objetos reutilizables que pueden originarse en otras jerarquías de objetos no relacionados y lograr más funcionalidad que las clases parciales. Cuando usa la palabra clave parcial, está agregando código dentro de la misma clase o tipo, pero en una ubicación física diferente. El siguiente ejemplo de mixin ilustra cómo agregar un comportamiento que no es específico de la clase Contact, sino de una clase reutilizable llamada FieldUndoer. FieldUndoer implementa la interfaz IUndoable, lo que permite restaurar los objetos modificados a su estado original.
Interfaz pública IUndoable
Propiedad de solo lectura HasChanges() como booleano
Sub Undo()
Sub AcceptChanges()
Interfaz final
La propiedad HasChanges significa que si se producen cambios, Deshacer restaurará el objeto a su estado original y AcceptChanges recibe los cambios actuales en el objeto, por lo que cada vez que se vuelva a llamar a Deshacer, lo hará. volver al último estado de recepción cambiado. Si la interfaz se implementa en una clase parcial, entonces estos tres métodos deben implementarse repetidamente en cada clase que desee incluir este comportamiento. Como programador pragmático, trato de ceñirme al principio de "código una vez y solo una vez", por lo que nunca quiero repetir ningún código, y cuanto menos copie y pegue, mejor. Al usar mixins, pude reutilizar objetos FieldUndoer que implementan IUndoable. En ServiceManager mezclé esta nueva funcionalidad. Todo el código del cliente aún desconoce el nuevo mixin y no se requieren cambios a menos que se requiera la interfaz IUndoable. Pruebe este comportamiento cambiando el objeto Contacto en MainForm y haciendo clic en Deshacer.
Función pública compartida GetAllContacts() como ContactService.Contact()
Atenuar servicio como ContactService.Service = Nuevo ContactService.Service
Atenuar contactos() como ContactService .Contact = service.GetAllContacts
'//Envuelve cada objeto de contacto
For i As Integer = 0 To contacts.Length-1
'//Crear una nueva instancia de encaser
'//responsable de envolver nuestro objeto
Dim encaser As encaser = New encaser
'//Agregar instancia mixta de ContactSave
Dim saver As ContactSave = New ContactSave
encaser.AddMixin(saver)
'//Agregar instancia mixin de FieldUndoer
Dim deshacer As FieldUndoer = New FieldUndoer
encaser.AddMixin(undoer)
'//Crea un nuevo objeto con Contact
'//implementaciones de ContactSave p> p>
Dim envueltoObjeto como objeto = encaser.Wrap(contacts(i))
'//Asigna nuestro nuevo objeto de contacto envuelto
'//al anterior objeto de contacto
contacts(i) = DirectCast(wrappedObject, _
ContactService.Contact)
'//Observe que el objeto envuelto sigue siendo del mismo tipo
'//Asigne el nuevo objeto Contacto envuelto a los campos de destino
saver.Target = contacts(i)
undoer.Target = contacts(i) p>
Siguiente
Volver contactos
Finalizar función
Comportamiento combinado
La combinación es solo la punta del iceberg. La característica que realmente pone a AOP en el mapa es el comportamiento de mezcla combinable. Tomando el nuevo objeto Contact como ejemplo, al llamar al método ISavable.Save, el código del cliente también necesita llamar al método IUndoable.AcceptChanges para volver a los últimos cambios guardados la próxima vez que se llame a IUndoable.Undo.
Es fácil explorar y agregar este objeto en este pequeño MainForm, pero codificar esta regla en cualquier sistema mucho más grande que una interfaz de usuario sería una tarea tediosa. Debe encontrar todas las instancias en las que se llama al método Save y luego agregar otra llamada a AcceptChanges. Y al crear código nuevo, los desarrolladores también deben recordar agregar esta nueva funcionalidad cada vez que se llama a Guardar. Esto rápidamente tendrá un efecto en cascada, que puede desestabilizar fácilmente el sistema e introducir errores que son difíciles de rastrear. El uso de programación orientada a aspectos permite combinar estos métodos. Especifique un punto de acceso y una notificación, y cuando se llame al método Guardar, el objeto Contacto llamará automáticamente a AcceptChanges en segundo plano.
Para implementar la composición en su aplicación, necesita agregar una línea más de código al ServiceManager. Agregamos esta línea de código después de agregar el mixin FieldUndoer.
'//Especificar punto de unión para guardar, ejecutar el método AcceptChanges
encaser.AddPointcut(Save, AcceptChanges)
El método AddPointcut se realiza a través de varias firmas diferentes Sobrecarga, que proporciona mayor flexibilidad a la hora de especificar puntos de corte. Nuestra llamada a AddPointcut recibe un nombre de punto conjunto de tipo cadena, que se representa como un método Guardar, y luego recibe un método llamado AcceptChanges como una notificación para ejecutar. Para ver si esto funciona, establezca un punto de interrupción antes del método FieldUndoer.AcceptChanges y el método ContactSave.Save respectivamente. Al hacer clic en el botón Guardar en MainForm se interceptará el cruce y primero accederá a la notificación: el método AcceptChanges. El método Guardar se ejecutará después de que se ejecute la notificación.
Este ejemplo simple muestra cómo agregar un nuevo comportamiento en toda su aplicación, pero es increíblemente poderoso. A pesar de esta característica, es más que una simple nueva forma de agregar funcionalidad. Entre las muchas ventajas, sólo unas pocas implican la reutilización del código y la mejora de la mantenibilidad del sistema al simplificar la evolución del sistema a medida que surgen nuevos requisitos. Al mismo tiempo, el mal uso de AOP puede tener efectos negativos significativos en la capacidad de mantenimiento del sistema, por lo que es importante comprender cuándo y cómo utilizar AOP.
¿Hasta dónde ha llegado AOP?
El uso de AOP para la mayoría de los sistemas de producción grandes o críticos aún no está completamente maduro, pero a medida que mejore el soporte del lenguaje, será más fácil adoptar AOP. Además, se proporciona soporte mejorado para nuevos paradigmas de desarrollo de software, como fábricas de software que utilizan programación orientada a aspectos. Hay varios marcos AOP disponibles en el mundo .NET, cada uno con sus propios métodos, propiedades positivas y negativas.
Encase: el marco de Encase en este ejemplo de código es solo una herramienta para ayudarlo a comenzar a utilizar AOP rápidamente y comprender los conceptos detrás de AOP. Encase aplica aspectos que se pueden agregar individualmente a un objeto durante el tiempo de ejecución.
Aspect#: un marco compatible con la federación AOP para la CLI, que proporciona un lenguaje integrado para la declaración y configuración.
RAIL: el marco RAIL se aplica a las clases JIT de máquinas virtuales.
Primavera. . . — Una versión .NET del popular marco Java Spring. AOP se implementará en la próxima versión.
Eos: una extensión orientada a aspectos para C#.