局部状态
通过银行账户的例子讲解 OOP 中如何用 ask 进行消息传递,以及对象状态与局部状态变量的概念。
消息传递
在 OOP 中,让事情发生的方式是「请求」某个对象替你做某事。我们这样做的方式与第 6 课中的「消息传递」类似。用 OOP 的词汇该怎么做呢?
假设我们有两个对象:Matt-Account 和 Brian-Account,它们是 bank-account 类的实例,分别保存 Matt 和 Brian 拥有的金额。(你现在还无法在 Scheme 中输入这些代码!我们假设之前已经创建好了这些对象。)
> (ask Matt-Account 'balance)
1000
> (ask Brian-Account 'balance)
10000
> (ask Matt-Account 'deposit 100)
1100
> (ask Brian-Account 'withdraw 200)
9800
> (ask Matt-Account 'balance)
1100
> (ask Brian-Account 'withdraw 200)
9600
ask
我们用 ask 过程来告诉对象执行某个动作。在上面的例子中,银行账户对象接受 3 种消息:
balancedepositwithdraw
对每一种消息,银行账户对象都知道需要执行什么操作。注意,有些消息需要附带额外信息:
-
对于 balance,不需要任何额外参数,它返回该账户中的金额。
> (ask Matt-Account 'balance) 1000 -
对于 deposit 和 withdraw,我们还需要一个参数来指定存入或取出的金额。
> (ask Matt-Account 'deposit 50000) 51000
这里的隐喻是:对象「知道如何」完成某些任务。这些任务被称为方法(method)。
笔记
测试你的理解
假设我们为 Max 开了一个银行账户,然后我们输入以下表达式:
(ask max-account 'balance)
1000
(define withdraw 'deposit)
下面的表达式会返回什么?
(ask max-account 'withdraw 100)
如果不是上一个表达式,而是调用下面这个表达式,结果又会如何?
(ask max-account withdraw 100)
状态
看看下面这些调用:
> (ask matt-account 'balance)
500
> (ask brian-account 'balance)
9999
> (ask matt-account 'deposit 500)
1000
> (ask matt-account 'balance)
1000
> (ask matt-account 'withdraw 200)
800
> (ask matt-account 'balance)
800
> (ask brian-account 'balance)
9999
笔记
测试你的理解
我们多次调用 (ask matt-account 'balance),每次得到的值都不一样。这告诉了我们 OOP 的什么特性?
matt-account 和 brian-account 都返回各自账户里有多少钱。Matt 对自己账户的操作(对 matt-account 的方法调用)会影响 Brian 的账户吗?
OOP 范式 vs. 函数式编程范式
在第一个问题中,我们看到 Matt 的余额随着每次存取而变化。这感觉很自然,因为银行账户就是这样运作的。但是,在我们一直使用的函数式编程范式中,我们会预期同一个调用返回相同的值。
在 OOP 范式中,对象拥有状态(state)。也就是说,它们对「过去发生在自己身上的事情」有所记忆。在这个例子中,银行账户有一个余额,当你存入或取出一些钱时,余额就会变化。
局部状态变量
在第二个问题中,我们看到虽然 Matt 有自己的 'balance、Brian 也有自己的 'balance,但它们互不干扰。
用 OOP 的行话说,'balance 是一个局部状态变量(local state variable),也叫实例变量(instance variable)。同一个实例变量在不同实例上会有不同的值。
这里我们可以和第 1 单元中见过的 square 与 cube 的定义做一个类比:
(define (square x)
(* x x))
和
(define (cube x)
(* x x x))
两个定义都使用了 x,但它们是相互独立的。