消息传递

介绍消息传递:把类型表示为函数、数据表示为对象,奠定面向对象编程的基础。

什么是消息传递?

在传统风格中,运算符被表示为了解不同类型的函数,而类型本身只是数据。在数据导向编程中,运算符和类型都是数据,有一个通用的 operate 函数包揽所有工作。我们也可以把传统风格整个颠倒过来:把类型表示为函数,把操作仅仅当作数据。

事实上,不仅类型是函数,每一条数据本身也是函数。也就是说,有一个函数(下面的 make-circle)代表圆形类型;当你调用这个函数时,它返回另一个函数,代表你作为实参传给它的那个特定的圆。每个圆都是一个对象(object),而代表它的那个函数是一个分派过程(dispatch procedure),它接受一个消息作为实参,用来说明要执行哪个操作。

make-square 和 make-circle 的新定义如下:

(define (make-square side)
    (lambda (message)
        (cond ((eq? message 'area)
               (* side side))
              ((eq? message 'perimeter)
               (* 4 side))
              (else (error "Unknown message")))))

(define (make-circle radius)
    (lambda (message)
        (cond ((eq? message 'area)
               (* pi radius radius))
              ((eq? message 'perimeter)
               (* 2 pi radius))
              (else (error "Unknown message")))))

(define square5 (make-square 5))

(define circle3 (make-circle 3))

消息传递为什么重要?

消息传递看起来可能是处理形状问题的一种过于复杂的方式,但我们将在下一课看到,它是构建面向对象编程的关键思想之一。消息传递与局部状态(local state)的思想结合起来(我们下周会学到)之后,会变得强大得多。

我们似乎已经放弃了带标签数据:每种形状类型都只是某个函数,很难判断一个给定的函数代表哪种形状。如果愿意的话,我们可以给每个对象加一个 type 消息,把消息传递和带标签数据结合起来:

(define (make-square side)
    (lambda (message)
        (cond ((eq? message 'area)
               (* side side))
              ((eq? message 'perimeter)
               (* 4 side))
              ((eq? message 'type) 'square)
              (else (error "Unknown message")))))