Méthode

Une méthode CLOS est une implémentation partielle d'une fonction générique. L'intérêt d'une fonction générique réside dans le fait que ses implémentations peuvent être physiquement réparties, en particulier dans des fichiers différents. De plus, elles peuvent être rajoutées a posteriori, ce qui permet d'adapter un programme existant (ou une bibliothèque existante) à de nouveaux besoins.

Un appel à une fonction générique peut provoquer un appel à une ou plusieurs de ses méthodes. Les paramètres de la lambda-liste d'une méthode peuvent être qualifiés à l'aide de classes, indiquant que la méthode n'est applicable que pour des arguments instances de ces classes. Le choix de la méthode à appliquer se fait grâce à un mécanisme de sélection détaillé dans la section 16.4.

La lambda-liste d'une méthode, appelée lambda-liste spécialisée (en Anglais : specialized lambda list) est similaire à celle d'une fonction ordinaire (voir section 7.1), à la différence qu'un paramètre obligatoire d'une lambda-liste spécialisée peut être complété du nom d'une classe ou d'un objet. Dans ce cas, la méthode ne sera applicable que si les arguments sont instances de la classe ou égaux aux objets. Exemple :

(defmethod graft-width ((graft clx-graft) &key (units :device))
  (let ((screen (clx-port-screen (port graft))))
    (ecase units
      (:device (xlib:screen-width screen))
      (:inches (/ (xlib:screen-width-in-millimeters screen)
                  25.4s0))
      (:millimeters (xlib:screen-width-in-millimeters screen))
      (:screen-sized 1))))

Dans cet exemple, on rajoute une méthode pour la fonction générique graft-width. Elle a un paramètre obligatoire graft qui, pour que la méthode soit applicable, doit être instance (directe ou indirecte) de la classe clx-graft. Elle a aussi un paramètre mot-clé units dont la valeur par défaut (si l'argument correspondant n'est pas fourni) est le symbole (mot-clé) :device. C'est une utilisation assez fréquente des symboles en Common Lisp d'être des valeurs d'énumération. Exemple :

(defmethod medium-gcontext ((medium clx-medium)
                            (ink (eql +background-ink+)))
  (medium-gcontext medium (medium-background medium)))

Cet exemple illustre une spécialisation sur un objet au lieu d'une classe. Il y a deux paramètres obligatoires, medium et ink. Le premier est spécialisé par la classe clx-medium, alors que le deuxième est spécialisé sur l'objet valeur de la variable (globale) +background-ink+. C'est avec la syntaxe (eql expression)expression est une expression arbitraire qui est évaluée une seule fois lors de la définition de la méthode. Exemple :

(defmethod medium-draw-points* ((medium clx-medium) coord-seq)
  (loop for (x y) on coord-seq by #'cddr
        do (medium-draw-point* medium x y)))

Cet exemple illustre l'utilisation de paramètres obligatoires non spécialisés. Le deuxième paramètre n'est pas spécialisé, ce qui veut dire que la méthode est applicable pour n'importe quelle valeur du deuxième argument. Exemple :

(defmethod graft ((graft graft))
  graft)

Dans cet exemple, une méthode pour la fonction générique graft est définie. Elle a un seul paramètre dont le nom est graft et dont la classe de spécialisation s'appelle graft. La méthode renvoie la valeur de son argument.

Toutes les méthodes définies ci-dessus sont des méthodes primaires. CLOS utilise aussi la notion de méthode auxiliaire. Il y a trois rôles possibles pour une méthode auxiliaire : :before, :after et :around. La signification de ces rôles sera détaillée dans la section 16.4. Voici quelques exemples de définitions de ce type de méthodes :

(defmethod generate-panes :before (fm  (frame application-frame))
  (declare (ignore fm))
  (when (and (slot-boundp frame 'pane)
             (frame-pane frame))
    (sheet-disown-child (frame-top-level-sheet frame)
                        (frame-pane frame))))

(defmethod adopt-frame :after ((fm clx-frame-manager)
                               (frame menu-frame))
  (xlib:map-window (sheet-direct-mirror
                    (slot-value frame 'top-level-sheet))))

(defmethod run-frame-top-level :around ((frame application-frame))
  (let ((*application-frame* frame)
        (*input-context* nil)
        (*input-wait-test* nil)
        (*input-wait-handler* nil)
        (*pointer-button-press-handler* nil))
    (declare (special *input-context*
                      *input-wait-test*
                      *input-wait-handler*
                      *pointer-button-press-handler*))
    (call-next-method)))

Irene DURAND
2011-10-14