Qu'est-ce que la « réflexion » en programmation ?

0
172
Shutterstock/whiteMocca

La programmation réfléchie est un mécanisme qui permet à un processus des capacités d'introspection. Les API de réflexion intégrées aux langages de programmation vous permettent d'inspecter le code lors de l'exécution. Vous pouvez utiliser cette capacité pour en savoir plus sur la base de code environnante et son contenu.

On parle souvent de réflexion dans le contexte de la programmation orientée objet. Vous utilisez souvent la réflexion pour découvrir des entités de base de code au moment de l'exécution. L'API de réflexion du langage vous permettra d'inspecter les classes, les méthodes, les propriétés et les types à partir de votre système. Cela vous permet de créer des fonctionnalités plus dynamiques.

Les systèmes qui utilisent la réflexion sont capables d'interroger et de modifier leurs propres environnements. C'est là que la réflexion diffère de la simple introspection des valeurs. Un langage avec prise en charge complète de la réflexion permettra la modification de la base de code au moment de l'exécution, permettant effectivement à la source de réécrire des aspects d'elle-même.

Un exemple de réflexion

< p>Une utilisation courante de la réflexion est pendant les tests. La réflexion peut vous aider à vous moquer des classes en exposant leurs comportements internes. Une méthode de classe protégée ou privée ne serait généralement pas testable ; en utilisant la réflexion, vous pouvez remplacer la contrainte de visibilité afin qu'elle devienne publique dans vos tests unitaires.

classe Test {   protégé int $Valeur;   fonction publique __construct(int $Value) { $ceci -> Valeur = $Valeur ; }   fonction protégée computeValue() : entier { renvoie ($this -> Valeur * 2); }   }   /** * Sans réflexion */  $t = nouveau test(10);   //Erreur – la méthode n'est pas accessible publiquement assert($t -> calculerValeur() === 20);   /** * Utilisation de Reflection */  $reflectionMethod = new ReflectionMethod(Test::CLASS, "computeValue"); $reflectionMethod -> setAccessible(true);   $t = nouveau test(10);   //Cela fonctionne maintenant ! assert($reflectionMethod -> invoque($t) === 20);

Dans cet exemple utilisant PHP, la classe Test définit une méthode protégée qui est utilisée en interne. Pendant que la méthode effectue un calcul, vous souhaiterez peut-être la tester unitairement. Vous ne pouvez pas appeler la méthode en externe, mais l'API Reflection de PHP vous permet de contourner les contraintes de visibilité. Une instance ReflectionMethod fournit des informations sur la méthode et vous permet d'invoquer une version modifiée.

Publicité

Bien que cela soit utile, vous devez être conscient qu'il peut être utilisé à mauvais escient. L'utilisation généralisée de la réflexion dans les tests est souvent révélatrice de problèmes plus importants dans votre base de code. Cela implique la classe’ l'interface est trop restrictive et inadaptée à ses responsabilités. Dans de nombreux cas, il est plus approprié de refactoriser la méthode protégée dans une nouvelle classe qui expose sa propre interface publique.

Voici à quoi cela pourrait ressembler pour l'exemple ci-dessus :

Calculatrice de classe {   fonction publique computeValue() : entier { renvoie ($this -> Valeur * 2); }   }   classe Test {   protégé int $Valeur;   protégé Calculatrice $Calculator;   fonction publique __construct(int $Valeur, Calculatrice $Calculatrice) { $ceci -> Valeur = $Valeur ; $ceci -> Calculatrice = $Calculatrice ; }   }

Le composant calculateur est désormais sa propre unité autonome avec une interface publique testable. Cela va de pair avec l'injection de dépendances – la classe Test reçoit maintenant une calculatrice qui implémente la logique de calcul.

Utilisation de la réflexion avec des valeurs imprévisibles

La réflexion est également utile lorsque vous écrivez du code générique dans un framework. Vous devrez peut-être vous connecter à des types fournis par l'utilisateur que vous ne pouvez pas anticiper. La réflexion peut vous aider lorsque vous ne savez pas quelles méthodes et propriétés un type expose.

Vous pouvez obtenir une image de la fonctionnalité du type sans aucune connaissance préalable de sa source. Ceci est utile dans le contexte des composants de journalisation et de rapport d'erreurs qui peuvent vouloir vider la liste des membres de toute classe à laquelle ils ont passé.

Les systèmes de tri des données sont souvent mis en œuvre de cette manière également. Imaginez un marshaller qui prend une classe et la convertit en une représentation JSON. Vous pouvez définir une convention selon laquelle toute méthode préfixée par get et se terminant par Json (par exemple, getUserJson()) doit être appelée par le marshaller et ajoutée à sa sortie. Reflection fournit le mécanisme pour obtenir la liste des méthodes. Vous devez ensuite implémenter une logique pour identifier ceux que vous devez appeler.

Publicité Reflection, Compilation et Assembles

Reflection fournit des fonctionnalités supplémentaires dans les langages compilés qui utilisent des bibliothèques et des assemblys liés. Les API de réflexion vous permettent d'inspecter le contenu des assemblys chargés. Dans des langages tels que C#, vous pouvez charger dynamiquement des assemblys supplémentaires à l'aide des API de réflexion.

Cette approche peut être utile si vous implémentez un système de plug-in avec des assemblys fournis par l'utilisateur. Votre programme ne saura pas quels plugins sont disponibles lorsqu'il est compilé. À chaque lancement, il devra vérifier le système de fichiers pour trouver les assemblages de plug-ins disponibles. Une fois qu'un plugin a été trouvé, Reflection fournit un mécanisme pour charger et instancier ses membres.

Vous pouvez inspecter le plugin, trouver les classes qu'il fournit et l'enregistrer avec votre application. Une inspection plus approfondie de l'assemblage peut fournir le nom et la version du plug-in à afficher dans vos journaux et votre interface utilisateur.

La réflexion peut également être utilisée pour désactiver les assemblages en fonction de la configuration externe. Disons que vous écrivez une application qui enregistre les fichiers image sur le stockage. Vous pouvez avoir un LocalStorageDriver, FtpStorageDriver et AmazonS3StorageDriver, chacun contenu dans son propre assembly (un .dll en C#).

En utilisant la réflexion, vous pouvez proposer un “pilote de stockage” clé dans le fichier de configuration de votre système. L'assembly approprié serait chargé dynamiquement en fonction de la valeur de votre fichier de configuration. Vous inspecterez l'assembly pour découvrir la classe qui implémente votre interface StorageDriver.

Cette approche vous permet de changer les composants de votre système lors de l'exécution. Vous n'avez pas besoin de recompiler ou de redémarrer votre programme pour passer d'un assembly à l'autre. Cela vous donne une flexibilité accrue et aide à la mise en œuvre des directives de configuration.

Déclarations d'évaluation

Publicité

La réflexion est étroitement liée à eval. De nombreux langages de programmation offrent un moyen d'exécuter des valeurs de chaîne dynamiques en tant que code source.

Eval est une permutation de réflexion avec une puissance presque illimitée. Il vous permet de créer et d'exécuter un nouveau code dans un programme en direct. Cela pose un problème de sécurité potentiellement catastrophique si l'entrée de l'utilisateur est introduite dans la chaîne d'évaluation.

Une instruction eval doit être utilisée lorsque vous n'avez plus d'autres options. Vous devez vous assurer que l'instruction est exécutée dans un contexte restreint qui ne peut pas être exploité par les utilisateurs. N'oubliez pas qu'une injection de code réussie donnerait à un attaquant les mêmes pouvoirs que votre code d'application habituel.

Conclusion

La réflexion est une technique de programmation qui donne des capacités d'introspection au code. L'utilisation efficace de la réflexion vous permet d'écrire des systèmes plus dynamiques et de bénéficier d'une automatisation accrue. Vous pouvez également utiliser la réflexion pour tester un code privé autrement inaccessible.

Vous devez faire preuve de prudence. Les API de réflexion dans les langages de programmation sont puissantes, donc avec elles vient la responsabilité. Le plus gros problème est la capacité de la réflexion à subvertir les protections fournies par votre langage de programmation.

La réflexion permet l'existence de scénarios qui seraient autrement impossibles, tels que l'écriture sur “immuable” ; variables et l'utilisation publique généralisée de méthodes privées. Vous devez pouvoir faire confiance à votre code pour respecter les règles de son langage. L'utilisation des API de réflexion doit donc être soigneusement étudiée et limitée à des sections spécifiques de votre système.