[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Initialize-instance
- To: Steve Strassmann <straz@cambridge.apple.com>, jbk@world.std.com (Jeffrey B Kane)
- Subject: Re: Initialize-instance
- From: bill@cambridge.apple.com (Bill St. Clair)
- Date: Mon, 6 Jul 1992 15:32:41 -0500
- Cc: info-mcl
>>Date: Mon, 6 Jul 92 10:38:38 -0400
>>From: jbk@world.std.com (Jeffrey B Kane)
>>To: info-mcl@cambridge.apple.com
>>Subject: Initialize-instance
>>
>>I have another couple of quick CLOS related questions. The first has to do
>>with defining my own "initialize-instance" method for my classes. For my
>>simplest example, I didn't need any parameters, so I tried:
>>
>>(defclass twindow (window)
>> ((garbage :initarg :garbage ; holds some value that I might need
>> :initform nil
>> :accessor garbage)))
>>
>>(defmethod initialize-instance ((me twindow) &rest)
>> (print "Hi, I'm initializing an instance of a twindow")
>> (initialize-instance (coerce me window)))
>>
>Forget the coerce command, that's not what you want at all.
>Here's two different ways to do this:
>
>; This doesn't shadow the previous definition, it just runs BEFORE it.
>; Also check out AFTER methods.
>(defmethod initialize-instance :before ((me twindow) &rest rest)
> (declare (ignore rest))
> (print "Hi, I'm initializing an instance of a twindow"))
>
>; This wraps AROUND the prevous definition. You can provide
>; different args to CALL-NEXT-METHOD.
>(defmethod initialize-instance :around ((me twindow) &rest rest)
> (declare (ignore rest))
> (print "Hi, I'm about to initialize an instance of a twindow")
> (call-next-method)
> (print "Hi, I just initialized an instance of a twindow"))
>
>
This is a matter of taste, but I rarely use :AROUND methods. I save them
for cases where I want to add a patch to some existing code. Arguably,
patches are better done with ADVISE, but I try to save :AROUND methods
for patches anyway. I would code Steve's second example as:
(defmethod initialize-instance ((me twindow) &rest rest)
(declare (ignore rest))
(print "Hi, I'm about to initialize an instance of a twindow")
(call-next-method)
(print "Hi, I just initialized an instance of a twindow"))
Sometimes you DO want an :AROUND method.
1) You want to get control around all :BEFORE, primary, & :AROUND methods.
An :AROUND method can decide not to CALL-NEXT-METHOD, eliding execution
of the :BEFORE & :AFTER methods. If a primary method does not CALL-NEXT-METHOD,
the :BEFORE & :AFTER methods will run (the :BEFORE methods run before the
primaries).
2) :BEFORE & :AFTER methods get the arguments that were passed by the inner-most
:AROUND method. Hence, if supply changed arguments to CALL-NEXT-METHOD from a
primary method, the :AFTER methods will see the original, not the changed
args:
? (defmethod primary ((x integer))
x)
#<STANDARD-METHOD PRIMARY (INTEGER)>
? (defmethod primary :before ((x integer))
(print `(:before ,x)))
#<STANDARD-METHOD PRIMARY :BEFORE (INTEGER)>
? (defmethod primary :after ((x integer))
(print `(:after ,x)))
#<STANDARD-METHOD PRIMARY :AFTER (INTEGER)>
? (defmethod primary ((x fixnum))
(print `(primary ,x))
(call-next-method (1+ x)))
#<STANDARD-METHOD PRIMARY (FIXNUM)>
? (primary 1)
(:BEFORE 1)
(PRIMARY 1)
(:AFTER 1)
2
? (defmethod around ((x integer))
x)
#<STANDARD-METHOD AROUND (INTEGER)>
? (defmethod around :before ((x integer))
(print `(:before ,x)))
#<STANDARD-METHOD AROUND :BEFORE (INTEGER)>
? (defmethod around :after ((x integer))
(print `(:after ,x)))
#<STANDARD-METHOD AROUND :AFTER (INTEGER)>
? (defmethod around :around ((x integer))
(print `(:around ,x))
(call-next-method (1+ x)))
#<STANDARD-METHOD AROUND :AROUND (INTEGER)>
? (around 1)
(:AROUND 1)
(:BEFORE 2)
(:AFTER 2)
2
?
Finally, there's a canonical use of CALL-NEXT-METHOD from inside of an
INITIALIZE-INSTANCE method that happens often enough that it's worth
mentioning. Sometimes you want to change a few args to the usual
INITIALIZE-INSTANCE method, then do a little more work. I often find
myself doing this when I am initializing a custom window. I want the
Macintosh WindowRecord to be created, but I don't want the window to
be shown on the screen until everything is done:
(defclass my-window (window) ())
(defmethod initialize-instance ((w my-window) &rest rest &key (window-show t))
(apply #'call-next-method
w ; the window
:window-show nil ; don't show it yet
rest) ; include other initargs
(do-hairy-initialization w)
(if window-show
; Show the window unless my caller said not to
(window-show w)))
Note that the :WINDOW-SHOW keyword may appear twice in the argument list given
to the next method. Common Lisp is defined to look at only the first occurrence
of each keyword given to a function that accepts keyword arguments.
Clear as mud, right?