therubyway:ruby way的OOP的一

  1 使用多个构造思路方法

  在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是在状态信息被拷贝的后.



Tags:  ruby安装 ruby是什么意思 rubyonrails therubyway

延伸阅读

最新评论

发表评论