天天看点

Ruby元编程

现在关于Ruby元编程可以说比较热门,这个隐藏在Ruby背后的特性随着大家对Ruby的了解逐渐显现出来啦。这篇文章是自己对Ruby MetPrgamming的理解。

元编程中的元是指元信息(Meta),主要是为其载体提供基本信息,如html页面中就有meta,如content type说明,SEO等。在C++,Java和Ruby语言中,也有元信息概念,如加载到内存中(运行期)的当个对象模型(Class或者Object),都会包含元信息(蓝色)和执行代码(灰色):

Ruby元编程

图中蓝色区块表示元信息,C++最少,在C++编译的过程中,编译器仅保留了很少的元信息;Java次之,包含了部分元信息;Ruby在解析过程中保留很多信息,所以元信息最多。当然这里不能说C++设计不合理,C++流行的时候,那个时候内存说不定是640K的,硬盘16M的,如果保留过多的信息,那势必导致程序无法运行,这些设计都是出于性能的考虑。当然Java在后续的版本(Java 9,Java 10?)中可能会添加更多的元信息,都不确定,毕竟语言在发展。

就一个Class来说,这些元信息是什么,如Java,元信息区块包括编译版本号、类名、属性、函数方法名、返回值、参数类型、annotation等等,这个我们可以通过查看Java Class文件得知。对于Ruby来说,元信息则更丰富,如对函数来说,函数的参数名都会保存下来。接下来还有一个关于元信息的是否可以修改的问题,C++和Java中,这些元信息都是只读的,你不能更改。一个例子,我们在做Java Debug的时候,如果我们修改函数内部的逻辑,那么hotspot机制可以重新加载该class,但是你给Java Class添加属性或函数后,hotspot机制会警告你无法进行新的class加载,这个时候我们需要重新启动应用进行debug。当然也不是绝对的,JavaRebel能够在修改Java Class结构后,仍能重新加载,其主要是其修改了Java对象模型,不在这里讨论。但是Ruby就不一样拉,元信息不是封闭的,是可以动态修改的,这样就可以在运行期调整其结构。综合上述的讨论,Ruby的元信息丰富,且可以动态修改,所以我们可以就元信息进行更多的编程,实现某些特殊的功能,而C++和Java在这方面就表现的比较弱一些。

讲述了元信息,那么什么是元编程?就是利用元信息进行编码,也就是你的代码中包含对元信息的访问和修改。网上有对元编程的定义为”Write code to write code”(利用代码去写代码),可能不便于理解,如你编写Maven Plugin或者Velocity模板来生成代码,这些都不是元编程,因为它们压根就没有接触元信息,就是代码生成工具而已。在Java中,如果你的代码访问了元信息(java.lang.reflect下的类),我们就说你使用了反射机制,反射其实就是Java中的元编程,所以这里将代码中涉及元信息的操作定义为元编程。在代码中如何访问元信息?其实很简单,有相关的API,如Java中,java.lang.reflect下的类都是围绕元信息设计的。在Ruby中,和Java一样,有相关的函数,如instance_methods、class_eval, instance_eval, define_method等等,这些函数主要负责访问和修改元信息,网上有很多文章介绍这些函数的,不会很难理解。

为何要进行元编程?我们写代码主要是为了实现业务逻辑,如数据库操作,调用搜索引擎等,而元编程只是在操作元信息,好像和业务逻辑无关,其实不然。我们访问和修改元信息主要目的有两个:简化代码和实现复杂逻辑。简化代码虽然不直接为业务逻辑服务,但是会降低维护成本,如动态函数定义等都是这样的目的;实现复杂逻辑,举一个例子,如Java中,我们利用反射机制来实现AOP,这样一些复杂的功能(事务、监控统计、Cache、权限控制等)就非常容易实现。在Ruby中,使用元编程实现的ActiveRecord让复杂的数据库操作更加简单,利用Ruby元编程可以实现各种DSL,如Sinatra就是HTTP的DSL。

我们进行元编程的主要目的是解决业务逻辑实现中的技术问题,所以元编程时有一定的适用场合,不是说利用元编程实现某种奇淫技巧,造成代码理解困难,增加维护成本,那就不必要啦。元编程也有一些局限性:

1 动态结构更改可能导致某些不可预料的错误,如程序A按照既定的逻辑编程,而B程序使用元编程,更改了相关的元信息,导致逻辑不正确,这些都是有的;