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 /[^\w\s]/, ''
 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
,以便于您获取更多的相关知识。

时间: 2024-10-22 01:05:46

Ruby面向对象编程中类与方法的基础学习_ruby专题的相关文章

Ruby面向对象编程中类的方法与类的扩展_ruby专题

类方法 类方法其实质是生活在该类的单件类中的单件方法.其定义方法有三种,分别是: # 法一 def MyClass.a_class_method; end # 法二 class MyClass def self.anther_class_method; end end # 法三* class MyClass class << self def yet_another_class_method; end end end 其中第三种方法道出了,类方法的实质,特别记忆一下! 类扩展 类扩展通过向类的

实例解析Ruby设计模式编程中Strategy策略模式的使用_ruby专题

今天你的leader兴致冲冲地找到你,希望你可以帮他一个小忙,他现在急着要去开会.要帮什么忙呢?你很好奇. 他对你说,当前你们项目的数据库中有一张用户信息表,里面存放了很用户的数据,现在需要完成一个选择性查询用户信息的功能.他说会传递给你一个包含许多用户名的数组,你需要根据这些用户名把他们相应的数据都给查出来. 这个功能很简单的嘛,你爽快地答应了.由于你们项目使用的是MySQL数据库,你很快地写出了如下代码: require 'mysql' class QueryUtil def find_us

Ruby的面向对象编程的基础教程_ruby专题

Ruby 是纯面向对象的语言,Ruby 中的一切都是以对象的形式出现.Ruby 中的每个值都是一个对象,即使是最原始的东西:字符串.数字,甚至连 true 和 false 都是对象.类本身也是一个对象,是 Class 类的一个实例.本章将向您讲解所有与 Ruby 面向对象相关的主要功能. 类用于指定对象的形式,它结合了数据表示法和方法,把数据整理成一个整齐的包.类中的数据和方法被称为类的成员.Ruby 类定义 当您定义一个类时,您实际是定义了一个数据类型的蓝图.这实际上并没有定义任何的数据,而是

Ruby面向对象编程详解_ruby专题

Ruby是纯面向对象的语言,所有项目似乎要Ruby中为一个对象.Ruby中的每个值是一个对象,即使是最原始的东西:字符串,数字甚至true和false.即使是一个类本身是一个对象,它是Class类的一个实例.本章将通过所有功能涉及到Ruby的面向对象. 类是用来指定对象的形式,它结合了数据表示和方法操纵这些数据,转换成一个整齐的包.在一个类的数据和方法,被称为类的成员. Ruby类的定义: 定义一个类,定义的数据类型的草图. 这实际上并不定义任何数据,但它定义的类名字的意思什么,即是什么类的对象

javascript 面向对象编程 function是方法(函数)_js面向对象

好程序不是写给computer的,而是写给human的.遇到复杂功能,应该想着把它简化.组件化,把小功能封装成小组件,小功能块可以任意的组合得到千变万化的复杂功能.function就可以帮助我们把功能进行封装.那什么是封装呢.要我说,只要把具体实现给打包,对外提供调用接口那就是封装,方法也好.类也好就做了这些事.       javascript中的function可以用来创建方法.也可以用来创建类,实际上我们可以认为是用function来模拟出的类(说到类一般都会要去了解闭包的知识).还是先看

简要解读Ruby面向对象编程中的作用域_ruby专题

作用域 Ruby中不具备嵌套作用域(即在内部作用域,可以看到外部作用域的)的特点,它的作用域是截然分开的,一旦进入一个新的作用域,原先的绑定会被替换为一组新的绑定. 程序会在三个地方关闭前一个作用域,同时打开一个新的作用域,它们是: 类定义class 模块定义 module 方法定义 def 上面三个关键字,每个关键字对应一个作用域门(进入),相应的end则对应离开这道门. 扁平化作用域 从一个作用域进入另一个作用域的时候,局部变量会立即失效,为了让局部变量持续有效,可以通过规避关键字的方式,使

真正面向对象编程:PHP5.01发布_php基础

PHP是一种简单,实用的动态网页编程语言.目前最新版为5.01,其使用帮助内含最新UNIX和Windows安装文档,方便初学者使用! 最新版的更新文档列出了众多更新特性,修正的bug.同4.XX一系列的版片相比,它主要的优点就是真正支持面向对象编程(OOP).然而,5.01不同于以前4.XX版的开发水平,要成为主流动态网页开发工具还需一段时间. PHP 5.01新特性 -改变析构机制,以便更好优先调用或请求关闭. -重写UNIX和Windows安装帮助文件. -更新几个微软发布的库,包含libx

Ruby编程中的语法使用风格推荐_ruby专题

使用 :: 引用常量(包括类和模块)和构造器 (比如 Array() 或者 Nokogiri::HTML()).     永远不要使用 :: 来调用方法. # bad SomeClass::some_method some_object::some_method # good SomeClass.some_method some_object.some_method SomeModule::SomeClass::SOME_CONST SomeModule::SomeClass()     使用括

详解Ruby中的单件方法和单件类_ruby专题

单件方法 Ruby允许给单个对象增加方法,这种只针对单个对象生效的方法,称为单件方法 示例代码 str = "just a regular string" def str.title? self.upcase == self end str.title? # => false str.methods.grep(/title?/) # => [:title?] str.singleton_methods #=> [:title?] str.class # => S