Most CLOS objects are implemented
as standard-instance
s.
A standard-instance
is a collection of storage cells
called slots, and the slots are addressed by name. You
could imagine an alternative implementation where
an instance is a vector that is addressed by an integer, but named
slots are more flexible and abstract.
Many object systems map the named fields of an instance into
lexically scoped variables. Within a method body, you can just
refer to the slot as if it were a variable. Assignment to the
variable effectively updates the slot. There are pros and cons to
this. On the plus side, it is very convenient to refer to slots as
if they were variables. On the minus side, it is difficult to
rename a slot, because you have to rename all the references to it,
and slot names can collide with lexical variables. It can make the
code brittle with regard to slot naming. But CLOS lets you choose if
you want to do this or not. The with-slots
macro
installs a set of symbol macros that let you refer to each slot as if
it were a variable.
But the slots of an instance are an implementation detail. You really want an abstract API for your objects. You want logical fields to be accessed by getter and setter functions. The logical field will typically be backed by a slot, but it could be a computed value. Logical fields are more flexible and abstract than slots.
When you define a slot, you can specify a :reader
and :accessor
function for that slot. This covers the
very common use case of a getter/setter pair that is backed by a
slot in the instance.
You can also map the logical fields of an instance into lexical
variables. The with-accessors
macro installs a set of
symbol macros that let you refer to each logical field as if it were
a lexical varible.
I often see with-slots
used
where with-accessors
would be more appropriate. If you
find yourself wanting to use with-slots
, consider if
you should be using with-accessors
instead.
Personally, I prefer to avoid both with-slots
and
with-accessors
. This makes CLOS objects act more like
structs. Structs are easier for me to understand than magic lexical
variables.
Tip
The accessors for slots are generic. You therefore want them to
have generic names. For example, suppose you have a point
class
with an x
and y
slot. You don't want to
call your accessors point-x
and point-y
because the names would be inappropriate for subclasses. You want
to have names something like get-x
and get-y
.
These functions would naturally work on subclasses of points, but
because get-x
and get-y
are generic, you
could also extend them to work on any class that has a
meaningful x
and y
.
No comments:
Post a Comment