消息传递:从风格到机制

[quote]这是最终确定的 JavaScript 基于消息传递编程风格的文章“OOP 诡异教程(上)”的下篇,它的 Python 改写版本就是 [url=http://lichray.iteye.com/topic/88435]尝试用Python实现消息传递编程风格[/url]。原文地址:(豆瓣:[url]http://www.douban.com/group/topic/1669427/[/url] 博客:[url]http://let-in.blogspot.com/2007/06/oop.html[/url])。原来的想法是以风格开头,谈到 JavaScript 的内部机制,但作者 lichray 迟迟没有动键盘,认为不如利用已有的风格做一套机制出来,这样可能更有意义。于是,就有了这个更加“诡异”的下篇,展示了一个更加“诡异”的招数。
[/quote]

[quote]这篇文章的宗旨是利用我们仅有的“宾谓”语法构造出完整的一套面向对象机制,所以更多代码在更多的时候是不应在实际工作中使用的(也算一种元语言抽象),所以类似效率、代码风格之类的问题反对回帖质疑。[/quote]

[b][size=12]四. 扩展的实现[/size][/b]
[url=http://www.iteye.com/topic/88435]上篇[/url]最后给出了一个“看上去很美”的基于消息传递的编程风格,比如构造一个 People 类的代码类似:

function People () {
var money = 0
function setMoney (dollars) {
money = dollars
}
function pay (dollars) {
money -= dollars
}
return (function (verb) {
return eval(verb)
})
}

有了这样的语法我们就可以描述不少句子了。但是存在一个问题:现实中的 Objects 之间是存在关系的——比如,forrest 是个 IQ 为 75 的傻子,傻子是 People 的一种。而我们仅仅是生搬硬套了一种语法而割裂了这种 "is-a" 关系。现在我们的工作,目的之一就是让这样一个“真切”的世界从我们已有的编程风格的地基上拔地而起。
到底应该怎样做才能使 Fool 产生的对象都能响应 People 的消息呢?我们要给 Fool 产生的对象(也就是返回的那个匿名函数啦)都添加这样一种能力:如果在 Fool 中响应不了消息,那就反馈给 People 响应。

function Fool (iq) {
var IQ = iq || 0
function init (iq) {
IQ = iq
}
return (function (verb) {
try {
return eval(verb)
} catch (e) {
return People()(verb)
}
})
}

js> forrest = Fool()
js> forrest('init')(75)
js> forrest('IQ')
75
js> forrest('money')
0

[b][size=12]五. 语法扩展和代码生成[/size][/b]
这下代码量增加了很多,强迫潜在的使用者们在创建每个类时都这样写那实在是令人抓狂。本来这篇文章应该不提此类问题的解决,但考虑到有益于读者理解“机制”这个抽象概念,这里给出一个可行的方案——把普通的类代码用 Function() 函数重编译为可用的 JavaScript 函数。也就是说,我们能给出类扩展的代码并指定被扩展的类来获取类似上文的代码:

Fool = extend('People()', function (iq){
var IQ = iq || 0
function init (iq) {
IQ = iq
}
})

为了方便字符串操作,我们希望编译后的代码的参数部分(如 People())都集中出现在一个位置且尽可能便于定位。在函数头添加一句

var origin = People()

当然是可行的,这样还能使 Fool 内部显式引用到其超类。但这样还不够漂亮。我们修改编译后的样例代码为:

function () {
return (function (origin) {
var IQ = 0
function init (iq) {
IQ = iq
}
return (function (verb) {
try {
return eval(verb)
} catch (e) {
return origin(verb)
}
})
})(People())
}

这个利用参数传递变量的小技巧不值得学习,实际效率不高。但在这篇文章中,这样绑定特殊变量的技术是标准方案。
那么,我们的语法扩展兼代码生成函数 extend() 的实现为:

function extend (originc, code) {
function argsArea (code) {
// 题外话,正则表达式也有不值得一用的时候
return code.slice(code.indexOf('(')+1, code.indexOf(')'))
}
function bodyCode (code) {
// 不用 trim() 了,别没事儿找事儿
return code.slice(code.indexOf('{')+1, code.lastIndexOf('}'))
}
function format (body) {
var objc = bodyCode(function () {
return (function (verb) {
try {
return eval(verb)
} catch (e) {
return origin(verb)
}
})
}.toString())
return 'return (function (origin) {'+body+objc+'})('+originc+')'
}
var $ = code.toString()
return Function(argsArea($), format(bodyCode($)))
}

这样前文提到过的 extend 的实例代码就可以正常运行了,测试代码不再重复。

[b][size=12]六. 机制完备化[/size][/b]
这样,我们的基于消息传递编程风格的一套面向对象机制就确定下来了。机制是宪法,是语言的根本大法,有了它,我们就可以通过修改代码生成器,很快地给这套机制进行完备化。
想法有很多,例子只举两个。
第一个例子:类的定义中应该能直接引用到将产生的对象 self。答案只有一句话:把返回的那个作为对象的匿名函数命名为 self。
第二个例子:既然是单继承模式,应当存在一个顶层类 AbsObj,使没有指定继承的类自动继承它。答案是:在 extend 函数体第一行添加代码:

if (arguments.length == 1) {
code = originc
originc = 'AbsObj()'
}

然后手工构造设计 AbsObj 类,为空也无所谓。不过当然了,一般都会给顶层类添加一些全局性质的消息绑定。由于是“底层操作”,基本上都需要修改 extend 函数。做了一个简单的:

function AbsObj () {
//检测是否能响应此 verb,要再用一次异常处理
function canHandle(verb){
try {
// 别担心这里的 self 会传递不过去
self(verb)
} catch (e) {
return false
}
return true
}
function toString() {} // 这个搞起来其实很麻烦~`
var self = function (verb) {
return eval(verb)
}
return self
}


js> Obj=extend(function(){x=5})
js> o=Obj()
js> o('canHandle')('x')
true
js> o('canHandle')('y')
false

文章写完了,小结一下。消息传递的编程不仅仅是一种代码风格,还可以成长为一种完备的机制。这种完备性远不只是这两篇加起来不到300行的文章所能覆盖的(例如非常彻底的“万物皆对象”,因为只要是能响应消息的函数,连接一下 AbsObj 就是合法对象了;类,函数都可以),大家可以试着玩一玩,顺便体会一下这个计算模型的透明和强大。
另外,熟悉函数式编程的朋友可以帮忙思考一下:这样一个基于闭包变换的计算模型实质上是函数式的,再配合动态的函数式的对象级继承(用一个匿名类代换一下)就能在纯 FP 真正下实现 OOP 了。可惜的是每一次更新操作都要重新生成对象,性能代价大了点,yet another question.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值