class Tree
# 定义实例变量,使用attr或attr_accessor关键字,前者定义变量和访问变量的同名getter方法(即只读),后者定义的变量多了同名setter方法(注意这里使用了符号)
attr_accessor :children, :node_name
# 构造方法(构造方法必须命名为initialize)
def initialize(name, children=[])
@node_name = name
@children = children
end
# 遍历所有节点并执行代码块block,注意参数前加一个&表示将代码块作为闭包传递给函数
def visit_all(&block)
visit &block
children.each {|c| c.visit_all &block}
end
# 访问一个节点并执行代码块block
def visit(&block)
block.call self
end
end
ruby_tree = Tree.new("Ruby",
[Tree.new("Reia"),
Tree.new("MacRuby")])
# 访问一个节点
ruby_tree.visit {|node| puts node.node_name}
# 访问整棵树
ruby_tree.visit_all {|node| puts "Node: #{node.node_name}"}
再提一下Ruby的命名规范:
(1)类采用CamelCase命名法
(2)实例变量(一个对象有一个值)前必须加上@,类变量(一个类有一个值)前必须加上@@
(3)变量和方法名全小写用下划线命名法,如underscore_style
(4)常量采用全大写下划线命名法,如ALL_CAPS_STYLE
(5)用于逻辑测试的函数和方法一般要加上问号,如if test?
3. 模块与混入(Mixin)
面向对象语言利用继承,将行为传播到相似的对象上。若一个对象像继承多种行为,一种做法是用多继承,如C++;Java采用接口解决这一问题,Ruby采用模块Mixin。模块是函数和常量的集合,若在类中包含一个模块,那么该模块的行为和常量也会成为类的一部分。
# 定义模块ToFile
module ToFile
# 获取文件名
def filename
"object_name.txt"
end
# 创建文件
def to_f
File.open(filename, 'w') {|f| f.write(to_s)} # 注意这里to_s在其他地方定义!
end
end
# 定义用户类
class Person
include ToFile
attr_accessor :name
def initialize(name)
@name = name
end
def to_s
name
end
end
Person.new('matz').to_f # 创建了一个文件object_name.txt,里面包含内容matz
上面的代码很好理解,只是有一点要注意:to_s在模块中使用,在类中实现,但定义模块的时候,实现它的类甚至还没有定义。这正是鸭子类型的精髓所在。写入文件的能力,和Person这个类没有一点关系(一个类就应该做属于它自己的事情),但实际开发又需要把Person类写入文件这种额外功能,这时候mixin就可以轻松胜任这种要求。
Ruby有两个重要的mixin:枚举(enumerable)和比较(comparable)。若想让类可枚举,必须实现each方法;若想让类可比较,必须实现<=>(太空船)操作符(比较a,b两操作数,返回1、0或-1)。Ruby的字符串可以这样比较:'begin' <=> 'end => -1。数组有很多好用的方法:










