咬文嚼字
meta-programming(a.k.a.元编程) 中的这个 meta-词前缀源自希腊词,本意为“在…后,变换,以上”。经过几千年的演进,这个词前缀有了一种全新的意义:关于事物自身的事物。比如 meta-knowledge 就是“关于知识本身的知识”,meta-data 就是“关于数据的数据”,meta-language 就是“关于语言的语言”,而 meta-programming 也是由此而来,是“关于编程的编程”。
元编程
元编程算不上什么新鲜的概念,各种语言里早已有之:
- C和Lisp系语言里的宏
- C++里的模板
- Java的注解
- C#的属性和反射
- 各种脚本语言(如javascript、python)的eval
- ……
Matz 给元编程的定义是:
编写能在运行时操作语言构件的代码
So,元编程是“让代码写代码”,是“运行时操作语言”,知道这些,怎么利用?来看两个场景消化一下:
场景
考虑如下场景(以Ruby为例:
1) ORM
你设计了一个ORM框架,并将表实体(Table)的定义权交给了框架的使用者,并希望使用者定义表时能获得如下体验:
1 2 3 4 5 6 7 8 |
class User < Entity def name #TODO end def sex #TODO end end |
然后框架会根据表定义,来生成数据库的列以及相关实体操作功能……那么,这个Entity
类该如何设计?
2) API Proxy
某著名SNS网站提供了一套 restful API,目前只提供了如下两组API:
GET /api/friend
POST /api/friend
POST /api/search
但是你知道,API将来一定会扩张的,而你现在却需要为它编写一个代理的库,那该如何最大程度的保障库的灵活性和兼容性呢?
解决
1) ORM
显然,表实体唯一的变数就是使用者定义的字段(方法),在Ruby中,我们可以通过给类安装钩子方法,在用户定制字段时得到回调:
1 2 3 4 5 6 7 8 |
class Entity COLUMNS = [] # 钩子方法,在类添加新的方法时回调 def self.method_added(method_name) COLUMNS << method_name end end |
确定了字段范围,剩下的事情也无非就是些遍历COLUMNS
的 routines(操作DB等)
2) API Proxy
代理并不需要实现任何API,本质上它只是一个转发器,左手拿着调用者请求的API方法,右手去请求上游(拿到结果后再反方向传递),这个代理库的骨架看起来是这样的:
1 2 3 4 5 6 7 8 9 |
class Proxy def initialize @http = HttpClient.new end def method_missing(action, *args, &block) @http.send(action, *args, &block) end end |
- 会开一个http客户端,代为请求调用方的API调用
- method_missing 在类实例中出现未定义的方法时会被回调;action就是被请求的api名字,args中包含http method
- method_missing充当转发器,将请求以消息的形式发送给了http客户端,请求并返回结果
调用体验:
1 2 3 4 5 6 |
proxy = Proxy.new proxy.search('GET', 'name=tom') do |people| people.each do |person| puts person end end |
除了第一个GET
参数的传递方式不够优雅,each和块的使用都很原味 🙂
小结
看了如上片断,是不是一头雾水:什么是元编程,怎么用?
其实回到它的定义就好,利用 “运行时操作语言构件” 这个特性,来
- 简化模板代码
- 将想象力扩展到运行时,让代码动起来
最后,借用《Ruby元编程》里禅师的话来做个告别:
根本没有什么元编程,从来只有编程而已。走吧,别再打扰我了
参考
- 怎么理解元编程?https://www.zhihu.com/question/23856985
- 元编程艺术,第 1 部分: 元编程简介: http://www.ibm.com/developerworks/cn/linux/l-metaprog1.html
- 松本行弘谈Lisp元编程:http://www.ituring.com.cn/article/117593
实在是看不懂。