Ruby Ruby 中的类与对象

chalvern · 2017年03月26日 · 最后由 chalvern 回复于 2017年03月26日 · 3915 次阅读

Ruby 中的类

打开类 (Open Class)

在 Ruby 中,类和其他代码(对象、函数等)没有本质的区别。可以在类的定义中放任何代码:

3.times do
  class C
    puts "hel"
  end
end
#输出
#hello
#hello
#hello

Ruby 会在类中运行任何可以运行的代码。但是需要重点的说明的是,上面的代码并不意味着定义了三个 C 类,比如下面的代码:

class D
  def x; 'x'; end
end

class D
  def y; 'y'; end
end

obj = D.new
obj.x   # => "x"
obj.y   # => "y"

从代码可以知道,上面的两个类 D 包含了两个地方定义的方法。也就是说,在第二次定义 D 类时,Ruby 能够找到第一次定义的 D 类并把新的方法(y)添加到 D 类中。在 Ruby 术语中,这叫做“打开类”(Open class)

换句话说,可以在任何时候打开一个已经存在的类,并向里面添加新的方法——包括 String 和 Array 这样的标准类。

【注意】 Ruby 的这种特性是完全不同于其他常见语言,比如 Java、Python 等。可能未接触过 Ruby 的人会觉得这样太乱了,比如可能(而且很有可能,尤其当不同人做开发时)在不同的时间在同一个类中引入同样的一个方法,那么就容易引起混乱(一般称这种在类中全局引入方法的方式叫做猴子补丁 [Monkeypatch])。但是,还是要补充一句,对于有自制力并且时刻记得“能力越大,责任越大”的人来说,Ruby 的这种特性能带来很牛 x 的体验。

Ruby 中的对象

实例变量 (instance variables)

Ruby 中的对象也是一个比较有意思的东西,与 Java、Python 等也具有很大的不同。比如下面的代码:

class MyClass
  def my_method
    @v = 1
  end
end

obj = MyClass.new
obj.class   # => MyClass

obj.instance_variables  # => []

obj.my_method
obj.instance_variables  # => [:@v]

在 Ruby 中,对象中存在实例变量 (instance variables) ,可以通过方法 instance_variables 进行查看当前对象中的 实例变量

不过这里有意思的一点是,Ruby 中的实例变量和 是完全不搭嘎的。也就是说,Ruby 中对象中有哪些实例变量并不受类定义的影响。比如上面代码,在调用 obj.my_method前后,obj 中的实例变量是不一样的。或者可以这么认为:Ruby 中同一个类的不同实例对象可以具有不同的实例变量。(对于 javaer 或者 pythoner 来说,简直又一个喜闻乐见的特性)

方法 (Methods)

Ruby 中的方法和其他语言中的方法类似,对于每个实例化对象,它只保存自己的实例变量,而方法都保存在对应的类中。

obj(OBJECT) MyClass (CLASS)
@v = 1 (class) ——> my_method()
(instance variables) (Methods)

这里需要注意的是,Ruby 中的方法同样分 类方法和实例方法,其中实例方法只能对象被调用,而类方法则只能被类调用。

总结一下就是,类似于 Java 等语言,对象的实例变量在对象本地存在,而对象的方法在对象所对应的类中存在。但是和 Java 等编程语言不同的是,Ruby 中的类其实也是一种对象。那么 Ruby 基于这个前提又能引起哪些不可思议的特性呢,后面会一一道来。

Ruby 中类的真实面目

是的, Ruby 中的类其实是一种对象。

因为类也是一种对象,因此所有对象的特性也是类具有的特性。比如,(Class) 有自己的类,这个类就是——Class,举个例子,假如一个字符串可以通过 str#class 获得它的类 String,那么 String 作为一个对象也可以通过 String#class 获知它自己的类。比如下面的代码:

"hello".class   # => String
String.class    # => Class
Class.class     # => Class

这是一个完全不同于 Java 等开发语言的特性。可以这么认为,在 Java 中,类的实例对象只是一个可读的类,运行时可以随意生成某类的对象,却不能随意修改类的结构;但是在 Ruby 中,类的限制规范更少,可以像对象那样在运行时定义动态的类(通过 Class.new )。或者更简单地说:在运行时,其他语言只允许读类的信息,但是 Ruby 还允许编辑类的信息。

觉得打猴子补丁太可怕,你可以试试 refine 呀

ecnelises 回复
class MyClass
  def my_method
    "original my_mythod()"
  end

  def another_method
    my_method
  end
end

module MyClassRefinement
  refine MyClass do 
    def my_method
      "refined me_method()"
    end
  end
end

using MyClassRefinement
p MyClass.new.my_method   # => "refined me_method()"
p MyClass.new.another_method  # => "original my_mythod()"

refine 的使用也有限制的,感觉更不好把握。。。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号