Ruby和许多极为流行的编程语言都是面向对象的。多数的面向对象编程语言,每个对象都是一个样例或者既定类的实例以及独立对象的行为。
创建一个通用对象
obj = Object.new
定义通用对象的行为
def obj.talk
puts "I am an object"
puts "(Do you object?)"
end
定义带参数的方法
def obj.c2f(c)
c * 9.0 / 5 + 32
end
任何方法的返回值与方法执行中最后一个表达式的计算值相等
Ruby提供了一个关键字明确地定义了返回值:return,这个关键字地使用通常是可选的,但是多数程序员都喜欢用它,因为它使隐式的返回值表达式变得显而易见。
def obj.c2f(c)
return c * 9.0 / 5 + 32
end
假如需要返回多个值则必须使用它,这些值会自动被包装为一个数组。
一张入场券是一个常见的对象,且有一组众人熟知的属性和行为。这里将从一个较高的视角来看待一个如入场券般的Ruby对象能做什么以及该对象对自身的理解。
创建一个ticket.rb
ticket = Object.new
def ticket.date
"01/02/03"
end
def ticket.venue
"Town Hall"
end
def ticket.event
"Author's reading"
end
def ticket.performer
"Mark Twain"
end
def ticket.seat
"Second Balcony, row J, seat 12"
end
def ticket.price
5.50
end
这个马克吐温的入场券是一个简单的例子,但是它包含了一些Ruby编程的重要流程和原则。这个最重要的经验就是:程序完成任何有用事情的信息都必须存储于对象之中。ticket对象拥有这些信息,通过方法的调用可以请求ticket对象返回信息
查询对象
print "This ticket is for: "
print ticket.event + ", at "
print ticket.venue + ", on "
puts ticket.date + "."
print "The performer is "
puts ticket.performer + "."
print "The seat is "
print ticket.seat + ", "
print "and it costs $"
puts "%.2f." % ticket.price
通过字符串插值缩短查询代码:
puts "This ticket is for: #{ticket.event}, at #{ticket.venue}" +
"The performer is: #{ticket.performer}." +
"The seat is: #{ticket.seat}, " +
"The it costs #{"%.2f." % ticket.price}"
无论在插值运算符中的内容是什么,他都能被分步求值,求值的结果会被插入字符串中。
用方法表达布尔状态
def ticket.avai_status
"sold"
end
def ticket.avai?
false
end
if ticket.avai?
puts "You are in luck!"
else
puts "Sorry--that seat has been sold."
end
注意方法avai?以问号结尾
就算是新创建的对象也不会是一块白板。只要对象创建并存在,它就能响应一组消息。
输入以下命令可以查看原生方法的列表:
Object.new.methods.sort
这些原生方法中的一小部分是非常普遍和非常有用的。
在Ruby中,每个对象都有一个和它唯一关联的ID编号。可以通过请求一个对象的object_id获得一个对象的ID,使用如下类似的代码。
obj = Object.new
puts "The id of obj is #{obj.object_id}."
str = "Strings are objects too, and this is a string!"
puts "The id of the string object str is #{str.object_id}."
puts "And the id of the integer 100 is #{100.object_id}"
运行这段代码可以看到
The id of obj is 60.
The id of the string object str is 80.
And the id of the integer 100 is 201
在尝试确定两个对象是否相等的时候,每个对象都拥有一个唯一的ID编号是很有用的,可以通过判断object_id是否相等而判断两个对象是否相等。
a = Object.new
b = a
puts "a's id is #{a.object_id} and b's id is #{b.object_id}"
运行之后可以看到a和b的对象id是相同的。
Ruby对象响应消息。在程序运行期间的不同事件点,依赖于对象和为对象定义的各种方法,一个对象可能会响应指定的消息,也可能不会。所以可以使用respond_to?方法判断一个对象是否有某种方法。
if obj.respond_to?("talk")
obj.talk
else
puts "Sorry, the object doesn't understand the 'talk' message"
end
该方法时自省或者反射的一个例子,这两个词指的是可以在程序运行期间进行状态检测。Ruby提供了许多用于自省机制的工具。使用methods方法测试对象,是另一种自省和反射的技术。
如果希望从键盘输入合适的查询词组(venue、performer等),就能从ticket对象中得到信息,则需要把如下代码添加到已有的程序中:
print "Information desired: "
request = gets.chomp
该行代码可以从键盘中获取一行输入。这样可以使用双等号比较符测试两个不同的输入值,它会基于对象自身的内容对字符串进行比较,然后调用与值匹配的方法。
if request == "venue"
puts ticket.venue
elsif request == "performer"
puts ticket.performer
......
尽管如此,还是不得不继续编写整个入场券的属性列表,但那变得有些冗余了。
这里还有一个可选的方案:给ticket对象直接发送对应的词语。按如下方式替换前面例子里的代码:
if ticket.respond_to?(request)
puts ticket.send(request)
else
puts "No such information available"
end
这个版本中使用了send方法作为ticket对象获得消息的通用入口。这样可以避免整个可能的请求清单,并由处理ticket对象的消息执行该消息,以取代检查ticket对象所具有的能力。
还可以使用__send__或者public_send代替send,__send__方法比普通send更安全,而public_send不能访问私有方法。
在编写方法的时候允许传递任意数量的参数,在单独的方法参数名称前使用一个*即可:
def obj.multi_args(*x)
puts "I can take zero or more arguments!"
end
符号*x表示在调用方法的时候,可以提供任意数量的参数。变量x被分配一个对应任意参数的数组。可以在稍后使用数组每次测试其中的一个。
一般情况下,参数都是变量的引用,如果不想变量发生变化,可以传入s.dup复制了一个对象。
def change_string(s)
s.replace("New String!")
end
s = "Original String"
change_string(s)
change_string(s.dup)
还可以冻结一个变量,冻结后不能再对这个变量发生改变
s.freeze