4.2 委托
假设我们想用 broker
来 改善 seller
的行为;比方说,我们希望通过改变价格计算中使用的单位,来使每个产品的价格加倍。这很简单:我们只需要在 broker
中定义方法 unit
(单位):
(define broker
(OBJECT-FWD seller ()
([method unit () 2])))
有了这个定义,我们应该确保向 broker
询问某个产品的价格是向 seller
询问同样产品价格的两倍:
> (-> broker price 1)
100
嗯……这样不行!看来,一旦我们把 price
消息转发给 seller
,控制权将不再能流回 broker
;这里也即, seller
发给 self
的 unit
消息 不会 被 broker
收到。
让我们考虑一下这是为什么。在 seller
中 self
绑定到哪个对象? seller
!请记住,我们之前说过(参见 寻找 Self ),在我们的方法中, self
是 静态绑定 的:当对象被创建时, self
指向正被定义的对象/闭包,并且将始终绑定该值。这是因为 letrec
和 let
一样,遵从词法作用域。
我们正在寻找的则是另一种语义,称为 委托 (delegation)。委托要求对象中的 self
动态绑定 :它应该始终指向最初接收消息的对象。在我们的例子中,这将确保当 seller
向 self
发送 unit
消息时, self
指向 broker
,这样 broker
中新定义的 unit
将会生效。在这种情况下,我们说 seller
是 broker
的 父对象 (parent), broker
委托父对象处理消息。
怎样绑定标识符,能使其指向使用位置的值,而不是定义位置?在语言不提供动态作用域绑定指令的情况下,唯一可以实现这一点的方法是将该值作为参数传递。所以,必须给方法增加参数,新参数指向实际的接收方(receiver)。因此,不再从静态作用域中捕获 self
标识符,我们添加 self
参数。
具体说来,这意味着 seller
中这个方法:
(λ (prod) .... (-> self unit) ....)
必须改为:
有没有想过为什么 Python 中的方法必须显式地接受 self 作为第一个参数?
(λ (self)
(λ (prod)....(-> self unit)....))
这个新参数有效地允许我们在查找得到方法后传递当前的接收方。
现在让我们定义新的语法形式 OBJECT-DEL
,来支持对象之间的委托(delegation)语义:
(defmac (OBJECT-DEL parent
([field fname init] ...)
([method mname args body] ...))
#:keywords field method
#:captures self
(let ([fname init] ...)
(let ([methods
(list (cons 'mname
(λ (self) (λ args body))) ...)])
(λ (current)
(λ (msg . vals)
(let ([found (assoc msg methods)])
(if found
(apply ((cdr found) current) vals)
(apply (parent current) msg vals))))))))
有几地方改动了:首先, target
更名为 parent
,以明确我们定义的是委托语义。其次,如上所述,所有的方法现在都是带上了 self
参数。请注意,我们完全摆脱了 letrec
!这是因为 letrec
本来的用途就是允许对象引用 self
,同时遵循词法作用域。我们已经看到,对于委托来说,我们并不想要词法作用域。
这意味着,当我们在方法字典中找到某个方法时,必须首先将实际的接收方作为参数传给它。我们如何获得接收方?唯一的可能就是,给对象也加上参数,新参数是调用其方法时必须使用的当前接收方。也就是说,对象构造器返回的值不再是“ λ (msg . vals) ....
”,而是“ λ (rcvr) ....
”。“当前接收方”是我们的对象的参数。同样,如果某个消息不能被给定的对象所理解,那么它必须把当前接收者一起发送给它的父对象。
这样我们还有最后一个问题要解决:如何向对象发送消息?回忆一下, ->
的定义是:
(defmac (-> o m arg ...)
(o 'm arg ...))
但是现在我们不能简单地把 o
当做函数来调用,传给它一个符号(消息)和可变数量的参数。现在,对象是形式为 (λ (rcvr) (λ (msg . args) ....))
的函数。所以在传递消息和参数之前,我们必须指定哪个对象是当前的接收方。好吧,这很容易,因为在我们发送消息的时候,当前的接收方应该是……接受消息的对象!
为什么这里需要 let 绑定?
(defmac (-> o m arg ...)
(let ([obj o])
((obj obj) 'm arg ...)))
来看委托——也就是 self
的延迟绑定——的效果:
(define seller
(OBJECT-DEL root ()
([method price (prod)
(* (case prod
[(1) (-> self price1)]
[(2) (-> self price2)])
(-> self unit))]
[method price1 () 100]
[method price2 () 200]
[method unit () 1])))
(define broker
(OBJECT-DEL seller ()
([method unit () 2])))
> (-> seller price 1)
100
> (-> broker price 1)
200
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论