Ruby系列学习资料(三)

三、 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 

你可能感兴趣的:(Ruby,ruby,accessor,object,class,语言,include)