如何运行一个ruby类 中的方法

Python016

如何运行一个ruby类 中的方法,第1张

在Ruby中,有多种方法可以实现方法的动态调用。

1. 使用send方法

第一种实现动态方法调用是使用send方法,send方法在Object类中定义,方法的第一个参数是一个符号用来表示所要调用的方法,后面则是所调用方法需要的参数。

“This is a dog1″.send(:length) =>14

上面的代码中通过send方法去对一个字符串执行length操作,返回字符串的长度。

class TestClass

def hello(*args)

”Hello ” + args.join(‘ ‘)

end

end

a = TestClass.new

puts a.send :hello, “This”, “is”, “a”, “dog!”

执行结果为:

Hello This is a dog!

2. 使用Method类和UnboundMethod类

另一种实现动态方法调用是使用Object类的method方法,这个方法返回一个Method类的对象。我们可以使用call方法来执行方法调用。

test1 = “This is a dog1″.method(:length)

test1.call =>14

class Test

def initialize(var)

@var = var

end

def hello()

”Hello, @var = #{@var}”

end

end

k = Test.new(10)

m = k.method(:hello)

m.call #=>“Hello, @iv = 99″

l = Test.new(‘Grant’)

m = l.method(“hello”)

m.call #=>“Hello, @iv = Fred”

可以在使用对象的任何地方使用method对象,当调用call方法时,参数所指明的方法会被执行,这种行为有些像C语言中的函数指针。你也可以把method对象作为一个迭代器使用。

def square(a)

a*a

end

mObj = method(:square)

[1, 2, 3, 4].collect(&mObj) =>[1 4 9 16]

Method对象都是和某一特定对象绑定的,也就是说你需要通过某一对象使用Method对象。你也可以通过UnboundMethod类创建对象,然后再把它绑定到某个具体的对象中。如果UnboundMethod对象调用时尚未绑定,则会引发异常。

class Double

def get_value

2 * @side

end

def initialize(side)

@side = side

end

end

a = Double.instance_method(:get_value) #返回一个UnboundMethod对象

s = Double.new(50)

b = a.bind(s)

puts b.call

执行结果为:

100

看下面一个更具体的例子:

class CommandInterpreter

def do_2() print “This is 2

”end

def do_1() print “This is 1

”end

def do_4() print “This is 4

”end

def do_3() print “This is 3

”end

Dispatcher = {

?2 =>instance_method(:do_2),

?1 =>instance_method(:do_1),

?4 =>instance_method(:do_4),

?3 =>instance_method(:do_3)

}

def interpret(string)

string.each_byte {|i| Dispatcher[i].bind(self).call }

end

end

interpreter = CommandInterpreter.new

interpreter.interpret(’1234′)

执行结果为:

This is 1

This is 2

This is 3

This is 4

3. 使用eval方法

我们还可以使用eval方法实现方法动态调用。eval方法在Kernel模块中定义,有多种变体如class_eval,module_eval,instance_eval等。Eval方法将分析其后的字符串参数并把这个字符串参数作为Ruby代码执行。

str = “Hello”

eval “str + ‘ World!’” =>Hello World!

sentence = %q{“This is a test!”.length}

eval sentence =>15

当我们在使用eval方法时,我们可以通过eval方法的第二个参数指明eval所运行代码的上下文环境,这个参数可以是Binding类对象或Proc类对象。Binding类封装了代码在某一环境运行的上下文,可以供以后使用。

class BindingTest

def initialize(n)

@value = n

end

def getBinding

return binding() #使用Kernel#binding方法返回一个Binding对象

end

end

obj1 = BindingTest.new(10)

binding1 = obj1.getBinding

obj2 = BindingTest.new(“Binding Test”)

binding2 = obj2.getBinding

puts eval(“@value”, binding1) #=>10

puts eval(“@value”, binding2) #=>Binding Test

puts eval(“@value”) #=>nil

可以看到上述代码中,@value在binding1所指明的上下文环境中值为10,在binding2所指明的上下文环境中值为Binding Test。当eval方法不提供binding参数时,在当前上下文环境中@value并未定义,值为nil。

Ruby是一个完美的面向对象编程语言。拥有面向对象的编程语言的功能,包括:

1、数据封装

2、数据抽象

3、多态性

4、继承

这些功能已在讨论面向对象Ruby

面向对象的程序涉及类和对象。 一个类是蓝本,从个别对象被创建。在面向对象的术语,我们说小明的自行车是被称为自行车类的对象实例。

任何车辆的例子。它包括轮子,马力,燃油或燃气罐容量。这些特点形成的类车辆的数据成员。可以从其他车辆区分这些特征。

车辆也有一定的功能,如停止,驾驶,超速驾驶。即使这些功能形成的类车辆的数据成员。因此,可以定义一个类作为一个组合的特点和功能。

车辆类可以被定义为:

Class Vehicle

{

Number no_of_wheels

Number horsepower

Characters type_of_tank

Number Capacity

Function speeding

{

}

Function driving

{

}

Function halting

{

}

}

通过这些数据成员分配不同的值,可以形成类车辆的几个实例。例如,飞机的有三个轮子,1,000马力,不同的燃料罐及容量为100升。同样的方式,一辆汽车有四个轮子,200马力,气体作为不同的罐及容量25升。

Ruby中定义一个类:

要通过使用Ruby实现面向对象编程,需要先学习如何创建对象和Ruby中的类。

Ruby中一个类总是以关键字class类的名称开头。名称应始终以首字母大写。如以是Customer类可以显示为:

class Customer

end

类定义结束通过使用关键字end结束。在类的所有数据成员是类之间的定义,并以end关键字作为结束符。

Ruby类中的变量:

Ruby提供了四种类型的变量:

局部变量: 局部变量是在一个方法中定义的变量。局部变量是不可用的方法外。更多细节在随后的章节中的方法中会介绍。局部变量一般以小写字母或_开头。

实例变量: 实例变量是可跨越任何特定实例或对象的方法。这意味着,从对象到对象的实例变量改变。实例变量前面加上at符号(@),跟着变量名。

类变量:类变量是可在各种不同的对象。 一个类变量属于类,是类的一个特点。他们前面的符号@@跟着的变量名。

全局变量: 类变量是不能跨类。如果想要一个单一的变量可以跨类,需要定义一个全局变量。全局变量的前面总是用美元符号($)。

例子:

使用类变量@@no_of_customers,能确定创建的对象的数量。这使得导出的客户数量。

class Customer

@@no_of_customers=0

end

Ruby中使用new方法创建对象:

对象是类的实例。现在,将学习如何在Ruby中创建对象一个类对象。Ruby中通过使用new方法创建对象。

new方法是一种独特的方法,这是预定义在Ruby库。new方法属于类的方法。

下面的例子是创建两个对象类客户cust1 和 cust2:

cust1 = Customer. new

cust2 = Customer. new

在这里,cust1和cust2是两个对象的名字。在等于号(=)之后,类名称将按照对象名称。然后,点运算符和关键字new在后面。

自定义方法来创建Ruby对象 :

可以通过new方法的参数,这些参数可以用来初始化类变量。

当打算声明的new方法具有参数,需要声明的方法在创建类的时候初始化。

initialize方法是一种特殊类型的方法,该方法时将执行new方法的类被称为参数。

下面的例子是创建initialize方法:

class Customer

@@no_of_customers=0

def initialize(id, name, addr)

@cust_id=id

@cust_name=name

@cust_addr=addr

end

end

在这个例子中,可以声明局部变量的初始化方法id, name和addr。这里def 结束被用来定义一个Ruby的方法初始化。这些将在有关后续章节中了解更多。

在initialize方法中,对这些局部变量的值传递到实例变量@cust_id,@cust_name和@cust_addr。这里的局部变量持有的值由new方法一同传递。

现在可以创建对象,如下所示:

cust1=Customer.new("1", "John", "Wisdom Apartments, Ludhiya")

cust2=Customer.new("2", "Poul", "New Empire road, Khandala")

Ruby中类的成员函数:

在Ruby中,函数被调用的方法。在一个类中的每个方法的方法名用关键字def开始。

方法名总是以小写字母最好。你最终的方法Ruby中通过使用关键字end表示结束。

下面的例子是定义一个Ruby的方法:

class Sample

def function

statement 1

statement 2

end

end

这里statement1和statement2为函数体的一部分。这些statments可以是任何有效的Ruby语句。例如,我们可以在方法中打印Hello Ruby如下:

class Sample

def hello

puts "Hello Ruby!"

end

end

现在,在下面的例子Sample类创建一个对象,并调用hello方法,看到的结果:

#!/usr/bin/ruby

class Sample

def hello

puts "Hello Ruby!"

end

end

# Now using above class to create objects

object = Sample. new

object.hello

这将产生以下结果:

Hello Ruby!

1.Argument Processing

Ruby 使用了 Symbols 和 Hash 来达到虚拟关键字参数(Pseudo-Keyword Arguments)。这种技巧被广泛应用在 Ruby 的函式库和 Rails 中,增加了阅读性,也很容易使用。

def blah(options)

puts options[:foo]

puts options[:bar]

end

blah(:foo =>"test", :bar =>"test")

Ruby 也可以将参数列当成阵列使用:

def sum(*args)

puts args[0]

puts args[1]

puts args[2]

puts args[3]

end

sum(1,2,3)

如此就可以设计出不固定参数列、十分弹性的 API。类似於 C++ 的 function overloading。在 Rails 中也十分常见这样的 API 设计,例如 link_to 就支援了两种用法:

# USAGE-1 without block

<% link_to 'Posts list', posts_path, :class =>'posts' %>

# USAGE-2 with block

<% link_to posts_path, :class =>'posts' do %>

Posts list

<% end %>

搭配虚拟关键字参数使用的话,可以参考 ActiveSupport#extract_options! 这个小技巧取出 Hash 值。

2. Code Blocks

程式区块(Block)是 Ruby 最重要的特色,除了拿来做迭代(Iteration)之外,也可以包装前後置处理(pre- and Post-processing),一个最基本的例子就是开档了,一般程序式的写法如下:

f = File.open("myfile.txt", 'w')

f.write("Lorem ipsum dolor sit amet")

f.write("Lorem ipsum dolor sit amet")

f.close

使用 Block 之後,我们可以将 f.close 包装起来,不需要明确呼叫。只要程式区块结束,Ruby 就会自动关档。程式一来因为缩排变得有结构,二来也确定档案一定会关闭(不然就语法错误了)

# using block

File.open("myfile.txt", 'w') do |f|

f.write("Lorem ipsum dolor sit amet")

f.write("Lorem ipsum dolor sit amet")

end

另一个程式区块的技法,是用来当做回呼(Dynamic Callbacks)。在 Ruby 中,程式区块也是物件,於是我们可以将程式区块如透过”注册”的方式先储存下来,之後再依照需求找出来执行。例如在 Sinatra 程式中:

get '/posts' do

#.. show something ..

end

post '/posts' do

#.. create something ..

end

我们”注册”了两个回呼:一是当浏览器送出 GET ‘/posts’ 时,会执行 show something 的程式区块,二是 POST ‘/posts’ 时。

3. Module

模组(Module)是 Ruby 用来解决多重继承问题的设计。其中有一招 Dual interface 值得一提:

module Logger

extend self

def log(message)

$stdout.puts "#{message} at #{Time.now}"

end

end

Logger.log("test") # as Logger’s class method

class MyClass

include Logger

end

MyClass.new.log("test") # as MyClass’s instance method

Ruby 的 extend 作用是将模组混入(mix-in)进单件类别(singleton class),於是 log 这个方法除了可以像一般的模组被混入 MyClass 中使用,也可以直接用 Logger.log 呼叫。

要将 Ruby 模组的混入成类别方法(class method),也有一些常见的 pattern 模式,可以将模组设计可以同时混入实例方法(instance method)和类别方法,请参阅投影片范例。这在撰写 Rails plugin 时非常常用。

4. method_missing?

Ruby 的 Missing 方法是当你呼叫一个不存在的方法时,Ruby 仍然有办法处理。它会改呼叫 method_missing 这个方法,并把这个不存在的方法名称传进去当做参数。这个技巧在 Rails 的 ActiveRecord 中拿来使用:

class Person <ActiveRecord::Base

end

p1 = Person.find_by_name("ihower")

p2 = Person.find_by_name_and_email("ihower", "[email protected]")

其中 find_by_name 和 find_by_email 就是这样的方法。不过这个技巧不是万能丹,它的执行效率并不好,所以只适合用在你没办法预先知道方法名称的情况下。不过也不是没有补救之道,如果同样的方法还会继续呼叫到,你可以在 method_missing 之中用 define_method 或 class_eval 动态定义此方法,那麼下次呼叫就不会进来 method_missing,进而获得效能的改善。事实上,ActiveRecord::Base 的 method_missing 就是这麼做的。(感谢 BigCat 留言提醒我有此补救之道)

另一个 Missing 方法的绝妙 API 设计,是拿来构建 XML 文件:

builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)

builder.person do |b|

b.name("Jim")

b.phone("555-1234")

b.address("Taipei, Taiwan")

end

# <person>

# <name>Jim</name>

# <phone>555-1234</phone>

# <address>Taipei, Taiwan</address>

# </person>

搭配了区块功能,就能用 Ruby 语法来写 XML,非常厉害。

5. const_missing

除了 method_missing,Ruby 也有 const_missing。顾名思义就是找不到此常数时,会呼叫一个叫做 const_missing 的方法。现实中的例子有 Rails 的 ActiveSupport::Dependencies,它帮助我们不需要先载入所有类别档案,而是当 Rails 碰到一个还不认识的常数时,它会自动根据惯例,找到该档案载入。

我们也可以利用这个技巧,针对特定的常数规则来处理。例如以下的程式会自动将 U 开头的常数,自动转译成 Unicode 码:

class Module

original_c_m = instance_method(:const_missing)

define_method(:const_missing) do |name|

if name.to_s =~ /^U([0-9a-fA-F]{4})$/

[$1.to_i(16)].pack("U*")

else

original_c_m.bind(self).call(name)

end

end

end

puts U0123 # ģ

puts U9999 # 香

6. Methods chaining

方法串接是一个很常见的 API 设计,透过将方法的回传值设成 self,我们就可以串接起来。例如:

[1,1,2,3,3,4,5].uniq!.reject!{ |i| i%2 == 0 }.reverse

# 5,3,1

7. Core extension

Ruby 的类别是开放的,可以随时打开它新增一点程式或是修改。即使是核心类别如 Fixnum 或是 Object(这是所有类别的父类别) 都一样。例如 Rails 就定义了一些时间方法在 Fixnum 里:

class Fixnum

def hours

self * 3600 # 一小时有多少秒

end

alias hour hours

end

Time.now + 14.hours

Ruby 的物件模型与元编程(Meta-programming)

在 Ruby 中,所有东西都是物件。甚至包括类别(class)本身也是物件。这个类别物件(class object)是一个叫做 Class 的类别所实例出来的物件。而所有的物件(当然也包括类别物件),都有一个 metaclass (又叫做 singleton, eigenclass, ghost class, virtual class 等名字)。定义在 metaclass 里的方法,只有该物件能够使用,也就是 singleton method (单件方法),只有该物件才有的方法。

了解什麼是 metaclass 是 Ruby 元编程的一个重要前提知识。Ruby 元编程最常用的用途,就是因应需求可以动态地定义方法,例如在 Rails ActiveRecord 中常见的 Class Macro 应用。

要能随心所欲动态定义方法的关键重点,就是 variable scope (变数的作用域) 了。例如以下我们透过 class_eval 和 define_method 帮 String 定义了一个 say 方法,注意到整个 variable scope 都是通透的,没有建立新的 scope:

name = "say"

var = "it’s awesome"

String.class_eval do

define_method(name) do

puts var

end

end

"ihower".say # it’s awesome

class_eval 可以让我们改变 method definition 区域(又叫做 current class)。除了本投影片,建议可以阅读 Metaprogramming in Ruby: It’s Allhe Self 和 Three implicit contexts in Ruby 这两篇文章深入了解 self 和 current class。

8. Class Macro (Ruby’s declarative style)

Class Macro 是 Ruby Meta-programming 非常重要的一个应用,例如在 Rails ActiveRecord 中:

class User <ActiveRecord::Base

validates_presence_of :login

validates_length_of :login,:within =>3..40

validates_presence_of :email

belongs_to :group

has_many :posts

end