therubyway:ruby way的高级OOP特性的一

  1 发送条消息给个对象

  当你个思路方法时你也就是发送了条消息给个对象在ruby中我们能够在运行时决定那个思路方法被send 思路方法就是做这个他接受个symbol为参数.

  举个简单例子假设我们要写个排序我们想要使用区别域作为比较key虽然我们这时可以用block可是如果使用send我们能有个更优美写法:

  Java代码

 Person 
 attr_reader :name, :age, :height 
 
 def initialize(name, age, height) 
  @name, @age, @height = name, age, height 
 end 
 
 def inspect 
  "#@name #@age #@height" 
 end 
end 
 
 
 Array 
 def sort_by(sym)  # Our own version of sort_by 
  self.sort {|x,y| x.send(sym) <=> y.send(sym) } 
 end 
end 
 
 
people =  
people << Person.("Hansel", 35, 69) 
people << Person.("Gretel", 32, 64) 
people << Person.("Ted", 36, 68) 
people << Person.("Alice", 33, 63) 
 
p1 = people.sort_by(:name) 
p2 = people.sort_by(:age) 
p3 = people.sort_by(:height) 
 
p p1  # [Alice 33 63, Gretel 32 64, Hansel 35 69, Ted 36 68] 
p p2  # [Gretel 32 64, Alice 33 63, Hansel 35 69, Ted 36 68] 
p p3  # [Alice 33 63, Gretel 32 64, Ted 36 68, Hansel 35 69]


  __send__ 其实也就是send思路方法别名了不过这边建议用__send__,这是send有可能作为个用户自己定义思路方法

  在1.9中send思路方法不能private思路方法了不过我们能够使用__send!来:

  Java代码

 Foo  
 private  
 def foo  
  "aa"  
 end  
end  
p Foo..__send!(:foo)   # => nil  
p Foo..send(:foo)   #private method `foo' called for #<Foo:0xa89530> (NoMethodError) 


  2 特殊化个单独对象

  在很多oo语言里个类所有对象共享相同行为类作为个模板当构造器制造个拥有相同接口对象

  ruby中我们可以改变个对象运行时状态你可以给对象个私有匿名子类:所有原始思路方法都是可用由于联系到这个对象上行为是私有因此它只发生件只发生事叫做“singleton”比如singleton 思路方法和singleton类.

  看下面代码:

  Java代码  

a = "hello" 
b = "goodbye" 
 
def b.up   # create single method 
 gsub(/(.)(.)/) { $1.up + $2 } 
end 
 
puts a.up  # HELLO 
puts b.up  # GoOdBye


  加个singleton 思路方法到个对象也就是创建了个singleton 然后这个类父类是这个对象如果你加多个思路方法到个对象这是你可以直接实现个singleton 类:

  Java代码

b = "goodbye" 
 
 << b 
 
 def up   # create single method 
  gsub(/(.)(.)/) { $1.up + $2 } 
 end 
 
 def up! 
  gsub!(/(.)(.)/) { $1.up + $2 } 
 end 
 
end 
 
puts b.up # GoOdBye 
puts b     # goodbye 
b.up! 
puts b     # GoOdBye


  这里要注意些更“primitive”对象(比如Fixnum)不能加singleton 思路方法这是这些对象不是存引用在内存中但是在ruby将来版本可能会实现这个

  在些库源码中我们能看到这种代码:

  Java代码  

 SomeClass 
 
 # Stuff... 
 
  << self 
  # more stuff... 
 end 
 
 # ... and so on. 
 
end 


  在个类体内self 就指这个类自己在这个类中例子思路方法其实也就是外面类类思路方法:

  Java代码

 TheClass 
  << self 
  def hello 
   puts "hi" 
  end 
 end 
end 
 
# invoke a  method 
TheClass.hello      # hi


  使用这个技术个原因是可以创建个类级别帮助思路方法然后我们就能在这个类其他地方使用它了.

  Java代码

 MyClass 
 
  << self 
 
  def accessor_(*names) 
   names.each do |name| 
    _eval <<-EOF 
     def #{name} 
      @#{name}.to_s 
     end 
    EOF 
   end 
  end 
 
 end 
 
 def initialize 
  @a = [ 1, 2, 3 ] 
  @b = Time.now 
 end 
 
 accessor_ :a, :b 
 
end 
 
 
o = MyClass. 
puts o.a      # 123 
puts o.b      # Mon Apr 30 23:12:15 CDT 2001


  extend 思路方法能够mix个模块到个对象:

  Java代码

module Quantier 
 
 def any? 
  self.each { |x|  true  yield x } 
  false 
 end 
 
 def all? 
  self.each { |x|  false  not yield x } 
  true 
 end 
 
end 
 
list = [1, 2, 3, 4, 5] 
 
list.extend(Quantier) 
 
flag1 = list.any? {|x| x > 5 }    # false 
flag2 = list.any? {|x| x >= 5 }    # true 
flag3 = list.all? {|x| x <= 10 }   # true 
flag4 = list.all? {|x| x % 2  0 }  # false


  3 创建个带参数

  假设我们想要创建个多样也就是说可以通过控制类变量来控制它多种状态:

  Java代码  

 Terran 
 
 @@home_planet = "Earth" 
 
 def Terran.home_planet 
  @@home_planet 
 end 
 
 def Terran.home_planet=(x) 
  @@home_planet = x 
 end 
 
 #... 
 
end


  这样是可以这时如果我想要定义些和Terran类似你可能会马上想到是可以给这些类抽象出来个超类就行了:

  (注意这里是思路方法)

  Java代码  

 IntelligentLe  # Wrong way to do this! 
 
 @@home_planet = nil 
 
 def IntelligentLe.home_planet 
  @@home_planet 
 end 
 
 def IntelligentLe.home_planet=(x) 
  @@home_planet = x 
 end 
 
 #... 
end 
 
 Terran < IntelligentLe 
 @@home_planet = "Earth" 
 #... 
end 
 
 Martian < IntelligentLe 
 @@home_planet = "Mars" 
 #... 
end


  当你Terran.home_planet时在1.9中会打印出nil在1.8中会打印出Mars.

  为什么会这样?答案是 variables 不是真正 variables 他们不属于类而是属于整个继承体系 variables不能从父类所被复制但是能够从父类所被共享

  我们可以剔除掉类变量在基类中定义可是这时我们定义类思路方法就不能工作了

  这里有个稍好思路方法使用了_eval 思路方法:

  Java代码

 IntelligentLe 
 
 def IntelligentLe.home_planet 
  _eval("@@home_planet") 
 end 
 
 def IntelligentLe.home_planet=(x) 
  _eval("@@home_planet = #{x}") 
 end 
 
 #... 
end 
 
 Terran < IntelligentLe 
 @@home_planet = "Earth" 
 #... 
end 
 
 Martian < IntelligentLe 
 @@home_planet = "Mars" 
 #... 
end 
 
 
puts Terran.home_planet      # Earth 
puts Martian.home_planet      # Mars


  这个可以打印出我们想要结果任何IntelligentLe里定义例子变量或者例子思路方法都会被Terran 和 Martian所继承

  下面思路方法可能是最好思路方法我们没有使用类变量而是使用类例子变量:

  Java代码    

 IntelligentLe 
  << self 
  attr_accessor :home_planet 
 end 
 
 #... 
end 
 
 Terran < IntelligentLe 
 self.home_planet = "Earth" 
 #... 
end 
 
 Martian < IntelligentLe 
 self.home_planet = "Mars" 
 #... 
end 
 
 
puts Terran.home_planet      # Earth 
puts Martian.home_planet      # Mars 

  这里我们打开了个singleton 定义了个存取思路方法home_planet两个子类他们自己accessors 来设置变量.我们其实还可以给home_planet=思路方法设置为 private

  这里其实还可以这样做:

  Java代码 module IntelligentLe 
  attr_accessor :home_planet 
end 
 
 Terran 
  << self 
   IntelligentLe 
 end 
 self.home_planet = "Earth" 
 #... 
end 
 
 Martian 
  << self 
   IntelligentLe 
 end 
 self.home_planet = "Mars" 
 #... 
end 
 
 
puts Terran.home_planet      # Earth 
puts Martian.home_planet      # Mars


  4 使用Continuations 来实现个生成器

  ruby个更抽象特性就是continuation这是种控制非局部跳转和返回思路方法个continuation 对象存储着个返回地址个上下文.他看起来很像c中jmp/longjmp 可是他存储着更多上下文.

  Kernel 思路方法callcc接受个block返回个Continuation 对象这个被返回对象作为个参数被传递进这个block.Continuation唯思路方法就是call它将会引起个非局部返回执行 callcblock直到它尾部

  其实Continuation很像游戏中保存游戏特性最好理解Continuation思路方法就是去看电影Run, Lola, Run (哈哈就是罗拉快跑).

  Continuation最好例子就是生成器现在生成器已经是ruby部分了

  请看下面使用生成器Fibonacci numbers 例子:

  Java代码

 Generator 
 
 def initialize 
  do_generation 
 end 
 
 def next 
  callcc do |here| 
   @_context = here; 
   @generator_context.call 
  end 
 end 
 
 private 
 
 def do_generation 
  callcc do |context| 
   @generator_context = context; 
    
  end 
  generating_loop 
 end 
 def generate(value) 
  callcc do |context| 
   @generator_context = context; 
   @_context.call(value) 
  end 
 end 
end 
 
# Sub this and  a generating_loop 
 
 FibGenerator < Generator 
 def generating_loop 
  generate(1) 
  a, b = 1, 1 
  loop do 
   generate(b) 
   a, b = b, a+b 
  end 
 end 
end 
 
 
# Now instantiate the ... 
 
fib = FibGenerator. 
 
puts fib.next      # 1 
puts fib.next      # 1 
puts fib.next      # 2 
puts fib.next      # 3 
puts fib.next      # 5 
puts fib.next      # 8 
puts fib.next      # 13




  这里要注意continuations性能不好它保存了太多状态和上下文...



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

延伸阅读

最新评论

发表评论