ruby way之OOP之二

1 理解allocate

在一些特殊的环境中,你可能需要不调用它的构造器(也就是initialize)来创建一个对象。也就是说你想要创建一个空的对象.例如,假设你有一个对象,它的状态完全的由它的存取方法所确定,因此如果你不是真的想要一个另外的状态的话,调用new方法是没必要的.此时我们能allocate方法.

class Person
attr_accessor :name, :age, :phone

def initialize(n,a,p)
@name, @age, @phone = n, a, p
end
def test
p "aaa"
end
end

p1 = Person.new("John Smith",29,"555-1234")

p2 = Person.allocate

p p1.age # 29
p p2.age # nil
p2.test


2 Modules

在ruby中使用Modules有两个原因.第一个原因是namespace 的管理,当我们在module中存储常量和方法时,我们将会有更少的命名冲突.调用module中的方法和类方法很类似,就是模块名+方法名.因此我们看到File.ctime 和FileTest.exist?, 时,我们从表面无法知道 File和FileTest是模块还是类.

第二个原因更有趣,我们能使用模块进行mixin.关于什么是mixin,就不说了。

一个模块也能有实例方法,当某个类include了它之后,这些实例方法就变成那个类的了。

module MyMod

def meth1
puts "This is method 1"
end

end


class MyClass

include MyMod

# ...
end


x = MyClass.new
x.meth1 # This is method 1


但是如果有一个模块方法时,要怎么办。你可能认为他们被作为类方法来调用。但是ruby没有这种行为。module methods 没有被mixed in。

可是我们如果想要这样使用,我们有一个小技巧.我们可以覆盖一个叫做append_features的方法来实现模块方法的mix.看下面的例子:

module MyMod

def MyMod.append_features(someClass)
def someClass.modmeth
puts "Module (class) method"
end
super # This call is necessary!
end

def meth1
puts "Method 1"
end

end


class MyClass

include MyMod

def MyClass.classmeth
puts "Class method"
end

def meth2
puts "Method 2"
end

end


x = MyClass.new

# Output:
MyClass.classmeth # Class method
x.meth1 # Method 1
MyClass.modmeth # Module (class) method
x.meth2 # Method 2


这是一个非常有价值的例子.首先我们应当理解append_features 不是一个当include发生时就被调用的hook。它实际上做的是include 操作的工作.这就是为什么super方法在这里是必须的了。没有super。模块剩下的(这里是meth1 )将不会被include.

这里还要注意的是append_features里面定义了一个方法,在ruby中方法内部定义的方法只能是singleton method (也就是说要么是类方法,要么是模块方法).如果你尝试着定义一个实例方法,就会抛出一个Nested method error.

有时一个模块可能想要决定一个mixin的发起类。append_features 也可以做这个,因为它的参数就是那个类.

我们其实也可以mix一个实例方法,作为类方法:

module MyMod

def meth3
puts "Module instance method meth3"
puts "can become a class method."
end

end


class MyClass

class << self # Here, self is MyClass
include MyMod
end

end


MyClass.meth3

# Output:
# Module instance method meth3
# can become a class method.


如果我们使用extend,代码就变成这样了:

class MyClass
extend MyMod
end


我们这里谈论的都是方法,那么实例变量呢,它能被mix吗。尽管模块拥有自己的实例数据是可能的,可是经常都不需要这样做.

mix一个模块到一个对象,也是可以操作的。后面我们会介绍.

我们可以mix Comparable模块 和定义<=> 方法。这样的话我们就能使用<, >, <=, 这样的操作.

3 转换一个对象

这张其实前面几张都有了。比如to_s和to_str的区别,比如coerce的使用。因此这边就简要的介绍下了.

看下面的例子

class String

def coerce(n)
if self['.']
[n, Float(self)]
else
[n, Integer(self)]
end
end
end

x = 1 + "23" # 24
y = 23 * "1.23" # 29.29


也就是说调用*或者+的时候会将字符串转换,而数字将不会做任何动作.

4 创建一个只有数据的类(Structs)

可能你想这样做:

class Address

attr_accessor :street, :city, :state

def initialize(street1, city, state)
@street, @city, @state = street, city, state
end

end

books = Address.new("411 Elm St", "Dallas", "TX")


虽然这样也可以做,可是他太丑陋了。在ruby中,我们有Struct 来做这个事.我们能够这样使用Struct:

Address = Struct.new("Address", :street, :city, :state)
books = Address.new("411 Elm St", "Dallas", "TX")


那么new方法的第一个参数的意思是什么呢?当我们调用Struct.new创建一个新的structure template 时,一个新的类就通过Struct 类他自己被创建。这个类的名字就是第一个参数的内容。因此我们还能这样做:

Struct.new("Address", :street, :city, :state)
books = Struct::Address.new("411 Elm St", "Dallas", "TX")


当你创建一个structure 的实例的时候不一定要把所有的变量都写上,当你调用new时,忽略的变量,它会默认为nil。

这里的话,不要创建一个名叫Tms的Struct,因为Struct内置了一个Tms的类。

5 冻结对象

我们如果想要保护一个对象不被改变,这时我们能使用freeze 方法。

str = "This is a test. "
str.freeze

begin
str << " Don't be alarmed." # Attempting to modify
rescue => err
puts "#{err.class} #{err}"
end

arr = [1, 2, 3]
arr.freeze

begin
arr << 4 # Attempting to modify
rescue => err
puts "#{err.class} #{err}"
end

# Output:
# TypeError: can't modify frozen string
# TypeError: can't modify frozen array


但是这里注意的是freeze 操作的是对象的引用,而不是那个变量。请看下面的例子:

str = "counter-"
str.freeze
str += "intuitive" # "counter-intuitive"

arr = [8, 6, 7]
arr.freeze
arr += [5, 3, 0, 9] # [8, 6, 7, 5, 3, 0, 9]


这里可以看到str使用+=之后创建了一个新的对象,这时str指向了新的对象,因此新的对象并没有被冻结,而老的对象依旧被冻结了。

frozen? 方法返回一个对象是否被冻结.
hash = { 1 => 1, 2 => 4, 3 => 9 }
hash.freeze
arr = hash.to_a
puts hash.frozen? # true
puts arr.frozen? # false
hash2 = hash
puts hash2.frozen? # true
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值