一、面向对象基础
1.1 定义
class Point def initialize(x, y) @x = x @y = y end end point = Point.new(0, 1) puts point.is_a? Point # true
其中 initialize 方法充当了构造器,以 @ 开头的变量是实例变量,也就是说是 Java 中的属性。
我们利用 new 方法创造一个变量,类似于一个工厂方法。如果往深里探究,应该是 new 方法是一个类对象方法,也就是说,Point 可以作为一个类,同时它也是 Class 这个类的一个对象,他作为对象有一个方法是 new 。这个 new 会调用实例方法(就是 java 中的非静态方法) new ,然后完成构造。
is_a?是一个方法,与 Java 中的 instanceof 类似。只要是这个类或者这个类的子类,都会返回 true ,与之相似的是 kind_of? 。 而如果是 instance_of? 那么就只有该类会返回 true。
1.2 实例方法
1.2.1 to_s
转换成字符串的方法,如下
def to_s "(#{@x}, #{@y})" end
1.2.2 getter, setter
我们可以用常规的方法定义,没错,感觉 ruby 的方法名中可以出现各种特殊字符
# getter def x @x end def y @y end # setter def x=(value) @x = value end def y=(value) @y = value end
这种方法无疑是最好的,因为可以掩盖内部具体的实现细节,不过如果只是普通的 getter, setter 那么其实可以利用元编程的知识,直接用 Module 的方法。
attr_accessor :x, :y
所谓的“元编程”就是利用代码创造代码,这里就不详细说了。attr_accessor 是一个 Module 的方法,Class 是 Module 的子类,可能 Point 也是 Module 的子类,所以可以使用这个方法。
1.2.3 operator
和 C++ 一样,Ruby 是可以定义运算符的,而且似乎更加简洁,跟定义方法一模一样,这可能是由于 Ruby 的方法声明中允许特殊字符的存在,而且单参数调用时可以不加括号导致的。我们为 Point 定义三个方法
定义加法:
def +(other) Point.new(@x + other.x, @y + other.y) end
定义负号,这里需要注意,有一个 -@ 的特殊写法
# negative def -@ Point.new(-@x, -@y); end
定义数乘,这里和 C++ 一样,是没有办法写 3 * point 的,只能写 point * 3
def *(scalar) Point.new(@x * scalar, @y * scalar) end
为了解决这个问题,我们可以定义个 coerce 方法,似乎这个方法会在 3.* 的时候使用,然后返回的值会重新调用 point.* 但是具体的原理就不清楚了。
def coerce(other) [self, other] end
定义索引访问:
def [](index) case index when 0, -2 then @x when 1, -1 then @y when :x, "x" then @x when :y, "y" then @y else nil end end
这个是一个只读数组,如果想写这个东西,需要其他的东西。
1.2.4 iterator
我们可以定义迭代器方法 each
class Point ... def each yield @x yield @y end include Enumerable end
底下这个 include 很有意思,学名叫做混入。似乎 Enumerable 这个模块里有很多基于 each 的方法,所以只要混入这个模块,就可以拥有这一堆的迭代器方法(似乎叫做枚举器方法更为合适)
pointA.each {|e| puts e} puts pointA.all? {|e| e == 0} puts Point.new(0, 0).all? {|e| e == 0}
1.2.5 equal
在 Ruby 中一般与 Java 相反,我们用 == 表示内容上的相等,而用 eq? 表示同一个引用。所以我们一般需要重写 == 号
def ==(other) if other.is_a? Point @x == x && @y == y else false end end
但是同时我们又在 Hash 结构中,使用的是 eq? 这个方法(所以前面那个约定好没用),和 Java 一样,如果 eq? 判断相等了,hash 就必须判断相等。所以只要定义了 eq? ,那么就需要定义 hash ,并且满足前面的条件。
1.2.6 Comparable
只要定义了 就可以获得一大堆比较符。
include Comparable def (other) @x**2 + @y**2 other.x**2 + other.y**2 end
Comparable 也是一个混入模块。
1.3 类方法
对应的是 Java 中的静态方法,但是实际上很不一样。类方法在 ruby 中是类对象的单键方法,也就是说,类本身就是一个对象。定义单键方法,需要指定方法所属的对象。如图所示
class Point ... def self.sum(*points) # self means the "Point", not the point x = y = 0 points.each {|p| x += p.x; y += p.y} Point.new(x, y) end end # single-key puts "sum = #{Point.sum(pointA, pointB, pointC)}"
1.4 类常量
定义在类中的常量,这个对应 Java 中的静态常量,如下所示
class Point ... def initialize(x, y) end ... ORIGIN = Point.new(0, 0) end puts "Origin = #{Point::ORIGIN}" Point::UNIT_X = Point.new(1, 0) puts "Unit x = #{Point::UNIT_X}"
有一个很有意思的,就是 ORIGIN 要在 initialize 之后,就很神奇,大概还有声明先后的问题吧。
1.5 类变量
定义在类中的变量,静态变量,如下所示
class Point @@n = 0 @@totalX = 0 @@totalY = 0 ... def initialize(x, y) @x = x @y = y @@n += 1 @@totalX += x @@totalY += y end def self.report puts "There are #{@@n} points" puts "total x is #{@@totalX}" puts "total y is #{@@totalY}" end end Point.report
1.6 方法的可见性
默认方法一般都是 public ,除了 initialize 。当然对于全局的方法(就是函数),他是 Object 的实例私有方法。
同样是三种:
- private:没法用 object.method 或者 self.method 调用,只能直接 method 使用。这只是定义,如果要解释的话,那么就是适用于 method 这种写法的东西,就是其他实例方法。
- protected:只能在
二、面向对象高阶
3.1 Struct
利用 Struct 类可以快速创造出一个类,我的评价是充满了神秘,比如说之前实现的 Point
OtherPoint = Struct.new(:x, :y) class OtherPoint def +(other) Point.new(self.x + other.x, self.y + other.y) end def to_s "(#{self.x}, #{self.y})" end end
感觉 Ruby 十分灵活,可以随时改变类,而且可以多次改变类,如下所示
OtherPoint = Struct.new(:x, :y) class OtherPoint def +(other) Point.new(self.x + other.x, self.y + other.y) end def to_s "(#{self.x}, #{self.y})" end end otherPointA = OtherPoint.new(4, 3) puts otherPointA otherPointA.x = 1 class OtherPoint undef x=, y= end # otherPointA.x = 2 NoMethodError
3.2 打开
这个就很本质了,就是对于这样的语句
class SomeClass ... end module SomeModule ... end
除了第一次有定义的意思,只有都是有“给原有的东西添砖加瓦的意思”。每次的“打开”,都意味着一种 self 指针的变换和作用域的变化。
3.3 泛化关系
基本上所有的类都是 Object 类的子类,Class 是 Module 类的子类,所有的类都是 Class 类的实例化,所有的模块都是 Module 的实例化 。Kernel 是一个 Module,它混入了 Object ,所以哪里都有他,他似乎提供了基本的方法实现(类似于某种 stdlib)。
跟前面的 Struct 类似,也可以有很多神秘的语法
M = Module.new # some module called M C = Class.new # some class called C D = Class.new(C){include M} # some C subclass called D, which include M
3.4 include, extend
利用 include 和 extend 都可以进行 Mixin(混入操作),对于 include 就是直接混入到实例上? extend 是混入到类上?
3.5 关系
三、函数式特征
3.1 block
block 是 一小段代码,用 {} 包裹, 用 yield 关键字调用。迭代器就是基于这个实现的。
def call_twice yield yield end call_twice {puts "Hello World"}
yield 是可以传参的
def call_twice yield(1) yield(2) end call_twice { |x| puts "Hello World! #{x}"}
3.2 Proc
3.3 Lambda