在ruby中没有像c或者ruby中构造器可是我们依然能够创建出类似多个构造器:
Ruby代码
ColoredRectangle
def initialize(r, g, b, s1, s2)
@r, @g, @b, @s1, @s2 = r, g, b, s1, s2
end
def ColoredRectangle.white_rect(s1, s2)
(0xff, 0xff, 0xff, s1, s2)
end
def ColoredRectangle.gray_rect(s1, s2)
(0x88, 0x88, 0x88, s1, s2)
end
def ColoredRectangle.colored_square(r, g, b, s)
(r, g, b, s, s)
end
def ColoredRectangle.red_square(s)
(0xff, 0, 0, s, s)
end
def inspect
"#@r #@g #@b #@s1 #@s2"
end
end
a = ColoredRectangle.(0x88, 0xaa, 0xff, 20, 30)
b = ColoredRectangle.white_rect(15,25)
c = ColoredRectangle.red_square(40)
可是如果属性很多话那不是要写很郁闷于是我们这里还有个更漂亮思路方法:
Ruby代码
PersonalComputer
attr_accessor :manufacturer,
:model, :processor, :clock,
:ram, :disk, :monitor,
:colors, :vres, :hres, :net
def initialize(&block)
instance_eval &block
end
# Other methods...
end
desktop = PersonalComputer. do
self.manufacturer = "Acme"
self.model = "THX-1138"
self.processor = "986"
self.clock = 9.6 # GHz
self.ram = 16 # Gb
self.disk = 20 # Tb
self.monitor = 25 # inches
self.colors = 16777216
self.vres = 1280
self.hres = 1600
self.net = "T3"
end
p desktop
这边主要是使用了个instance_eval思路方法它和eval很像只不过他是在个给定对象上下文中对个串或者个block进行求值.这里还要注意是block中self
是必须.如果你想要将个属性存取思路方法去除掉你可以在block结尾加上undef(后面是你思路方法名):
Ruby代码
desktop = PersonalComputer. do
self.manufacturer = "Acme"
self.model = "THX-1138"
undef model
end
p desktop.model #报错
2 创建个例子属性
这个很简单有好几种思路方法:
Ruby代码
Person
def name
@name
end
def name=(x)
@name = x
end
def age
@age
end
end
Ruby代码
Person
attr :name, true # Create @name, name, name=
attr :age # Create @age, age
end
Ruby代码
SomeClass
attr_reader :a1, :a2 # Creates @a1, a1, @a2, a2
attr_writer :b1, :b2 # Creates @b1, b1=, @b2, b2=
attr_accessor :c1, :c2 # Creates @c1, c1, c1=, @c2, c2, c2=
# ...
end
最常用第 3种.
3创建个类级别属性和思路方法.
个思路方法或者属性不只能关联到个例子而且还能关联到类它自己:
创建个类思路方法:
Ruby代码
SoundPlayer
MAX_SAMPLE = 192
def SoundPlayer.detect_hardware
# ...
end
def play
# ...
end
end
这边还有另外种创建思路方法:
Ruby代码
SoundPlayer
MAX_SAMPLE = 192
def play
# ...
end
end
def SoundPlayer.detect_hardware
# ...
end
两种区别是,当类思路方法被定义在类外面时他常量不在它作用域中也就是说在第 2种思路方法里面要得到常量MAX_SAMPLE必须这样SoundPlayer::MAX_SAMPLE .
类变量话是以@@开头,看下面例子:
Ruby代码
Metal
@@current_temp = 70
attr_accessor :atomic_number
def Metal.current_temp=(x)
@@current_temp = x
end
def Metal.current_temp
@@current_temp
end
def liquid?
@@current_temp >= @melting
end
def initialize(atnum, melt)
@atomic_number = atnum
@melting = melt
end
end
aluminum = Metal.(13, 1236)
copper = Metal.(29, 1982)
gold = Metal.(79, 1948)
Metal.current_temp = 1600
puts aluminum.liquid? # true
puts copper.liquid? # false
puts gold.liquid? # false
Metal.current_temp = 2100
puts aluminum.liquid? # true
puts copper.liquid? # true
puts gold.liquid? # true
这里类变量在类思路方法被的前就被例子化了要注意是我们能够从个例子思路方法里面存取个类变量但是我们不能从个类思路方法存取个例子变量.
Ruby代码
Test
def Test.test
puts @a
end
def tests
puts @a
end
def a=(x)
@a=x
end
end
temp=Test.
temp.a="2"
Test.test #nil
temp.tests #2
当我们想要从个类思路方法中打印出个例子变量会出现什么呢这时就会打印出nil.为什么是这样呢?原因是这时我们打印出已经不是这个类例子变量了而是Class类个例子变量因此会打印出nil.
还有种叫做是类例子变量这边只是简要介绍后面我们会详细介绍:
Ruby代码
MyClass
SOME_CONST = "alpha" # A -level constant
@@var = "beta" # A variable
@var = "gamma" # A instance variable
def initialize
@var = "delta" # An instance variable
end
def mymethod
puts SOME_CONST # (the constant)
puts @@var # (the variable)
puts @var # (the instance variable)
end
def MyClass.meth1
puts SOME_CONST # (the constant)
puts @@var # (the variable)
puts @var # (the instance variable)
end
end
def MyClass.meth2
puts MyClass::SOME_CONST # (the constant)
# puts @@var # error out of scope
puts @var # (the instance variable)
end
myobj = MyClass.
MyClass.meth1 # alpha, beta, gamma
MyClass.meth2 # alpha, gamma
myobj.mymethod # alpha, beta, delta
可见这边是建立了两个变量(虽然名字什么都是样)个是类例子变量个是例子变量当在类思路方法中使用是类例子变量而在例子思路方法中是例子变量.
4 测试个对象所属类
经常我们需要知道这个对象是属于那个类,这里有很多思路方法:
思路方法将会返回个对象类和它同义思路方法type 已经被废弃了:
Ruby代码
s = "Hello"
n = 237
sc = s. # String
nc = n. # Fixnum
不要被思路方法所返回东西所迷惑其实它返回是个Class类例子因此我们能够返回类型类思路方法看起来就好像是Class个例子思路方法:
Ruby代码
s2 = "some "
var = s2. # String
my_str = var.("Hi...") # A
我们能够比较个变量和个类名来看他们是否相等甚至我们能够使用个变量作为个超类从而定义个子类.这里只要记住在ruby中Class是个对象Object是个类.
我们如果想要知道个变量是否属于某个类我们能这样做:
Ruby代码
puts (5.instance_of? Fixnum) # true
puts ("XYZZY".instance_of? Fixnum) # false
puts ("PLUGH".instance_of? String) # true
如果我们想要测试个有继承关系对象时我们可以使用kind_of?或者is_a?思路方法:
Ruby代码
n = 9876543210
flag1 = n.instance_of? Bignum # true
flag2 = n.kind_of? Bignum # true
flag3 = n.is_a? Bignum # true
flag3 = n.is_a? Integer # true
flag4 = n.is_a? Numeric # true
flag5 = n.is_a? Object # true
flag6 = n.is_a? String # false
flag7 = n.is_a? Array # false
这里还有种就是mix当个类mix了个模块时候我们使用is_a?测试时会出现什么呢:
Ruby代码
x = [1, 2, 3]
flag8 = x.kind_of? Enumerable # true
flag9 = x.is_a? Enumerable # true
可以看到由于.gif' /> mix了 Enumerable 因此个对象也就是(is_a?)个Enumerable.
我们还可以使用些其他操作符:
Ruby代码
flag1 = Integer < Numeric # true
flag2 = Integer < Object # true
flag3 = Object Array # false
flag4 = IO >= File # true
flag5 = Float < Integer # nil
这些比较都是他们继承关系.
每个类都有个=思路方法如果是 = instance 当这个例子属于这个类时就会返回true.
这里我们要注意respond_to思路方法它可以测试是否对象含有某个思路方法,它第 2个参数是来决定是否搜索private思路方法:
Ruby代码
# Search public methods
wumpus.respond_to?(:bite)
puts "It's got teeth!"
puts "Go ahead and taunt it."
end
# 会搜索private思路方法
woozle.respond_to?(:bite,true)
puts "Woozles bite!"
puts "Ah, the non-biting woozle."
end
有时我们想要立即得到个类或者对象父类:
Ruby代码
.gif' />_parent = Array.super # Object
fn_parent = 237..super # Integer
obj_parent = Object.super # nil
5 测试对象相等
rubyObject实现了5个区别思路方法来测试对象是否相等,你类可能实现了他们中些让我们来个个看.
equal?思路方法如果接受者和参数有相同object id,那么就返回true这个思路方法是object基本语义因此你不能覆盖它
思路方法它测试它接受者和参数值相等话就返回true.
eql?思路方法它是Object部分(eql?是在Kernel 模块被实现而Object mix了Kernel).就像是思路方法但是eq?思路方法更严格例如区别numeric 对象使用比较时将会转换为个更通用类型而使用eq?就不会进行这种转换:
Ruby代码
flag1 = (1 1.0) # true
flag2 = (1.eql?(1.0)) # false
eq?思路方法存在还有个原因:那被用来比较hash keys值当你使用你对象作为个hash时你如果想要覆盖ruby默认行为你就需要同时覆盖eql?和hash思路方法.
还剩下两个思路方法所有对象都实现了他们,= 被用来在语句中:
Ruby代码
an_object
when String
puts "It's a ."
when Numeric
puts "It's a number."
puts "It's something entirely."
end
这边比较an_object和下面条件就是用=思路方法.
还有个正则表达式匹配思路方法=~它相反思路方法是!~.
6 控制思路方法存取
定义个private思路方法:
Ruby代码
Bank
def open_safe
# ...
end
def close_safe
# ...
end
private :open_safe, :close_safe
def make_withdrawal(amount)
access_allowed
open_safe
get_cash(amount)
close_safe
end
end
# make the rest private
private
def get_cash
# ...
end
def access_allowed
# ...
end
end
Private 思路方法不能被个明确接受者所可是他们能被隐式接受者self所这就意味着你不能在另外对象里面个Private思路方法.private思路方法对子类是可用但是只能在相同对象.
Ruby代码
Bank
private
def get_cash
puts "aaa"
end
end
BankChild <Bank
def test
get_cash
end
end
BankChild..test
protected 有更少限制Protected思路方法只能够被类和他子类例子所存取.请看下面例子age思路方法被设置为protected因此它可以被多个对象可是在Person外面age思路方法是不可用:
Ruby代码
Person
def initialize(name, age)
@name, @age = name, age
end
def <=>(other)
age <=> other.age
end
attr_reader :name, :age
protected :age
end
p1 = Person.("fred", 31)
p2 = Person.("agnes", 43)
compare = (p1 <=> p2) # -1
x = p1.age # Error!
如果把age思路方法变为private话直接<=>思路方法就会出错那是age被多个对象了.而不仅仅是被self.
般在个类或者模块外面定义思路方法默认都是private那是他们都是在Object类中定义他们都是全局变量但是他们不能被个接收者所.
7拷贝个对象
ruby内置Object#clone 和 #dup思路方法能够复制它们接收者.他们区别是clone思路方法能够把singleton method 也复制了:
Ruby代码
s1 = "cat"
def s1.up
"CaT"
end
s1_dup = s1.dup
s1_clone = s1.clone
s1 #=> "cat"
s1_dup.up #=> "CAT" (singleton method not copied)
s1_clone.up #=> "CaT" (uses singleton method)
dup 和clone都只是浅拷贝也就是说如果有嵌套对象话他们将不会复制:
Ruby代码
arr1 = [ 1, "flipper", 3 ]
arr2 = arr1.dup
arr2[2] = 99
arr2[1][2] = 'a'
arr1 # [1, "flapper", 3]
arr2 # [1, "flapper", 99]
你可以选择自己实现个deep copy,或者使用前面讲Marshal 模块.
Ruby代码
arr1 = [ 1, "flipper", 3 ]
arr2 = Marshal.load(Marshal.dump(arr1))
arr2[2] = 99
arr2[1][2] = 'a'
arr1 # [1, "flipper", 3]
arr2 # [1, "flapper", 99]
可以看到arr2改变并没有影响arr1
9 使用initialize_copy
当你使用dup或者clone时构造器被绕过了所有状态信息被保存.
如果你不想绕过构造器呢?看下面代码:
Ruby代码
Document
attr_accessor :title, :text
attr_reader :timestamp
def initialize(title, text)
@title, @text = title, text
@timestamp = Time.now
end
end
doc1 = Document.("Random Stuff",File.read("somefile"))
sleep 300 # Wait awhile...
doc2 = doc1.clone
doc1.timestamp doc2.timestamp # true
#两个timestamp是样!
我们想要得到拷贝发生时时间话这时我们能够使用initialize_copy思路方法:
Ruby代码
Document # 重新打开Document类
def initialize_copy(other)
@timestamp = Time.now
end
end
doc3 = Document.("More Stuff",File.read("somefile"))
sleep 3 # Wait awhile...
doc4 = doc3.clone
p doc3.timestamp doc4.timestamp # 这时变成了false
这里要注意initialize_copy是在状态信息被拷贝的后.
最新评论