• Aucun résultat trouvé

k’héritage et la liaison dynamique sont deux mécanismes de la programmation par objets qui sont utiles pour le détournement. ri l’on écrit un interpréteur en suivant le patron interpréteur en iava, où chaque terme du langage est représenté par une classe qui comporte une méthodeev6l:

interf6ce Term { int ev6l(); } cl6ss Num implements Term {

int n;

Num(int n) { this.n = n; } int ev6l() { return n; } }

cl6ss Plus implements Term { Term 6, b;

Plus(Term 6, Term b) { this.6 = 6; this.b = b; }

. . En programmation par objets érinacé

int ev6l() {

return 6.ev6l() + b.ev6l(); } }

Alors on peut facilement réutiliser cette classe pour créer une variante :

cl6ss NumDouble extends Num { int ev6l() { return n * ; } }

hl suǦt d’hériter deNum, et de surcharger la méthodeev6l. dn- suite, il faut remplacer les instances deNumpar des instances de

NumDouble. tne façon de faire cela de manière transparente est d’utiliser un constructeur virtuel qui serait un argument du pro- gramme principal :

m6in(NumF6ctory num) {

Term e = new Plus(num.new( ), num.new( )); e .ev6l();

}

k’exécution dee .ev6l()invoque la méthodePlus.ev6l, qui récursivement invoque les méthodesev6lde ses termes membres

6et b. oar liaison dynamique, l’évaluation de ces termes pourra faire appel à la méthodeNum.ev6l ouNumDouble.ev6l, suivant les instances retournées par le constructeur virtuel NumF6ctory. nn peut donc facilement substituer un code par un autre simple- ment en contrôlant le constructeur virtuel, mais sans avoir à modi- Ǥer le code existant de l’interpréteur. bette construction rappelle d’ailleurs des solutions déjà décrites en §3.6, comme le visiteur ex- tensible ou les algèbres d’objets. keur point commun est qu’elles reposent toutes sur l’indirection créée par la liaison dynamique : un même appel commenf.newou e .ev6lpourra invoquer des codes diǣérents suivant les receveurs. bes mécanismes de la pro- grammation par objets sont donc tout à fait adéquats pour détour- ner des interpréteurs, et plus généralement des programmes.

tn inconvénient de ces solutions est qu’elles sont dépendantes de la structure du programme. ri l’interpréteur n’utilise pas de constructeurs virtuels, il n’est plus aussi facile d’insérer notre éva- luation alternative NumDouble; il faudra modiǤer l’interpréteur. ce même, dans un interpréteur plus complexe que celui de l’exemple, la fonctionnalité d’évaluation pourra être répartie sur plusieurs classes, potentiellement sur plusieurs méthodes ; il faudra alors hé- riter de toutes ces classes pour surcharger toutes les méthodes per- tinentes et trouver le moyen de remplacer les instances originales par les instances de l’extension. k’héritage et la liaison dynamique ne sont pas suǦsants pour détourner un programme quelconque sans y apporter un minimum de modiǤcations au préalable.

étésie Variations de détournement sur un interpréteur minimal

En JavaScript

dn iavarcript, l’héritage est réalisé par la délégation. be n’est pas très diǣérent de la situation en iava, si bien que les mêmes remarques s’y appliquent. iavarcript permet cependant de modi er le lien de délégation après la création de l’objet, ce qui ouvre des possibilités intéressantes pour le détournement.

orenons un simple objet qui représente une constante dans le langage : num new eval n1 objet global n 2 function v6r num = {

new(n) { return {__proto__: this, n }}, ev6l() { return this.n }

} v6r n = num.new( ) n .ev6l() //: objet clé valeur clé référence prototype

kégende des diagrammes qui accom- pagnent les exemples en iavarcript.

ke diagramme à côté du code illustre les objets manipulés par l’interpréteur iavarcript. kes objets y sont représentés par des boîtes qui contiennent des paires, chaque paire associant une clé à une va- leur. korsque la valeur est une référence, elle est représentée par une puce d’où part un trait vers l’objet référencé ( ). oar exemple, icinumest une référence vers un objet qui contient les clésnewet

ev6l. gabituellement, on désigne un objet par la clé qui le réfère : on parlera ici de l’objetnum, de l’objetn , etc. tne puce qui part du coin d’un objet représente le lien de délégation vers l’objet parent (le prototype). oar exemple,n anumpour parent (l’objet référencé parn a pour parent l’objet référencé parnum). kes références en pointillés sont omises par concision : icinewest une référence vers une fonction qui a peu d’importance pour la suite.

nn a plusieurs façons de modiǤer l’évaluation den après sa déǤnition. nn peut surcharger la fonction sur l’objet lui-même :

num new eval n1 objet global n 2 function eval function

n .ev6l = function() { return this.n * } n .ev6l() //:

nn voit sur le diagramme correspondant que cette nouvelle fonction n’existe que sur l’objet n . bette modiǤcation n’aǣecte donc que l’objetn , et aucune autre instance dérivée denum, exis- tante ou future.

Alternativement, on peut changer la fonction ev6l du proto- typenum: num new eval n1 objet global n 2 function n .ev6l() //:

num.ev6l = function() { return this.n * } n .ev6l() //:

bette fois, tous les objets qui délèguent l’appel de la fonction

ev6lànumutiliseront cette nouvelle version. k’objetn est aǣecté, ainsi que toutes les instances de num que l’on pourrait créer en appelantnum.new.

. . En programmation par objets embas

ri l’on souhaite éviter de toucher à num, on peut proposer un objetnum qui servira de prototype alternatif pourn :

num new