|
|
|
|
|
by jonathlee
4923 days ago
|
|
To the best of my knowledge Common Lisp methods (CLOS) already allows the ability to do things such as your rendering co-ordinate transformation example already without requiring extraneous abstract method definitions. Generic functions and their associated methods allow :before, :after and :around qualifiers to be added to specify the order in which they are run without the need of creating secondary method names for each layer of subclasses. For base class methods that need code to be run before or after the more-specific subclass method is called, a :before/:after method(s) is written that will be executed before the more-specific subclass "overridden" unqualified method is called. The developer of the subclass doesn't have to care/know that the base class is still doing some work there. Conversely, if this base :before method needs to be modified, it too can be overridden with a corresponding :before qualified method (with/or without a call back to the base :before method via call-next-method) or even totally shadowed by an :around qualified method in the subclass. This seems to be the best of both worlds. The original base class developer can specify code that needs to be executed for
each overridden method without the sub-class developer needing to be aware of it without handcuffing the sub-class developer
in handling edge-cases that the base-class developer didn't foresee. Here is the example code that implements this: (defclass game-object ()
((x :accessor x :initarg :x :initform 0)
(y :accessor y :initarg :y :initform 0)))
(defmethod set-transform (renderer x y)
;; set the renderer coordinates here.
)
(defmethod draw-image (renderer image)
;; draw image here
)
(defgeneric render (game-object renderer)
(:documentation "Render GAME-OBJECT to display using RENDERER."))
(defmethod render :before (game-object renderer)
(set-transform renderer (x game-object) (y game-object)))
(defmethod render (game-object renderer)
(draw-image renderer game-object))
;; Class where the :before base class method just runs
(defclass scary-monster (game-object)
((image :accessor image :initarg :image)))
(defmethod render ((game-object scary-monster) renderer)
(draw-image renderer (image game-object)))
;; Class where the :before base class method is prevented from running
;; to prevent some undesirable action.
(defclass happy-monster (game-object)
((image :accessor image :initarg :image)))
(defmethod render :around ((game-object happy-monster) renderer)
(draw-image renderer (image game-object)))
;; REPL session with the RENDER, SET-TRANSFORM and DRAW-IMAGE methods traced
CL-USER> (render (make-instance 'scary-monster :x 1 :y 2 :image 'a) nil)
0: (RENDER #<SCARY-MONSTER {2456D3F1}> NIL)
1: (SET-TRANSFORM NIL 1 2)
1: SET-TRANSFORM returned NIL
1: (DRAW-IMAGE NIL A)
1: DRAW-IMAGE returned NIL
0: RENDER returned NIL
NIL
CL-USER> (render (make-instance 'happy-monster :x 3 :y 4 :image 'b) nil)
0: (RENDER #<HAPPY-MONSTER {246A1689}> NIL)
1: (DRAW-IMAGE NIL B)
1: DRAW-IMAGE returned NIL
0: RENDER returned NIL
NIL
|
|