Che cos'è la “Riflessione” nella programmazione?

0
220
Shutterstock/whiteMocca

La programmazione riflessiva è un meccanismo che consente a un processo di capacità introspettive. Le API di riflessione integrate nei linguaggi di programmazione consentono di ispezionare il codice in fase di esecuzione. Puoi usare questa capacità per conoscere la base di codice circostante e i suoi contenuti.

Si parla spesso di riflessione nel contesto della programmazione orientata agli oggetti. Si usa spesso la reflection per scoprire le entità di codebase in fase di esecuzione. L’API di riflessione del linguaggio ti consentirà di ispezionare classi, metodi, proprietà e tipi dal tuo sistema. Ciò ti consente di creare funzionalità più dinamiche.

I sistemi che utilizzano la riflessione sono in grado di interrogare e modificare i propri ambienti. È qui che la riflessione differisce dalla semplice introspezione di valore. Un linguaggio con pieno supporto per la riflessione consentirà la modifica della base di codice in fase di esecuzione, consentendo effettivamente alla fonte di riscrivere aspetti di se stessa.

Un esempio di riflessione

< p>Un uso comune della riflessione è durante i test. La riflessione può aiutarti a prendere in giro le classi esponendo i loro comportamenti interni. Un metodo di classe che è protetto o privato di solito non è verificabile; usando la riflessione, puoi ignorare il vincolo di visibilità in modo che diventi pubblico nei tuoi test unitari.

prova di classe {   int protetto $Valore;   funzione pubblica __construct(int $Value) { $questo -> Valore = $ Valore; }   funzione protetta computeValue() : int { restituisce ($this -> Valore * 2); }   }   /** * Senza riflessione */  $t = nuovo Test(10);   //Errore: il metodo non è accessibile pubblicamente assert($t -> computeValue() === 20);   /** * Utilizzo della riflessione */  $reflectionMethod = nuovo ReflectionMethod(Test::CLASS, "computeValue"); $Metodo di riflessione -> setAccessibile(true);   $t = nuovo Test(10);   //Ora funziona! assert($reflectionMethod -> invoca($t) === 20);

In questo esempio che utilizza PHP, la classe Test definisce un metodo protetto utilizzato internamente. Poiché il metodo sta eseguendo un calcolo, potresti volerlo testare unitariamente. Non è possibile chiamare il metodo esternamente, ma l'API Reflection di PHP consente di aggirare i vincoli di visibilità. Un'istanza di ReflectionMethod fornisce informazioni sul metodo e consente di invocare una versione modificata.

Annuncio

Anche se questo è utile, dovresti essere consapevole che può essere usato in modo improprio. L'uso diffuso della riflessione nei test è spesso indicativo di problemi più grandi nella tua base di codice. Implica la classe’ interfaccia è eccessivamente restrittiva e inadatta alle sue responsabilità. In molti casi, è più appropriato eseguire il refactoring del metodo protetto in una nuova classe che espone la propria interfaccia pubblica.

Ecco come potrebbe apparire nell'esempio mostrato sopra:

Calcolatrice di classe {   funzione pubblica computeValue() : int { restituisce ($this -> Valore * 2); }   }   prova di classe {   int protetto $Valore;   Calcolatrice protetta $ Calcolatrice;   public function __construct(int $Value, Calcolatrice $Calcolatrice) { $questo -> Valore = $ Valore; $questo -> Calcolatrice = $Calcolatrice; }   }

Il componente della calcolatrice ora è un'unità autonoma con un'interfaccia pubblica verificabile. Questo va di pari passo con l'iniezione di dipendenza – alla classe Test è ora assegnato un calcolatore che implementa la logica di calcolo.

Uso di Reflection con valori imprevedibili

Reflection è utile anche quando si scrive codice generico all'interno di un framework. Potrebbe essere necessario interfacciarsi con tipi forniti dall'utente che non puoi prevedere. La riflessione può essere d'aiuto quando non si conoscono i metodi e le proprietà esposti da un tipo.

È possibile ottenere un'immagine della funzionalità del tipo senza alcuna conoscenza precedente della sua origine. Questo è utile nel contesto della registrazione e dei componenti di segnalazione degli errori che potrebbero voler scaricare l'elenco dei membri di qualsiasi classe che sono passati.

Anche i sistemi di marshalling dei dati sono spesso implementati in questo modo. Immagina un marshaller che prende una classe e la converte in una rappresentazione JSON. Potresti definire una convenzione secondo cui qualsiasi metodo che ha il prefisso get e termina con Json (ad esempio getUserJson()) deve essere chiamato dal marshaller e aggiunto al suo output. Reflection fornisce il meccanismo per ottenere l'elenco dei metodi. Dovresti quindi implementare la logica per identificare quelli che dovresti chiamare.

Reflection, Compilation e Assemble

Advertisement

Reflection fornisce funzionalità aggiuntive nei linguaggi compilati che utilizzano librerie e assembly collegati. Le API Reflection consentono di ispezionare il contenuto degli assembly caricati. In linguaggi come C#, è possibile caricare dinamicamente ulteriori assembly utilizzando le API Reflection.

Questo approccio può essere utile se stai implementando un sistema di plug-in con assembly forniti dall'utente. Il tuo programma non saprà quali plugin sono disponibili quando viene compilato. Ogni volta che viene avviato, sarà necessario controllare il filesystem per trovare gli assembly di plug-in disponibili. Una volta trovato un plugin, Reflection fornisce un meccanismo per caricare e istanziare i suoi membri.

Potresti ispezionare il plugin, trovare le classi che fornisce e registrarlo con la tua applicazione. Un'ulteriore ispezione dell'assembly potrebbe fornire il nome e la versione del plug-in da visualizzare nei registri e nell'interfaccia utente.

La riflessione può essere utilizzata anche per scambiare gli assembly in base alla configurazione esterna. Supponiamo che tu stia scrivendo un'applicazione che salva i file di immagine nella memoria. Potresti avere LocalStorageDriver, FtpStorageDriver e AmazonS3StorageDriver, ciascuno contenuto nel proprio assembly (un .dll in C#).

Usando la riflessione, potresti offrire un “driver di archiviazione” key nel file di configurazione del sistema. L'assembly appropriato verrebbe caricato dinamicamente in base al valore del file di configurazione. Dovresti ispezionare l'assembly per scoprire la classe che implementa la tua interfaccia StorageDriver.

Questo approccio ti consente di cambiare i componenti del tuo sistema in fase di runtime. Non è necessario ricompilare o riavviare il programma per passare da un assembly all'altro. Questo ti dà una maggiore flessibilità e aiuta l'implementazione delle direttive di configurazione.

Dichiarazioni di valutazione

Pubblicità

La riflessione è strettamente correlata alla valutazione. Molti linguaggi di programmazione forniscono un modo per eseguire valori di stringa dinamici come codice sorgente.

Eval è una permutazione della riflessione con un potere quasi illimitato. Ti consente di creare ed eseguire nuovo codice all'interno di un programma live. Ciò pone un problema di sicurezza potenzialmente catastrofico se l'input dell'utente viene inserito nella stringa eval.

Un'istruzione eval dovrebbe essere utilizzata quando non hai altre opzioni. È necessario assicurarsi che l'istruzione venga eseguita in un contesto limitato che non può essere sfruttato dagli utenti. Ricorda che un'iniezione di codice riuscita darebbe a un utente malintenzionato gli stessi poteri del normale codice dell'applicazione.

Conclusione

La riflessione è una tecnica di programmazione che fornisce capacità introspettive al codice. L'uso efficace della riflessione consente di scrivere sistemi più dinamici e di beneficiare di una maggiore automazione. Puoi anche usare la reflection per testare un codice privato altrimenti irraggiungibile.

È necessario prestare attenzione. Le API Reflection nei linguaggi di programmazione sono potenti, quindi con esse derivano le responsabilità. Il problema più grande è la capacità della riflessione di sovvertire le protezioni fornite dal linguaggio di programmazione.

La riflessione consente l'esistenza di scenari che sarebbero altrimenti impossibili, come le scritture su “immutabile&#8221 ; variabili e uso pubblico diffuso di metodi privati. Dovresti essere in grado di fidarti del tuo codice per rispettare le regole della sua lingua. L'uso delle API di riflessione deve quindi essere considerato attentamente e limitato a sezioni specifiche del sistema.