J'ai récemment eu besoin d'implémenter quelque chose comme ça pour tracer des trucs OO-récursifs compliqués. Fondamentalement, je voulais rendre une méthode "traçable" sans trop la polluer; alors peut-être que la solution pourrait être appliquée ici aussi.
D'abord, ajouter une fonction qui fait d'autres fonctions traçable:
Function::trace = do ->
makeTracing = (ctorName, fnName, fn) ->
(args...) ->
console.log "#{ctorName}:#{fnName}"
fn.apply @, args
(arg) ->
for own name, fn of arg
@prototype[name] = makeTracing @name, name, fn
Ensuite, pour l'utiliser, il suffit d'ajouter un @trace
avant chaque méthode que vous voulez tracer:
class MyClass
@trace methodA: ->
@methodB 42
@trace methodB: ->
console.log "method b called with #{n}"
Ou ajouter @ Tracez une seule fois puis indentez toutes les méthodes traçables à un niveau de plus:
class MyClass
@trace
methodA: ->
@methodB 42
methodB: (n) ->
console.log "method b called with #{n}"
Comme vous pouvez le voir, trace
abuse un peu de la syntaxe de CoffeeScript. method: -> 'foo'
à l'intérieur d'un class MyClass
est interprétée une définition de méthode. Mais @trace method: -> 'foo'
est interprété comme appelant la fonction trace
de MyClass
(qui est une instance Function
, à laquelle nous avons ajouté la fonction trace
) en lui passant un objet littéral avec une clé method
. En JavaScript ce serait quelque chose comme this.trace({method: function() {return 'foo';}})
. La fonction trace
prendra juste cet objet et itérera ses clés (les noms de méthodes) et les valeurs (les méthodes) et ajoutera des fonctions au prototype MyClass
qui enregistre leurs appels et appelle les méthodes originales.
Quoi qu'il en soit, la sortie de (new MyClass).methodA()
sera:
MyClass:methodA
MyClass:methodB
method b called with 42
Cette solution ne fonctionne pas pour les constructeurs mais, comme ils ne sont pas seulement des méthodes normales.
Vous pouvez obtenir assez chic avec ceci. Vous pouvez également consigner les arguments passés à chaque méthode, la valeur de retour, et même ajouter une indentation pour les appels imbriqués si vous le souhaitez (les traces résultantes peuvent alors être très utiles si vous devez déboguer un problème complexe = D).
Mise à jour: comme un exemple plus intéressant, voici une mini-version de l'exemple de motif composite typique, des figures géométriques et des groupes de figures: http://jsfiddle.net/2YuE7/ avec une fonction de traçage plus intéressant. Toutes les figures comprennent la méthode move
.Si nous avons ce chiffre composite et appelons move
sur elle:
f = new Composite [
new Rectangle 5, 10, 3, 4
new Composite [
new Circle 0, 0, 2
new Circle 0, 0, 4
new Circle 0, 0, 6
]
]
f.move 2, 3
La sortie de trace est:
(Composite[Rectangle[5,10,3,4],Composite[Circle[0,0,2],Circle[0,0,4],Circle[0,0,6]]]).move(2, 3)
| (Rectangle[5,10,3,4]).move(2, 3)
| -> Rectangle[7,13,3,4]
| (Composite[Circle[0,0,2],Circle[0,0,4],Circle[0,0,6]]).move(2, 3)
| | (Circle[0,0,2]).move(2, 3)
| | -> Circle[2,3,2]
| | (Circle[0,0,4]).move(2, 3)
| | -> Circle[2,3,4]
| | (Circle[0,0,6]).move(2, 3)
| | -> Circle[2,3,6]
| -> Composite[Circle[2,3,2],Circle[2,3,4],Circle[2,3,6]]
-> Composite[Rectangle[7,13,3,4],Composite[Circle[2,3,2],Circle[2,3,4],Circle[2,3,6]]]
Ceci est très cool. La capacité de consigner les arguments était aussi quelque chose à laquelle je pensais. Merci! –
@FrankLoVecchio Cool. J'ai mis à jour la réponse avec un exemple qui consigne les paramètres, renvoie les valeurs et met les journaux en retrait en fonction du niveau de récursion = D – epidemian
Cela va être une question StackOverflow très utile :) –