三种局部状态变量
介绍实例化变量、实例变量和类变量三种局部状态变量的区别与用法,并用 worker 类展示类变量的共享特性。
概述
到目前为止,我们见过的局部状态变量只有实例化变量(instantiation variable)。本小节,我们来看另外两种:实例变量(instance variable)和类变量(class variable)。
实例变量
回顾一下 checking-account 类:
(define-class (checking-account init-balance)
(parent (account init-balance))
(method (write-check amount)
(ask self 'withdraw (+ amount 0.10)) )
每次开支票,账户会被额外收取 10 美分。所有 checking-account 一开始都是 10 美分的费用,但现在我们希望能够随时修改这个费用。一种办法是把 check-fee 加为实例化变量。
(define-class (checking-account init-balance check-fee)
(parent (account init-balance))
(method (write-check amount)
(ask self 'withdraw (+ amount check-fee)) )
(method (set-fee! fee)
(set! check-fee fee)) )
(define lily (instantiate checking-account 1000 0.10))
(define ted (instantiate checking-account 1000 0.10))
(define barney (instantiate checking-account 9999 0.10))
但这种写法有点冗余,因为即使我们总是希望费用从 10 美分开始,每次实例化时仍然要手动指定 check-fee。为此,我们引入一个新的子句:instance-vars,正好解决我们的问题。
(define-class (checking-account init-balance)
(parent (account init-balance))
(instance-vars (check-fee 0.10))
(method (write-check amount)
(ask self 'withdraw (+ amount check-fee)))
(method (set-fee! fee)
(set! check-fee fee)) )
实例变量 vs. 实例化变量
实例化变量同时也是实例变量;也就是说,每个实例都拥有自己私有的值。两者的区别仅在于写法和设置初始值的时机:实例化变量在调用 instantiate 时给出值,而其他实例变量的值在类定义中给出。
类变量
第三种局部状态变量是类变量(class variable)。与实例变量不同,一个类变量对整个类只有一个值,类的所有实例共享这个值。举个例子,假设我们有一个 worker 类,所有工人都在做同一个项目。也就是说,只要其中任何一个人工作,完成的工作总量就会增加。另一方面,每个工人在工作时会各自感到饥饿。因此,这个类有一个共同的 work-done 变量,而每个实例有各自独立的 hunger 变量。
(define-class (worker)
(instance-vars (hunger 0))
(class-vars (work-done 0))
(method (work)
(set! hunger (+ hunger 1))
(set! work-done (+ work-done 1))
'whistle-while-you-work ) )
> (define brian (instantiate worker))
brian
> (define matt (instantiate worker))
matt
> (ask matt 'work)
whistle-while-you-work
> (ask matt 'work)
whistle-while-you-work
> (ask matt 'hunger)
2
> (ask matt 'work-done)
2
> (ask brian 'work)
whistle-while-you-work
> (ask brian 'hunger)
1
> (ask brian 'work-done)
3
> (ask worker 'work-done)
3
如你所见,请求任何 worker 对象工作都会使 work-done 变量增加。相比之下,每个工人都有自己的 hunger 实例变量,所以 Brian 工作时,Matt 不会感到饿。你可以向任何实例询问类变量的值,也可以直接询问类本身。这是「消息必须发给实例而非类」这条通常规则的一个例外。
笔记
测试你的理解
我们来设计一个「Dog」类。对于下面每一项,判断它对于狗类来说应该是「child」(子类)、「parent」(父类)、「instance variable」(实例变量)、「class variable」(类变量)还是「method」(方法)。
(例如,如果问题是「cat?」而你认为 cat 应该是 dog 的父类,就输入「parent」。)
对于 Dog 类来说,「name」是:
对于 Dog 类来说,「age」是:
对于 Dog 类来说,「wag-tail」是:
对于 Dog 类来说,「Animal」是:
要点回顾
局部状态变量有三种:实例化变量、实例变量和类变量。
- 实例化变量(instantiation variable)在用
instantiate创建对象时指定。 - 实例变量(instance variable)是每个对象各自拥有、彼此独立的变量;改变其中一个的值不会影响其他变量。
- 类变量(class variable)是被该类所有对象共享的变量;改变一个类变量的值,该类的每个对象都会察觉到这一变化。