reflection - que - software reflexion
¿Qué es exactamente Reflection y cuándo es un buen enfoque? (11)
¿Qué es exactamente Reflection? Leí el artículo de Wikipedia sobre este tema y entiendo que es una especie de metaprogramación, donde el programa puede modificarse en el tiempo de ejecución, pero ¿qué significa esto? ¿En qué tipo de situaciones es este un buen enfoque y cuándo es el mejor para usarlo?
El primer buen ejemplo en el que puedo pensar es cuando necesitas ejecutar un conjunto de métodos en un objeto dado sin saber en tiempo de compilación qué métodos existirán en él.
Tome marcos de pruebas unitarias, por ejemplo. La clase de corredor de pruebas que es responsable de ejecutar todas las pruebas de su unidad no sabe de antemano qué nombre va a dar a sus métodos. Todo lo que sabe es que tendrán el prefijo "prueba" (o en el caso de Java 5, anotado con @Test
). Entonces cuando se le da una clase de prueba, se refleja en esa clase para obtener una lista de todos los métodos en ella. Luego, itera a través de esos nombres de método como cadenas y llama a esos métodos en el objeto si comienzan con "prueba". Eso no sería posible sin reflexión. Y ese es solo un ejemplo.
En Java, básicamente es una forma de crear instancias de una clase sin saberlo antes de la mano. Supongamos que desea que el usuario pueda cambiar un archivo de configuración agregando la clase que desea que use su programa (supongamos que tiene numerosas implementaciones de alguna interfaz). Con la reflexión puede crear un objeto basándose simplemente en su nombre, firma de método, etc. . y luego lanzarlo a tu interfaz.
La reflexión es útil para la configuración de tiempo de ejecución, lo que permite que partes de un sistema se controlen mediante una configuración externa.
Por ejemplo, una fábrica de clases podría construir diferentes tipos de concreto basados en un archivo de entrada, donde los tipos concretos requieren información de configuración diferente para llamar a un constructor concreto en lugar de usar una interfaz de constructor. (El método del constructor del objeto que se encuentra utilizando la reflexión).
La reflexión es (básicamente) la capacidad de un programa para consultar información de tipo que estaba disponible para el compilador. Entonces, por ejemplo, dado el nombre de un tipo, puede consultar los métodos que contiene. Luego, para cada método, puede consultar los tipos de parámetros que toman, etc., etc.
Es útil para la configuración en tiempo de ejecución donde tienes un archivo de configuración que especifica el comportamiento de tu aplicación. La configuración puede contener los nombres de los tipos concretos que debe usar (como suele ser el caso con los contenedores IOC). Usando la reflexión puedes crear una instancia de este tipo concreto (a través de una API de reflexión) y usarla.
La reflexión es una instalación donde puede consultar un objeto sobre sus atributos en tiempo de ejecución. Por ejemplo, Python, Java y .Net tienen instalaciones donde puede encontrar las variables o métodos de instancia de un objeto.
Un ejemplo de una aplicación para la reflexión es una capa de mapeo O / R. Algunos usan la reflexión para construir un objeto al poner en cola sus propiedades en el tiempo de ejecución y rellenar dinámicamente una instancia. Esto le permite hacer esto programáticamente en base a los metadatos de algún tipo de diccionario de datos sin tener que volver a compilar la aplicación.
Para tomar un ejemplo simple, usaré Python porque sus facilidades de reflexión son muy simples de usar e involucran menos texto estándar que las de java o .Net.
ActivePython 2.5.2.2 (ActiveState Software Inc.) based on
Python 2.5.2 (r252:60911, Mar 27 2008, 17:57:18) [MSC v.1310 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class foo:
... def __init__(self):
... self.x = 1
...
>>> xx = foo() # Creates an object and runs the constructor
>>> xx.__dict__ # System metadata about the object
{''x'': 1}
>>> a = xx.__dict__ # Now we manipulate the object through
>>> a[''y''] = 2 # its metadata ...
>>> print xx.y # ... and suddenly it has a new instance variable
2
>>>
Ahora, hemos utilizado la reflexión básica para examinar las variables de instancia de un objeto arbitrario. La variable especial __dict__
en Python es una propiedad del sistema de un objeto que tiene una tabla hash de sus miembros marcada por el nombre de la variable (o método). Hemos examinado de forma reflexiva el objeto y hemos utilizado las funciones de reflexión para introducir artificialmente una variable de segunda instancia en él, que luego podemos visualizar invocando como una variable de instancia.
Tenga en cuenta que este truco en particular no funciona en Java o .Net, ya que las variables de instancia son fijas. El sistema de tipos de estos idiomas no permite que se agreguen nuevas variables de instancia en tiempo de ejecución en la forma en que lo hace el sistema de tipado ''pato'' de python. Sin embargo, podría haber actualizado reflexivamente el valor de una variable de instancia que se declaró en la definición de tipo.
También puede usar la reflexión para construir dinámicamente invocaciones de método y realizar otros trucos sencillos, como crear instancias de un objeto en función de un parámetro. Por ejemplo, si tiene algún tipo de sistema basado en complementos donde ciertas capacidades son opcionales, puede usar el reflejo para consultar el complemento sobre los servicios que ofrece (tal vez preguntando si se implementaron ciertas interfaces) sin requerir metadatos explícitos.
Muchas interfaces dinámicas de lenguaje como la automatización OLE utilizan la reflexión como parte integral de la interfaz.
La reflexión me ha sido útil en al menos 1 proyecto en el que puedo pensar. Escribimos un programa interno de "Administrador de procesos" que realiza una gran cantidad de procesos clave de negocios en ciertos intervalos. El proyecto está configurado de modo que el núcleo sea en realidad solo un servicio de Windows con objetos de temporizador que se disparan cada 30 segundos y verifican el trabajo que se debe realizar.
El trabajo real se está haciendo en una biblioteca de clase (apropiadamente llamada "WorkerLib"). Definimos clases con métodos públicos que realizan ciertas tareas (mover archivos, cargar datos a sitios remotos, etc.). La idea es que el servicio central pueda llamar a métodos de la biblioteca de trabajadores sin saber nada sobre los métodos que está llamando. Esto nos permite crear un cronograma en la base de datos para trabajos, e incluso agregar nuevos métodos en la biblioteca de clases sin tener que alterar el sistema central.
La idea básica es que podemos utilizar la reflexión en el servicio central para ejecutar métodos cuyos nombres hemos almacenado en la base de datos que define las programaciones. Es bastante limpio en la práctica. Nuestro servicio central es sólido y se encarga de ejecutar los trabajos según sea necesario, mientras que la biblioteca de trabajadores real puede ampliarse y modificarse según sea necesario sin preocuparse siquiera por el núcleo.
Si necesita más explicaciones, no dude en preguntar. Esta era simplemente la mejor manera en que podía pensar para explicar un escenario del mundo real donde la reflexión realmente nos facilita las cosas.
Los ensamblados contienen módulos, los módulos contienen tipos y los tipos contienen miembros. Reflection proporciona objetos que encapsulan ensamblajes, módulos y tipos. Puede usar la reflexión para crear dinámicamente una instancia de un tipo, vincular el tipo a un objeto existente u obtener el tipo de un objeto existente. A continuación, puede invocar los métodos del tipo o acceder a sus campos y propiedades. Los usos típicos de la reflexión incluyen los siguientes:
- Utilice Ensamblaje para definir y cargar ensamblajes, cargar módulos que se enumeran en el manifiesto de ensamblaje y ubicar un tipo de este ensamblaje y crear una instancia del mismo.
- Use el Módulo para descubrir información como el ensamblaje que contiene el módulo y las clases en el módulo. También puede obtener todos los métodos globales u otros métodos específicos no globales definidos en el módulo.
- Use ConstructorInfo para descubrir información como el nombre, los parámetros, los modificadores de acceso (como público o privado) y los detalles de implementación (como abstractos o virtuales) de un constructor. Use el método GetConstructors o GetConstructor de un tipo para invocar un constructor específico.
- Use MethodInfo para descubrir información como el nombre, el tipo de devolución, los parámetros, los modificadores de acceso (como público o privado) y los detalles de implementación (como abstractos o virtuales) de un método. Use el método GetMethods o GetMethod de un Tipo para invocar un método específico.
- Use FieldInfo para descubrir información como el nombre, los modificadores de acceso (como público o privado) y los detalles de implementación (como la estática) de un campo, y para obtener o establecer valores de campo.
- Use EventInfo para descubrir información como el nombre, el tipo de datos del controlador de eventos, los atributos personalizados, el tipo de declaración y el tipo reflejado de un evento, y para agregar o eliminar manejadores de eventos.
- Use PropertyInfo para descubrir información como el nombre, el tipo de datos, el tipo de declaración, el tipo reflejado y el estado de solo lectura o escritura de una propiedad, y para obtener o establecer valores de propiedad.
- Use ParameterInfo para descubrir información tal como el nombre de un parámetro, tipo de datos, si un parámetro es un parámetro de entrada o salida, y la posición del parámetro en una firma de método.
No es tanto modificar el código en el momento de la ejecución, sino examinar objetos y pedirles que ejecuten código sin saber su tipo de forma estática.
Una manera simple de describirlo sería "una manera algo dolorosa de hacer que un lenguaje estáticamente estático se comporte dinámicamente".
EDITAR: Usos:
- Configuración (por ejemplo, tomar un archivo XML que especifica tipos y propiedades, luego construir los objetos apropiados)
- Pruebas (pruebas unitarias que se identifican por nombre o atributos)
- Servicios web (en .NET al menos, se usa mucha reflexión en el motor del servicio web central)
- Cableado automático de eventos: proporcione un método con un nombre apropiado, por ejemplo,
SubmitButton_Click
y ASP.NET adjuntará ese método como controlador para el eventoClick
SubmitButton
(si tiene habilitado el autovínculo)
¿Es una buena idea? Bueno, solo cuando las alternativas son dolorosas. Prefiero el tipado estático cuando no se interpone en el camino, entonces obtienes mucha bondad en tiempo de compilación, y es más rápido también. Pero cuando lo necesitas, la reflexión te permite hacer varias cosas que de otro modo simplemente no serían posibles.
Otro ejemplo: tengo un código que uso que toma el resultado de una base de datos, que es un conjunto de filas con columnas con nombre, y lo alimenta a una matriz de objetos. Paso iterando por las filas, y si el objeto de destino tiene una propiedad con el mismo nombre y tipo, lo configuro. Esto hace que mi código de obtención de datos parezca algo así:
SqlDataReader sdr = Helper.GetReader("GetClientByID", ClientID);
Client c = new Client();
FillObject(sdr, c);
return c;
Realmente, la reflexión debe considerarse como una especie de amplificador para el código. Puede hacer que un código sea mejor y más limpio, y un código incorrecto peor. ¿Qué hace? Realmente le permite escribir código, que no está totalmente seguro de lo que va a hacer en el momento de escribirlo. Tiene una idea general, pero le permite no codificar qué objetos, métodos y propiedades ejecutarán cuando se compile el programa.
Otras publicaciones son correctas cuando dicen que le permite al programa ejecutar código basado en valores de configuración, pero su poder real es que le permite doblegar severamente las reglas de programación orientada a objetos. Eso es realmente lo que hace. Es como apagar las medidas de seguridad. Se puede acceder a los métodos y propiedades privados mediante la reflexión junto con casi cualquier otra cosa.
Un excelente ejemplo de cuando MS utiliza la reflexión es con el enlace de datos en el objeto de datos. Usted especifica el nombre del campo de texto y el campo de datos para que un objeto se vincule a una lista desplegable, etc., y el código refleja el objeto y extrae la información adecuada. El objeto de enlace de datos realiza el mismo proceso una y otra vez, pero no sabe con qué tipo de objeto se debe vincular. La reflexión es una forma conveniente de escribir un poquito de código para manejar todos los casos posibles.
Te daré un ejemplo.
Como ejercicio de programación, escribí un corrector de archivos mp3. Escanea mi biblioteca de música y muestra las etiquetas id1 / id2 que me interesan en un DataGridView. Uso el reflejo para obtener las propiedades de la clase de información mp3 sin que el código UI tenga que saber nada sobre esa clase. Si deseo cambiar la información que se muestra, puedo editar la clase de información mp3 o cambiar su configuración (dependiendo de cómo escribí la clase) y no tengo que actualizar la UI.
También significa que he podido usar Dependency Injection para usar el mismo desde el final para mostrar información sobre fotografías digitales simplemente intercambiando la clase de biblioteca de datos.