初始化与默认值
讲解 initialize 子句如何在对象创建后立即执行动作,以及 default-method 子句如何处理对象无法理解的消息。
初始化
先浏览一下 penguin 类:
(define-class (penguin name)
(class-vars (all-penguin nil)
(favorite-food 'tuna))
(instance-vars (hunger 50)
(weight 350))
(method (eat)
(set! hunger (- hunger 1))
(set! weight (+ weight 5))
(se 'That favorite-food '(was delicious!))))
> (define jack (instantiate penguin 'jack))
> (ask jack 'eat)
(that tuna was delicious!)
> (ask jack 'hunger)
49
> (ask jack 'weight)
355
一只企鹅有 2 个实例变量:hunger(饥饿度)和 weight(体重)。企鹅类有 2 个类变量:最喜欢的食物 tuna(金枪鱼),以及 all-penguin——一个记录所有已创建企鹅名字的列表。目前,all-penguin 从未被更新。在某些情况下,就像这里,我们希望对象在创建时就做某些事情。我们可以用 initialize 子句来做到这一点。
实例化之后
企鹅对象一被实例化,我们就希望它:
- 说出自己的名字,并且
- 把自己加入
all-penguin列表。
下面是用 initialize 子句实现的方式:
(define-class (penguin name)
(class-vars (all-penguin nil)
(favorite-food tuna))
(instance-vars (hunger 50)
(weight 350))
(initialize (print (se '(hi my name is) name))
(set! all-penguin (cons name all-penguin)))
(method (eat)
(set! hunger (- hunger 1))
(set! weight (+ weight 5))
(se 'That favorite-food '(was delicious!))))
> (define jack (instantiate penguin 'jack))
(hi my name is jack)
> (define jennie (instantiate penguin 'jennie))
(hi my name is jennie)
> (ask penguin 'all-penguin)
(jennie jack)
> (ask jack 'all-penguin)
(jennie jack)
默认方法
这是我们目前对企鹅类的定义:
(define-class (penguin name)
(class-vars (all-penguin nil)
(favorite-food 'tuna))
(instance-vars (hunger 50)
(weight 350))
(initialize (print (se '(hi my name is) name))
(set! all-penguin (cons name all-penguin)))
(method (eat)
(set! hunger (- hunger 1))
(set! weight (+ weight 5))
(se 'That favorite-food '(was delicious!))))
假设我们调用下面的方法:
> (define jack (instantiate penguin 'jack))
(hi my name is jack)
> (ask jack 'favorite-food)
tuna
> (ask jack 'eat)
(That tuna was delicious!)
> (ask jack 'back-flip)
*** Error: No method back-flip in class penguin
看起来 jack 不知道怎么 back-flip(后空翻)。我们的企鹅目前只懂有限的消息,但作为企鹅类的设计者,我们不希望它们对其他消息都抛错误。相反,如果一只企鹅收到不理解的消息,我们希望它转而去吃东西。我们可以用 default-method(默认方法)子句来做到这一点。看看我们对企鹅类新增的部分:
(define-class (penguin name)
(class-vars (all-penguin nil)
(favorite-food 'tuna))
(instance-vars (hunger 50)
(weight 350))
(initialize (print (se '(hi my name is) name))
(set! all-penguin (cons name all-penguin)))
(method (eat)
(set! hunger (- hunger 1))
(set! weight (+ weight 5))
(se 'That favorite-food '(was delicious!)))
(default-method
(print (se '(I dont know how to) message '(I will eat instead)))
(ask self 'eat)))
现在我们调用这些方法:
> (define jack (instantiate penguin 'jack))
(hi my name is jack)
> (ask jack 'back-flip)
(I dont know how to back-flip I will eat instead)
(that tuna was delicious!)
> (ask jack 'weight)
355
> (ask jack 'fly)
(I don't know how to fly I will eat instead)
(that tuna was delicious!)
> (ask jack 'weight)
360
message 与 args
注意,在上面的 default-method 中,我们用 message 得知传给对象的是什么消息。类似地,我们还可以用 args 得知其他参数,它们以一个列表的形式给出。
例如,如果我们调用
(ask jack 'do-math 1 2 5 10)
那么变量 message 会指向 'do-math,而变量 args 会指向 (1 2 5 10)。