您的位置:首页 > 编程语言 > Ruby

Ruby系列学习资料(三)

2008-01-25 16:58 232 查看
三、OOP in Ruby
Ruby的所有元素与OOP语言关系更密切,如对象封装,数据隐藏,方法的多态和覆写,类的层次和继承。 它更进一步地添加了有限制的元类特征,单态方法,模块和混插。
类似的概念在其它OOP语言内使用了其它的名字,但是相同名字的概念在语言之间也有些差别。这个部分详尽阐述OOP的这些元素在Ruby中的理解和用法。

1、对象

在Ruby中,所有的数字,字符串,数组,正则表达式,和更多其它实体都是真正的对象。工作通过执行属于对象的方法来完成:
3.succ
# 4

"abc".upcase
# "ABC"

[2,1,5,3,4].sort
# [1,2,3,4,5]
someObject.someMethod
# some result
在Ruby中,每个对象都是一些类的实例,类包含方法的实现。对象的类本质上是它的类型:

"abc".type
# String

"abc".class
# String
除了封装它自己的属性和操作之外,Ruby内的对象有个标识。

"abc".id
#
53744407


2、内建类

在Ruby的类层次中预定义有超过30个的内建类。像大多数其它的OOP语言,Ruby不允许多重继承,但是那并不会减少它的强大。现代面向对象的语言经常允许单根继承模式。Ruby支持下一节讨论的模块和混插。它也实现了对象的ID,来支持对永续性,分布性,和可再配置对象的实现。
要从现有类创建对象,典型地是使用new方法:
myFile = File.new("textfile.txt","w")
myString = String.new("this is a string object")
然而,这并不总是被明确要求的,像这儿显示的:
yourString = "this is also a string object"
aNumber = 5
变量被用于持有对对象的引用。像先前提到的,变量本身没有类型,它们不是对象本身;它们只对象的简单引用。这儿个例子:
x = "abc"
它的例外是那些小的,固定不变的,内建类的对象,如Fixnum,被直接复制到它们的变量中。(这些对象没有指针大,并且以这种方式更高效地处理它们。)在这种情况下,赋值是对对象的拷贝,并且堆(内存分配区域)不再被使用。
变量赋值会引起对象引用被共享:
y = "abc"
x = y
x
# "abc"
x = y 被执行之后,变量x和y两者引用同一个对象:
x.id
# 53732208
y.id
# 53732208
如果对象是可变的,对一个变量完成修改操作将会影响到另一个:
x.gsub!(/a/,"x")
y
# "xbc"
Reassigning one of these variables has no effect on the other, however:
x = "abc"
y
# still has value "xbc"
一个可变对象可以使用freeze方法变成不可变的:
x.freeze
x.gsub!(/b/,"y")
#
error
Ruby内的符号(symbol)通过ID引用变量而不是通过引用。当我们说:x时,与我们说x.id(你在前面看到的)是一样的。在符号内一个冒号相当于一个标识符结果;如果这个标识符不存在,则创建它。在其它用法中,当我们提到而不是使用一个标识符时(经典的”使用与提到”之间的差别),符号也可以被使用;例如,具体方法method_missing,在方法没有被找到时调用,接受一个与未知方法相对应的符号。所有Symbo对象都有一个方法叫id2name,它返回相应于标识符名称的字符串。这儿是例子:
Hearts
= :Hearts
# This is one way of assigning
Clubs
= :Clubs
#
unique values to constants,
Diamonds
=

iamonds #
somewhat like an enumeration
Spades
= :Spades
#
in Pascal or C.


print Hearts.id2name
# Prints "Hearts"


3、模块和混插

来自类祖先的许多内建方法都是有利用的。要特别注意的是Kernel内的方法被混插到了Object超类中;因为Object是普遍存在的,所以从Kernel中添加给它的方法当然也是普遍存在的。这些方法形成Ruby非常重要的部分。
术语模块和混插(mixin)几乎是同义词。模块是方法和常量的集合,用于扩展Ruby程序。它也可被简单地用于名字空间的管理,但是模块大多数的普通用法是它对类的混合插入特征(使用include)。在这种情况下,它被用做一个混插(mixin,这个术语借用于Python,有时候被写成mix-in,但我写成一个单独的单词)。
为名字空间管理而使用模块的一个例子是经常使用的Math模块。例如,要使用pi的定义,必须包括Math模块;你要以像简单地使用常量那样使用Math:

I。
混插(mixin)提供了获取多重继承优点而不处理所有难点的一种途径。它可以被认为是多重继承的一个有限制的形式,但是语言的创始者Matz称它为"带实现共享的单继承"。
注意,include附加(模块的)名字空间特征给当前作用域。extend附加模块的方法给一个对象。在include内,模块的方法变成可用的实例方法;在extend内,它们变成可用的类方法。
我们应该提一下,load和require不是真正与模块有联系,而是指非模块的Ruby和二进制源(静态的或动态地被加载)。Load操作本质上是读一个文件并将它插入到源文件的当前位置,以便它的定义在那个位置是可用的。Require操作与load类似,但是如果它已经加载了一个文件,它就不会再次加载它。


4、创建类

Ruby有众多的内建类,Ruby程序内也可以定义额外的类。要定义一个新类,使用下面结构:
class ClassName

# ...
end
类的名字本身是个全局常量,所以它必须以大写字母开头。类定义可以包含类常量,类变量,类方法,实例变量和实例方法。类数据对类的所有对象都是有效的,反之,实例数据只对一个对象有效。这儿是个例子:
class Friend

@@myname = "Fred"
#类变量

def initialize(name, sex, phone)

@name, @sex, @phone = name, sex, phone
# 实例变量

end

def hello
# 实例方法

print "Hi, I'm #{ @name} .n"

end

def Friend.our_common_friend
# 类方法

print "We are all friends of #{ @@myname} .n"

end
end
f1 = Friend.new("Susan","F","555-0123")
f2 = Friend.new("Tom","M","555-4567")
f1.hello
# Hi, I'm Susan.
f2.hello
# Hi, I'm Tom.
Friend.our_common_friend
# We are all friends of Fred.
因为类级别数据在整个类中是有效的,它可以在类定义的时候被初始化。如果initialize方法被定义,则它保证在一个实例被分配内存之后被正确运行。Initialize方法类似于传递上的构造器概念,但是它不必须处理内存分配。分配的工作在内部由new完成,回收分配的工作明显由垃圾回收器完成。
现在,考虑这个片断,注意getmyvar, setmyvar, 和 myvar=
方法:
class MyClass

NAME = "Class Name"
# 类常量

def initialize
# 当对象被分配时调用。

@@count += 1


@myvar = 10

end

def MyClass.getcount
# 类方法

@@count


# 类变量

end

def getcount
# 返回类变量的实例

@@count
# 类变量

end

def getmyvar
# 实例方法

@myvar
# 实例变量

end

def setmyvar(val)
# 设置@myvar的实例方法。

@myvar = val

end

def myvar=(val)
# 另一种设置@myvar的方式。

@myvar = val

end
end
foo = MyClass.new
# @myvar is 10
foo.setmyvar 20
# @myvar is 20
foo.myvar = 30
# @myvar is 30
这儿,你看到getmyvar返回@myvar的值,并且用setmyvar设置它。(在很多语言的术语中,这些被分别做为getter和setter的引用。) 这儿工作的很好,但它们没有例证出Ruby的做事方式。方法myvar=看起来像被重载的赋值(尽管严格上讲,它不是);setmyvar可以很好地用于替换,但也不最好的方式。
The class 类Module包含了称为attr, attr_accessor, attr_reader, 和attr_writer的方法。它们可以被用于(使用符号(symbol)做为参数)自动地处理对实例变量的访问控制。例如,先前在类定义内命名的三个方法可以由一行来代替。
attr_accessor :myvar
这将创建方法myvar,它返回@myvar的值,和方法myvar=,它能够设置同名的变量。方法attr_reader和attr_writer分别地创建一个属性的只读和只写版本。有关更多细节,可看Ruby参考手册。
在类的实例方法内,伪变量self可按需要使用。它只是对当前接收者(实例方法被调用的对象)的引用。
修饰方法private, protected, 和public可以被用于控制类内方法的可见性。(实例变量总是private 的,并且它不可以从类的外部被访问,除了使用存取器手段。) 每个修饰方法每个修饰方法都接受一个符号(symbol)如:foo做为一个参数,如果省略参数,则修饰方法被应用于类内所的后续定义:
class MyClass

def method1

# ...

end

def method2

# ...

end

def method3

# ...

end

private :method1

public
:method2

protected :method3

private

def my_method

# ...

end

def another_method

# ...

end
end
在这个例子中,method1将是private的, method2将是public的,method3将是 protected的。因为private方法没有参数,所以my_method和another_method两个将是private的。
public访问级别是self-explanatory;在访问和可见性上没约束。private 级别意味着方法只可以由类内或它的子类访问,并且只能由self(明显的或暗中的)做为接收者以函数形式调用。protected级别意味着只可以从它的类内调用,但是不像private方法,它可以由不是self的接收者调用,如同一个类的另一个实例。
一个类内的方法定义缺省可见性是pubic。除了实例初始化方法initialize,它的private的因为它从new方法中被调用。顶层的方法定义缺省也是public的;如果它们是private,它们则只能以函数形式被调用(例如,Object内定义的方法)。
Ruby的类本身也是对象,是元类Class的实例。Ruby类总是具体的;它没有抽象类。可是,如果你真需要话,理论上Ruby内是可以实现抽象类的。
类Object是层次的根。它提供内建Kernel模块的所有方法。
要创建从另一个类继承的类,它可这样定义:
class MyClass < OtherClass

# ...
end
除了使用内建方法之外,你自然可以定义你自己的方法,也要以引用和覆写现有的方法。当你以现有方法的名字定义一个方法时,先前的方法被覆写。如果方法需要调用它已覆写了的"双亲"的方法(经常发生的),这时可以使用关键字super。
操作符重载严格上不是一个OOP特征,但对于C++等程序员则很熟悉它。因为Ruby内的大多数操作符只是一个简单的方法,所以这些操作符可以被重载或由用户定义的类定义则是很正常的事。为一个现有类覆写操作可能很少见,但对一个新类定义操作符则是很平常的。
为方法创建别名同义词是可行的。使用下面语法(用于类内定义):
alias newname oldname
参数的数量与原有名字是一样的,并且以相的方式调用。


5、方法和属性

前一节,方法被用于简单的类实例和变量中,是通过用句点来分离接收者和方法 (receiver.method)。这种情况下方法的名字就像个句法,句点被省略了。方法可以接受参数:
Time.mktime( 2000, "Aug", 24, 16, 0 )
因为方法调用返回对象,方法调用典型地可被链接或堆叠:
3.succ.to_s

/(x.z).*?(x.z).*?/.match("x1z_1a3_x2z_1b3_").to_a[1..3]

3+2.succ
注意累积的表达式若不是那个特定方法支持的类型,会出现问题。特别地,一些方法返回nil而不确定的条件,这会让任何接受那个结果方法失败。
某些方法可以被传递块。所有的迭代器都可以这样,而不管是内建的还是用户自定义的。被传递的块通常是do-end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: