(defmethod beat ((drum snare-drum) (stick wooden-drumstick)) ...)

(defmethod beat ((drum snare-drum) (stick brush)) ...)

(defmethod beat ((drum snare-drum) (stick soft-mallet)) ...)

(defmethod beat ((drum tom-tom) (stick wooden-drumstick)) ...)

(defmethod beat ((drum tom-tom) (stick brush)) ...)

(defmethod beat ((drum tom-tom) (stick soft-mallet)) ...)

Multimethods don't help with the combinatorial explosion—if you need to model five kinds of drums and six kinds of sticks, and every combination makes a different sound, there's no way around it; you need thirty different methods to implement all the combinations, with or without multimethods. What multimethods do save you from is having to write a bunch of dispatching code by letting you use the same built-in polymorphic dispatching that's so useful when dealing with methods specialized on a single parameter.[180]

Multimethods also save you from having to tightly couple one set of classes with the other. In the drum/stick example, nothing requires the implementation of the drum classes to know about the various classes of drumstick, and nothing requires the drumstick classes to know anything about the various classes of drum. The multimethods connect the otherwise independent classes to describe their joint behavior without requiring any cooperation from the classes themselves.

To Be Continued . . .

I've covered the basics—and a bit beyond—of generic functions, the verbs of Common Lisp's object system. In the next chapter I'll show you how to define your own classes.

17. Object Reorientation: Classes

If generic functions are the verbs of the object system, classes are the nouns. As I mentioned in the previous chapter, all values in a Common Lisp program are instances of some class. Furthermore, all classes are organized into a single hierarchy rooted at the class T.

The class hierarchy consists of two major families of classes, built-in and user-defined classes. Classes that represent the data types you've been learning about up until now, classes such as INTEGER, STRING, and LIST, are all built-in. They live in their own section of the class hierarchy, arranged into appropriate sub- and superclass relationships, and are manipulated by the functions I've been discussing for much of the book up until now. You can't subclass these classes, but, as you saw in the previous chapter, you can define methods that specialize on them, effectively extending the behavior of those classes.[181]

But when you want to create new nouns—for instance, the classes used in the previous chapter for representing bank accounts—you need to define your own classes. That's the subject of this chapter.

DEFCLASS

You create user-defined classes with the DEFCLASS macro. Because behaviors are associated with a class by defining generic functions and methods specialized on the class, DEFCLASS is responsible only for defining the class as a data type.

The three facets of the class as a data type are its name, its relation to other classes, and the names of the slots that make up instances of the class.[182] The basic form of a DEFCLASS is quite simple.

(defclass name (direct-superclass-name*)

(slot-specifier*))

What Are 'User-Defined Classes'?

The term user-defined classes isn't a term from the language standard—technically what I'm talking about when I say user-defined classes are classes that subclass STANDARD-OBJECT and whose metaclass is STANDARD- CLASS. But since I'm not going to talk about the ways you can define classes that don't subclass STANDARD-OBJECT and whose metaclass isn't STANDARD- CLASS, you don't really have to worry about that. User-defined isn't a perfect term for these classes since the implementation may define certain classes the same way. However, to call them standard classes would be even more confusing since the built-in classes, such as INTEGER and STRING, are just as standard, if not more so, because they're defined by the language standard but they don't extend STANDARD-OBJECT. To further complicate matters, it's also possible for users to define new classes that don't subclass STANDARD- OBJECT. In particular, the macro DEFSTRUCT also defines new classes. But that's largely for backward compatibility—DEFSTRUCT predated CLOS and was retrofitted to define classes when CLOS was integrated into the language. But the classes it creates are fairly limited compared to DEFCLASSed classes. So in this chapter I'll be discussing only classes defined with DEFCLASS that use the default metaclass of STANDARD-CLASS, and I'll refer to them as user-defined for lack of a better term.

As with functions and variables, you can use any symbol as the name of a new class.[183] Class names are in a separate namespace from both functions and variables, so you can have a class, function, and variable all with the same name. You'll use the class name as the argument to MAKE-INSTANCE, the function that creates new instances of user-defined classes.

The direct-superclass-names specify the classes of which the new class is a subclass. If no superclasses are listed, the new class will directly subclass STANDARD- OBJECT. Any classes listed must be other user-defined classes, which ensures that each new class is ultimately descended from STANDARD-OBJECT. STANDARD-OBJECT in turn subclasses T, so all user-defined classes are part of the single class hierarchy that also contains all the built-in classes.

Eliding the slot specifiers for a moment, the DEFCLASS forms of some of the classes you used in the previous chapter might look like this:

(defclass bank-account () ...)

(defclass checking-account (bank-account) ...)

Вы читаете Practical Common Lisp
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату