Ruby面向对象编程中类与做法的基础学习实用指南
平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“Ruby面向对象编程中类与做法的基础学习”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
打开类和猴子补丁
落到代码里,在Ruby里,类定义的方法和其他的语句没有任何区别,都是一行一行的执行下去的。如下所示例子:
class Example
def method_1
puts "method 1"
end
end
class Example
def method_2
puts "method 2"
end
end
结合项目来看,本例中,当第一次定义Class Example的时候,还没有一个叫做Example的Class存在,因此,Ruby开始定义这个类,当后面在定义这个类时,Ruby会发现该类已存在,同时得到这个类,而不是定义一个新类。
理解这一步时,因为这个特性,因此,Ruby天生具有打开一个已经存在的类,同时动态修改其内容的能力,即使其是标准类库的类也不例外。比方说,能够给SDK的String类添加一个去除String中的标点符号和特殊字符的方法:to_alphanumeric
class String
def to_alphanumeric
gsub /[^ws]/, ''
end
end
“H&&^^ello”.to_alphanumeric #==>Hello
理解这一步时,,随后,所有的String对象都具备“to_alphanumeric”的能力了,这种技术一般简称为打开类技术。
实际处理时,上面描述的打开类技术其实是隐含了一定的风险的,尤其是在大型系统中采用打开类技术扩展标准类库时,因为,很多开发人员都在扩展类,当多个扩展方法的名字一样时, 后定义的总会覆盖掉前面,从而导致整个系统的崩溃,业界把这种鲁莽的修改类的方式简称为猴子补丁(Monkey Patch)。所以在采用打开类技术时,一定要慎之又慎。
实例变量
理解这一步时,在Ruby中,实例变量是存储在对象里,但是,其于该对象的类没有关系,当给对象的实例变量赋值时,该实例变量就生成了,说白了,实例变量就像是一个挂载在对象上的HashMap,每个对象都能够用自己不同的HashMap, 如下所示例:
class Person
def name
@name = "xianlinbox"
end
end
p = Person.new
puts p.instance_variables #==>nil
p.name
puts p.instance_variables #==>@name
方法
落到代码里,作为一个对象,除了有实例变量(也能够称之为属性),还需有方法。 但是在Ruby中,关于方法的定义同时不在对象中,而是在对象自身的类中,这是因为“共享同一个类的对象也必须共享同样的方法”。但是,不能说Class有一个叫做“method”的方法,因为无法采用"Class.method"调用该方法,而要说Class有一个实例方法“method”,这意味着必须新建该类的实例对象,借助实例对象调用该方法。
若要定义类方法,那么在定义方法的时候,必须加类名前缀,如下所示:
class Person
def Person.name
@name = "xianlinbox"
end
end
类本身也是对象
从实现思路看,在Ruby中Class本身也是一个对象,关于对象的所有规则都适用来Class.
puts "hello".class #=> String
puts String.class #=> Class
puts Class.class #=> Class
puts Class.instance_methods(false) #=> [:superclass,:allocate,:new]
puts Class.instance_variables #=> nil
类的继承体系
puts String.superclass #=> Object
puts Class.superclass #=> Module
puts Module.superclass #=> Object
puts Object.superclass #=> BasicObjec
puts BasicObject.superclass #=> nil
BasicObject是继承体系的根节点。
所有类都继承自Object。
从实现思路看,Class是对Module的继承增强,增加了new()和allocate()方法以新建实例。
方法的查找与执行
实际处理时,Ruby中对象的方法都定义在类中,当对象想要执行一个方法时,首先需找到该方法,而Ruby编译器查找方法的方式就是,第一步在自己的类中找,没有的话,就沿着该类的祖先链(ancestors)一直往上找。
String.ancestors # => [String, Comparable, Object, Kernel, BasicObject]
落到代码里,为什么这儿会出现Comparable和Kernal, 这是因为Module的机制,当一个类include一个模块时,编译会把该模块放在最靠近该类的祖先链上, String类include了Comparable模块,而Kernal则是被Object类include的。
理解这一步时,方法执行的时候需一个接收者,方法就会在接收者对象中被执行,该接收者就是所谓的self对象。一般情况下,self对象是由最后一个接收该方法的对象担当,在类和模块的定义中(同时且在任何方法的定义外),self对象由类或模块担任。
动态调用方法
结合项目来看,通常方法的调用方式是“对象名.方法名”,在Ruby中有一个很酷的特性,能够借助send()方法,把想调用的方法名作为参数,这样就能够在代码运行时,直到最后时刻才决定调用哪个方法,这种技术称之为动态派发(Dynamic Dispatch)。这个技术很有用,比方说,当在项目里有一个设置文件对象,会根据设置文件初始化,在采用过程中,不同用户可能会设置不同的值。通常做法是,判断属性的键值是对应到哪个属性,随后,调用对应的set方法,代码如下所示:
config.name = v if isNameProperty?(k)
config.password = v if isPasswordProperty?(k)
config.port = v if isPortProperty?(k)
...
落到代码里,看着这么一堆的长得像亲兄弟似的代码,不由得产生一种阉掉它们的冲动。如果采用动态调用方法的话,代码能够简化如下所示:
load_config('config.properties').each do |k, v|
config.send("#{k}=", v)
end
实际处理时,根据拿到的每个键值,去调对应属性的set方法,代码清爽很多,而且以后扩展config对象不需修改load方法。
动态定义方法
落到代码里,除了动态调用方法外,Ruby甚至兼容动态定义方法,借助采用Module#define_method()方法,提供一个方法名和一个充当方法体的块即可定义一个方法。例:
class MyClass
define_method :doubleString do |args|
args * 2
end
end
t = MyClass.new
puts t.doubleString("he") # => hehe
结合项目来看,有了这个黑魔法之后,以后,就能够多个相似方法中不同的部分抽出来作为参数,随后,采用一个方法定义搞定所有的方法。
method_missing()方法
落到代码里,Ruby是动态语言,编译器同时不会检测方法调用时的行为,因此你能够调用一个不存在的方法。 在运行时,所有找不到对应方法的调用都会调用一个method_missing()方法,该方法定义在Kernal模块中,因此每个对象都继承了该方法。在kernal中method_missing()方法,会抛出一个NoMethodError的异常,这就是编译器所做的工作。
结合项目来看,但是,我们能够覆写这个方法,让它变得很有意思。比方说,新建一个Struct,当你想要新的属性时,只需给它赋个值就神奇的产生了。
class MyStruct
def initialize
@attributes = {}
end
def method_missing(name, *args)
attribute = name.to_s
if attribute =~ /=$/
@attributes[attribute.chop] = args[0]
else
@attributes[attribute]
end
end
end
s = MyStruct.new
s.weibo = "@xianlinbox"
puts s.weibo # => @xianlinbox
结合项目来看,这种,从调用者角度看,跟普通方法没什么区别,但是实际接收者却同时没有相对应的方法的方法,Ruby术语称之为幽灵方法(Ghost Method)。对于幽灵方法,除了自定义该方法以外,还能够把该方法转发给另外一个对象的方法,当然,你能够在转发前后包装一些自己的逻辑,这种处理技术称之为动态代理(Dynamic Proxy),和前面提到的动态调用异曲同工。
结合项目来看,采用了method_missing处理幽灵方法后,所有不存在方法的调用都会到这里来,这可能会导致一些错误信息不直观的问题(你看不到NoSuchMethod这样的提示了),因此, 在采用method_missing方案的时候,一定要限定其采用范围,同时且调用父类的super.method_missing方法,让不属于这个范围的,该报什么错还是报什么错。
白板类与保留方法
理解这一步时,每一个类都有从父类继承下来的一堆方法,在采用动态代理技术时,如果一个幽灵方法和真实的方法(你没有意料到的继承来的方法)发生名字冲突时,后者会获胜,从而导致系统报错。所以在采用动态代理技术时, 能够在代理类中删除绝大多数继承来的方法,避免发生名字冲突,删除了继承方法的类,就是所谓的白板类(Blank Slate),删除类方法的途径有2个,一个是调用Module#undef_method方法,一个是调用Module#remove_method方法。
从实现思路看,在Ruby的Object类里,有一些方法是内部采用的,如果对其重新定义,或删除,可能导致Ruby莫名其妙的挂掉,为了防止这种事情的发生,Ruby在这些方法名前面用“__”打头,这些方法称之为保留方法,当你试图修改这些方法时,Ruby会给出警告。
您可能感兴趣的文章:
- Ruby面向对象编程中类的方法与类的扩展
- 简要解读Ruby面向对象编程中的作用域
- Ruby的面向对象方式编程学习杂记
- Ruby的面向对象编程的基础教程
- Ruby面向对象编程详解
- ruby 面向对象思维 概念
- Ruby 面向对象知识总结
-
09.04
如何打开AI格式文件的方法与有哪些-类型差异和选择建议
-
09.04
Lion Safari Elf An提示词怎么写-画面元素和风格
-
09.04
Hot Air Balloon Vl提示词怎么写-画面元素和风格
-
09.04
社群运营复盘:用Gamma_AI梳理用户增长漏斗【运营】
-
09.04
codex模型选择和价格有什么关系
-
09.04
Korean提示词怎么写-画面元素和风格参数
-
-
-
-
- 冒泡排序算法及Ruby版的简单实现方式实用指南
- 09.04
-
- Ruby面向对象编程中类与做法的基础学习实用指南
- 09.04
-
- 完整指南Ruby中的代码块对象Proc
- 09.04
-
-
下载
- |
-
-
下载
- 《行尸走肉第一章》免安装中文汉化硬盘版下载
- 单机|436 MB
- 一款以动作冒险为主题的游戏
-
-
下载
- 《街头霸王X铁拳》免安装中文汉化硬盘版下载
- 单机|111MB
- 一款非常好玩的格斗游戏
-
-
下载
- |
-
-
下载
- 《暗黑破坏神3》免安装繁体中文正式版下载
- 单机|7630 MB
- 一款以角色扮演为主题的游戏
-
-
下载
- 《马克思佩恩3》免安装硬盘版下载
- 单机|27033 MB
- 一款以第三人称射击为主题的游戏