平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“Ruby的面向对象方式编程学习杂记”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
打开类
在这个场景下,能够重新打开已经存在的类同时对之进行动态修改,即使像String或者Array这样标准库的类也不例外。这种行为方式称之为打开类(open class)
猴子补丁
落到代码里,若你粗心地为某个类添加了新功能,同时覆盖了类原来的功能,进而影响到其他部分的代码,这样的patch称之为猴子补丁(Monkeypatch)
类与模块
理解这一步时,Ruby的class关键字更像是一个作用域操作符,而不是类型声明语句。class关键字的核心任务是把你带到类的上下下文,让你能够在里面定义方法。
从实现思路看,每个类都是一个模块,类就是带有三个方法(new,allocate,superclass)的增强模块,借助这三个方法能够组织类的继承结构,同时新建对象
从实现思路看,Ruby中的类和模块的概念十分接近,完全能够将二者相互替代,之所以同时保留二者的原因是为了保持代码的清晰性,让代码意图更加明确。采用原则:
- 希望把自己代码包含(include)到别的代码中,应该采用模块
- 希望某段代码被实例化或被继承,应该采用类
- 模块机制能够用来实现类似其它语言中的命名空间(Namespace)概念
Ruby中的::符号
实际处理时,Ruby中常量的路径(作用域),类似与文件系统中的目录,借助::进行分割和访问,默认直接以::开头(例: :: Y)表示变量路径的根位置
什么是对象
结合项目来看,对象就是一组实例变量外加一个指向其类的引用。对象的方法同时不存在于对象本身,而是存在于对象的类中。
什么是类
在这个场景下,类就是一个对象(Class类的一个实例)外加一组实例方法和一个对其超类的引用。Class类是Module类的子类,所以一个类也是一个模块。
load与require方法的异同
从实现思路看,借助load和require都能够进行导入别人的代码,不同的是load方法用来加载代码,如果不希望污染当前的命名空间,需借助load(‘file.rb',true)显式的要求新建一个匿名模块来,接管file.rb的常量,require用来导入类库,此外,就加载次数上load方法每次调用都会再次运行所加载文件,require则对每个库文件只加载一次。
prepend、include与祖先链
从实现思路看,祖先链用来描述Ruby对象的继承关系,因为类与模块是父子关系,所以祖先链中也能够包含模块,prepend与include分别能够向链中添加模块,不同的是调用include方法,模块会被插入祖先链,当前类的正上方,而prepend同样是插入到祖先链,但位置其他却在当前类的正下方,另外借助Class.ancestors能够查看当前的祖先链
private规则
在这个场景下,不能借助明确指定接受者来调用私有方法。私有方法只能借助隐性的接受者self调用(Object#send是个例外)
self相关
从实现思路看,调用一个方法时,接受者会扮演self角色 任何没有明确指定接受者的方法调用,都当做是调用self的方法 定义一个模块(或类)时,该模块(或类)会扮演self角色
对象、类与模块之间关系

在这个场景下,上面Module.class指向的也是Class类,能够理解为上面方框内容均为Class,但他们的父子组织关系借助superclass建立同时存在异同,能够借助Class.ancestors查看。
动态方法
动态调用方法
在Ruby中借助Object#send方法能够代替点标识调用对象的指定实例方法
示例
class MyClass
def my_method(my_arg)
my_arg * 2
end
end
obj = MyClass.new
obj.my_method(3) #=> 6
obj.send(:my_method, 3) #=> 6
落到代码里,上面代码借助直接调用和采用send方法调用得到的结果是一样的,采用send的好处是,能够在编码中,动态的决定方法调用。这个技巧在元编程中被称为动态派发
从实现思路看,另外需指出的地方是借助Object#send不仅能够调用公共方法,也能够调用对象的私有方法。如果想保留对象的封装特性,不向外暴露私有方法能够采用Object#public_send方法。
动态定义方法
在这个场景下,除了方法的动态调用之外,Ruby还借助Module#define_method方法和代码块提供了动态方法定义方式
示例
class MyClass
define_method :my_method do |my_arg|
my_arg * 3
do
end
obj = MyClass.new
obj.my_method(2) #=> 6
实际处理时,上面代码借助define_method方法取代了关键词def,其本质上都是相同的,只是在定义方式上,define_method的方式更加灵活一些,能够借助在编码中借助推导,完成函数的定义,增加了实现的灵活性。
method_missing方法
落到代码里,严格意义上将method_missing方法,同时不算是明确的定义(不会出现在methods列表中),其本质是借助方法查找的机制来截获调用信息进而合理的给出相应方法的回应。有点类似与异常处理中的抛出异常,一层一层的往外抛。
理解这一步时,method_missing借助的机制是,当一个对象进行某个方法调用的时候,会到其对应的类的实例方法中进行查找,如果没有找到,则顺着祖先链向上查找,直到找到BasicObject类为止。如果都没有则会最后调用一个BasicObject#method_missing抛出NoMethodError异常。
落到代码里,当我们需定义很多相似的方法时候,能够借助重写method_missing方法,对相似的方法进行统一做出回应,这样一来其行为就类似与调用定义过的方法一样。
示例
class Roulette
def method_missing(name, *args)
person = name.to_s.capitalize
super unless %w[Bob Frank Bill Honda Eric].include? person
number = 0
3.times do
number = rand(10) + 1
puts "#{number}..."
end
"#{person} got a #{number}"
end
end
number_of = Roulette.new
puts number_of.bob
puts number_of.kitty
动态代理
落到代码里,对一些封装过的对象,借助method_missing方法收集调用,同时把这些调用转发到被封装的对象,这一过程称为动态代理,其中method_missing体现了动态,转发体现了代理
const_missing方法
在这个场景下,与method_missing类似,还有关于常量的const_missing方法,当引用一个不存在的常量时,Ruby会把这个常量名作为一个符号传递给const_missing方法。
白板类(blank slates)
实际处理时,拥有极少方法的类称为白板类,借助继承BasicObject类,能够迅速的得到一个白板类。除了这种方法以外,还能够借助删除方法来将一个普通类变为白板类。
删除方法
删除某个方法有两种方式:
- Module#undef_method
- Module#remove_method
实际处理时,二者的区别是Module#undef_method会删除所有(包括继承而来的)方法。而Module#remove_method只删除接受者自己的方法,而保留继承来的方法。
动态方法与Method_missing的采用原则
实际处理时,当能够采用动态方法时候,尽量采用动态方法。除非必须采用method_missing方法(方法特别多的情况),否则尽量少采用它。
- Ruby面向对象编程中类的方法与类的扩展
- Ruby面向对象编程中类与方法的基础学习
- 简要解读Ruby面向对象编程中的作用域
- Ruby的面向对象编程的基础教程
- Ruby面向对象编程详解
- ruby 面向对象思维 概念
- Ruby 面向对象知识总结

