Wat is “reflectie” in programmeren?

0
238
Shutterstock/whiteMocca

Reflectief programmeren is een mechanisme dat een proces introspectieve mogelijkheden biedt. Met de reflectie-API's die in programmeertalen zijn ingebouwd, kunt u code tijdens runtime inspecteren. Je kunt dit vermogen gebruiken om meer te weten te komen over de omringende codebase en de inhoud ervan.

Over reflectie wordt vaak gesproken in de context van objectgeoriënteerd programmeren. U gebruikt reflectie vaak om codebase-entiteiten tijdens runtime te ontdekken. Met de reflectie-API van de taal kunt u klassen, methoden, eigenschappen en typen vanuit uw systeem inspecteren. Hierdoor kunt u meer dynamische functionaliteit bouwen.

Systemen die gebruik maken van reflectie zijn in staat om hun eigen omgeving te bevragen en te wijzigen. Dit is waar reflectie verschilt van gewone waarde introspectie. Een taal met volledige ondersteuning voor reflectie maakt modificatie van de codebase tijdens runtime mogelijk, waardoor de bron in feite aspecten van zichzelf kan herschrijven.

Een voorbeeld van reflectie

< p>Een veelgebruikt gebruik voor reflectie is tijdens het testen. Reflectie kan je helpen de lessen te spotten door hun interne gedrag bloot te leggen. Een klassemethode die beschermd of privé is, is meestal niet testbaar; door reflectie te gebruiken, kunt u de zichtbaarheidsbeperking opheffen, zodat deze openbaar wordt in uw eenheidstests.

klasse Test {   beschermd int $Value;   publieke functie __construct(int $Value) { $dit -> Waarde = $Waarde; }   beveiligde functie computeValue() : int { retourneer ($this -> Waarde * 2); }   }   /** * Zonder reflectie */  $t = nieuwe Test(10);   //Fout – de methode is niet publiekelijk toegankelijk assert($t -> computeValue() === 20);   /** * Reflectie gebruiken */  $reflectionMethod = nieuwe ReflectionMethod(Test::CLASS, "computeValue"); $reflectionMethode -> setToegankelijk(true);   $t = nieuwe Test(10);   //Dit werkt nu! assert($reflectionMethod -> aanroepen van($t) === 20);

In dit voorbeeld met PHP definieert de klasse Test een beveiligde methode die intern wordt gebruikt. Aangezien de methode een berekening uitvoert, wilt u deze misschien testen. Je kunt de methode niet extern aanroepen, maar met de Reflection API van PHP kun je de zichtbaarheidsbeperkingen omzeilen. Een ReflectionMethod-instantie geeft informatie over de methode en laat je een aangepaste versie aanroepen.

Advertentie

Hoewel dit nuttig is, moet u zich ervan bewust zijn dat het kan worden misbruikt. Wijdverbreid gebruik van reflectie bij het testen is vaak een indicatie van grotere problemen in uw codebase. Het impliceert de klasse’ interface is te beperkend en niet geschikt voor zijn verantwoordelijkheden. In veel gevallen is het passender om de beschermde methode te refactoren in een nieuwe klasse die zijn eigen openbare interface blootlegt.

Zo zou dat eruit kunnen zien voor het bovenstaande voorbeeld:

klasse Rekenmachine {   publieke functie computeValue() : int { retourneer ($this -> Waarde * 2); }   }   klasse Test {   beschermd int $Value;   beveiligde rekenmachine $ rekenmachine;   openbare functie __construct(int $Value, Calculator $Calculator) { $dit -> Waarde = $Waarde; $dit -> Rekenmachine = $Rekenmachine; }   }

De rekenmachinecomponent is nu zijn eigen zelfstandige eenheid met een testbare openbare interface. Dit gaat hand in hand met afhankelijkheidsinjectie – de klasse Test krijgt nu een rekenmachine die de berekeningslogica implementeert.

Reflectie gebruiken met onvoorspelbare waarden

Reflection is ook handig als je generieke code schrijft binnen een framework. Mogelijk moet u communiceren met door de gebruiker geleverde typen die u niet kunt voorzien. Reflectie kan helpen als je niet weet welke methoden en eigenschappen een type blootlegt.

Je kunt een beeld krijgen van de functionaliteit van het type zonder enige voorafgaande kennis van de bron. Dit is handig in de context van logboekregistratie en foutrapportagecomponenten die mogelijk de ledenlijst willen dumpen van elke klasse waaraan ze zijn doorgegeven.

Ook datamarshallingsystemen worden vaak op deze manier geïmplementeerd. Stel je een marshaller voor die een klasse neemt en deze omzet in een JSON-representatie. Je zou een conventie kunnen definiëren dat elke methode die voorafgaat met get en eindigt met Json (bijv. getUserJson()) door de marshaller moet worden aangeroepen en aan de uitvoer moet worden toegevoegd. Reflection biedt het mechanisme om de lijst met methoden te krijgen. Je zou dan logica implementeren om te identificeren welke je zou moeten aanroepen.

Reflection, Compilation, and Assembles

Advertentie

Reflection biedt extra mogelijkheden in gecompileerde talen die gebruikmaken van gekoppelde bibliotheken en samenstellingen. Met Reflection API's kunt u de inhoud van geladen assemblages inspecteren. In talen zoals C# kun je dynamisch extra assembly's laden door Reflection API's te gebruiken.

Deze benadering kan handig zijn als u een plug-insysteem implementeert met door de gebruiker geleverde assemblages. Uw programma weet niet welke plug-ins beschikbaar zijn wanneer het wordt gecompileerd. Elke keer dat het wordt gestart, moet het het bestandssysteem controleren om beschikbare plug-in-assembly's te vinden. Zodra een plug-in is gevonden, biedt Reflection een mechanisme om zijn leden te laden en te instantiëren.

Je zou de plug-in kunnen inspecteren, de klassen die deze biedt, kunnen vinden en deze bij je toepassing kunnen registreren. Verdere inspectie van de assembly kan de naam en versie van de plug-in opleveren voor weergave in uw logs en gebruikersinterface.

Reflectie kan ook worden gebruikt om samenstellingen uit te schakelen op basis van externe configuratie. Laten we zeggen dat u een toepassing schrijft die afbeeldingsbestanden opslaat in de opslag. Mogelijk hebt u een LocalStorageDriver, FtpStorageDriver en AmazonS3StorageDriver, elk in een eigen assembly (een .dll in C#).

Met reflectie zou je een “storage driver” sleutel in het configuratiebestand van uw systeem. De juiste assembly wordt dynamisch geladen op basis van de waarde van uw configuratiebestand. U zou de assembly moeten inspecteren om de klasse te ontdekken die uw StorageDriver-interface implementeert.

Met deze benadering kunt u componenten van uw systeem tijdens runtime uitschakelen. U hoeft uw programma niet opnieuw te compileren of opnieuw te starten om tussen assembly's te wisselen. Dit geeft u meer flexibiliteit en helpt bij de implementatie van configuratierichtlijnen.

Eval Statements

Advertentie

Reflectie is nauw verwant aan eval. Veel programmeertalen bieden een manier om dynamische stringwaarden als broncode uit te voeren.

Eval is een permutatie van reflectie met bijna onbeperkte kracht. Hiermee kunt u nieuwe code maken en uitvoeren binnen een live programma. Dit vormt een potentieel catastrofaal beveiligingsprobleem als gebruikersinvoer in de eval-reeks wordt ingevoerd.

Een eval-statement moet worden gebruikt als er geen andere opties meer zijn. U moet ervoor zorgen dat de instructie wordt uitgevoerd in een beperkte context die niet door gebruikers kan worden misbruikt. Onthoud dat een succesvolle code-injectie een aanvaller dezelfde bevoegdheden zou geven als uw normale applicatiecode.

Conclusie

Reflectie is een programmeertechniek die code introspectieve mogelijkheden geeft. Effectief gebruik van reflectie stelt u in staat om meer dynamische systemen te schrijven en te profiteren van meer automatisering. Je kunt reflectie ook gebruiken om de anders onbereikbare privécode te testen.

Je moet wel voorzichtig zijn. Reflectie-API's in programmeertalen zijn krachtig, dus met hen komt verantwoordelijkheid. Het grootste probleem is het vermogen van reflectie om de beveiligingen van uw programmeertaal te ondermijnen.

Reflectie staat het bestaan ​​van scenario's toe die anders onmogelijk zouden zijn, zoals schrijven naar “onveranderlijk&#8221 ; variabelen en wijdverbreid openbaar gebruik van private methoden. U moet erop kunnen vertrouwen dat uw code de regels van de taal respecteert. Het gebruik van de reflectie-API's moet daarom zorgvuldig worden overwogen en toegespitst op specifieke secties van uw systeem.