返回介绍

4.2 委托

发布于 2025-02-20 00:17:07 字数 4196 浏览 0 评论 0 收藏 0

假设我们想用 broker改善 seller 的行为;比方说,我们希望通过改变价格计算中使用的单位,来使每个产品的价格加倍。这很简单:我们只需要在 broker 中定义方法 unit (单位):

(define broker
  (OBJECT-FWD seller ()
   ([method unit () 2])))

有了这个定义,我们应该确保向 broker 询问某个产品的价格是向 seller 询问同样产品价格的两倍:

> (-> broker price 1)
100

嗯……这样不行!看来,一旦我们把 price 消息转发给 seller ,控制权将不再能流回 broker ;这里也即, seller 发给 selfunit 消息 不会broker 收到。

让我们考虑一下这是为什么。在 sellerself 绑定到哪个对象? seller !请记住,我们之前说过(参见 寻找 Self ),在我们的方法中, self静态绑定 的:当对象被创建时, self 指向正被定义的对象/闭包,并且将始终绑定该值。这是因为 letreclet 一样,遵从词法作用域。

我们正在寻找的则是另一种语义,称为 委托 (delegation)。委托要求对象中的 self 动态绑定 :它应该始终指向最初接收消息的对象。在我们的例子中,这将确保当 sellerself 发送 unit 消息时, self 指向 broker ,这样 broker 中新定义的 unit 将会生效。在这种情况下,我们说 sellerbroker父对象 (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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文