Ruby元编程构造简单优雅解决方案

开发 开发工具
虽然,元编程是从Java中得到的广泛推广,但是,Ruby元编程给我们带来了简便灵活的应用。帮助我们在实际编程应用中获得大量的方便之处。

Ruby语言虽然比较新颖,其编写方式和一些特性于其他常见语言不尽相同,但是一些编程语言特有的属性是不会改变的,比如Ruby元编程。#t#

元编程并不是一个很新的概念,通常元编程被认为是通过程序来生成程序,如果从这种意义上来考虑,那么lex和yacc以及JavaCC应该都可以算是具有了元编程的概念,在Java中,元编程得到了广泛的应用。

但在Ruby中,Ruby元编程的使用变得相当的简单和容易实现,使用Ruby语言本身来产生Ruby代码,不需要借助外部的工具,著名的RoR框架就是建立在Ruby元编程的基础上的。可能你对元编程还没什么概念,但是Ruby已经内建了元编程这种机制,所以很有可能,你在不知不觉中就已经使用了Ruby元编程技术为你带来的方便之处。如下面这段代码:

 

  1. class Person  
  2. attr_reader :name  
  3. end 

你肯定知道:name是和@name相关联的,但是你不一定清楚它到底是怎么实现的,其实attr_reader方法的实现就是采用了Ruby元编程技术,如下面的这段代码:

 

  1. class Module  
  2. def attr_reader(*syms)  
  3. syms.each do |sym|  
  4. class_eval %{def #{sym}   
  5. @#{sym}  
  6. end  
  7. end   
  8. end  
  9. end 

看了这段代码,你应该大概了解Ruby元编程的机制了吧,如果你现在还不了解,那么我建议你先认真的学习一下Ruby的反射机制,然后再接下去看这篇帖子,因为下面介绍的内容并不是一杯婴儿奶粉。

在Ruby On Rails中,有一个OR映射层,就是动态的从一张关系表映射到一个对象,这主要由ActiveRecord类来实现。在OR映射模型中,将关系数据库中的关系表映射到对象模型时,将关系表的表名映射到类名,表中的每一个元组映射到对应于这个类的一个对象,元组的一个字段对应于对象的一个属性。

假如我们有一个保存职员基本信息的文件,文件的格式是这样的:第一行是文件内容的每个字段的名称,从第二行开始,则是每个职员的基本信息。现在我们有一个文件名为“employee.txt”的文件,其内容如下所示:

 

  1. name,age,gender  
  2. "John", 23, "male"  
  3. "Linclon", 25, "male" 

 

假设我们就要从这个文本文件中读取数据,并进行一定的处理。如果是使用C++编程,你首先一定会想到应该定义一个Employee类,然后这个类中有name, age, gender这些成员变量。但是采用这种方法的话,可以发现,如果想在职员信息中加入一个字段,比如部门(department),就不得不修改Employee类的代码,在Employee类中增加一个“department”成员变量,所以我们的代码是高度依赖于文件的具体格式,这当然不是一个好的现象。

我们希望有一种更简单和优雅的方案,还有,Ruby动态性提高给我们一个解决方案,但是,我们应该从何下手呢,这就需要Ruby元编程能力。

首先,我们想应该有一个职员类,在Rails中,每个关系表的名称会成为类的名称,在这里,采用类似的方法,将文本文件的名称作为类的名称,在Ruby中,类名同时也是一个常量名,所以第一个字母必须为大写,我们使用如下的代码来生成类名。

 

  1. class_name = File.basename
    (file_name, ".txt").capitalize  
  2. # "employee.txt" => "Employee"  
  3. klass = Object.const_set
    (class_name, Class.new) 

Class.new生成一个新的类,这个类的名称是匿名的,所以采用const_set操作来绑定一个类名,变量klass是新类型的引用。

生成了这个类以后,需要想这个类添加姓名,年龄和性别这些属性,这些属性的名称是在文本文件的的第一行中给出的。

 

  1. data = File.new(file_name)  
  2. header = data.gets.chomp  
  3. data.close  
  4. names = header.split(",") 

下面的Ruby元编程代码给出了如何生成这些属性,以及初始化这些属性值。

 

  1. klass.class_eval do  
  2. attr_accessor *names  
  3. define_method(:initialize)
     do |*values|  
  4. names.each_with_index 
    do |name, i|  
  5. instance_variable_set
    ("@" + name, values)  
  6. end  
  7. end  
  8. #...  
  9. end  

现在,有了一系列的访问子(可读和可写),通过instance_variable_set方法,又给每个属性做了初始化。

变量names是在块外部定义的,由于块的闭合性,所以变量names在块中也是有效的。当然,为了Ruby元编程程序的演示,又定义的了一个to_s方法,代码如下所示:

 

  1. define_method(:to_s) do  
  2. str = "<#{self.class}: " 
  3. names.each {|name| str << 
    "#{name}=#{self.send(name)} "}  
  4. str + ">"  
  5. end  
  6. alias_method :inspect, :to_s 

完成了这些以后,对于类的构造已经基本结束了,现在就需要真正的从文本文件中读取数据了。从文本文件读数据应该是一个类方法,而不是一个实例的方法,其实现代码如下:

 

  1. def klass.read  
  2. array = []  
  3. data = File.new(self.
    to_s.downcase + ".txt")  
  4. data.gets #throw away header  
  5. data.each do |line|  
  6. line.chomp!  
  7. values = eval("[#{line}]")  
  8. array << self.new(*values)  
  9. end  
  10. data.close  
  11. return array  
  12. end 

在这个方法中,使用字的类名来匹配相关的文件,比如将Employee类映射到“employee。txt”。然后,从文件中读取职员信息,由于第一行是字段定义,所以要舍弃第一行数据。从第二行开始读取数据,每读取一行数据,则构造一个Employee实例。通过上面这个简单的例子,我们可以看出Ruby元编程的功能是相当之强大的,使用元编程技术,可以构造相当简单,优雅的解决方案。

责任编辑:曹凯 来源: ddvip.com
相关推荐

2009-09-14 10:54:07

LINQ解决方案

2011-03-29 14:35:34

2022-03-01 09:31:06

JWTSession跨域

2021-06-28 08:10:59

JavaScript异步编程

2009-12-03 13:51:51

JRubyJBossTorqueBox

2009-10-27 16:12:40

2018-12-03 12:17:27

Semptian解决方案

2012-05-27 16:21:31

IDC华为

2018-12-03 11:59:42

Inventec解决方案

2018-12-03 12:13:21

Mellanox解决方案

2018-12-03 12:26:30

YADRO解决方案

2011-01-12 11:12:38

普元平台软件

2016-03-13 17:58:57

2016-02-18 15:33:56

融合通信华为

2011-12-09 11:13:17

2009-12-23 21:06:47

统一通信多媒体联络中心平台华为

2016-03-13 17:35:18

2010-12-21 17:20:01

2017-08-02 17:23:22

AzureIoTAWS

2010-12-21 17:28:58

点赞
收藏

51CTO技术栈公众号