`
simohayha
  • 浏览: 1399885 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

ruby way之高级OOP特性之一

    博客分类:
  • ruby
阅读更多
1 发送一条消息给一个对象

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

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

class 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


class 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.new("Hansel", 35, 69)
people << Person.new("Gretel", 32, 64)
people << Person.new("Ted", 36, 68)
people << Person.new("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!来调用:

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


2 特殊化一个单独的对象

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

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

看下面的代码:

a = "hello"
b = "goodbye"

def b.upcase      # create single method
  gsub(/(.)(.)/) { $1.upcase + $2 }
end

puts a.upcase   # HELLO
puts b.upcase   # GoOdBye



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

b = "goodbye"

class << b

  def upcase      # create single method
    gsub(/(.)(.)/) { $1.upcase + $2 }
  end

  def upcase!
    gsub!(/(.)(.)/) { $1.upcase + $2 }
  end

end

puts b.upcase  # GoOdBye
puts b         # goodbye
b.upcase!
puts b         # GoOdBye


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

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

class SomeClass

  # Stuff...

  class << self
    # more stuff...
  end

  # ... and so on.

end


在一个类的体内,self 就指这个类自己,在这个类中的实例方法,其实也就是外面类的类方法:

class TheClass
  class << self
    def hello
      puts "hi"
    end
  end
end

# invoke a class method
TheClass.hello            # hi


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

class MyClass

  class << self

    def accessor_string(*names)
      names.each do |name|
        class_eval <<-EOF
          def #{name}
            @#{name}.to_s
          end
        EOF
      end
    end

  end

  def initialize
    @a = [ 1, 2, 3 ]
    @b = Time.now
  end

  accessor_string :a, :b

end


o = MyClass.new
puts o.a           # 123
puts o.b           # Mon Apr 30 23:12:15 CDT 2001


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

module Quantifier

  def any?
    self.each { |x| return true if yield x }
    false
  end

  def all?
    self.each { |x| return false if not yield x }
    true
  end

end

list = [1, 2, 3, 4, 5]

list.extend(Quantifier)

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 创建一个带参数的类

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

  @@home_planet = "Earth"

  def Terran.home_planet
    @@home_planet
  end

  def Terran.home_planet=(x)
    @@home_planet = x
  end

  #...

end


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

(注意,这里是错误的方法)
class IntelligentLife   # Wrong way to do this!

  @@home_planet = nil

  def IntelligentLife.home_planet
    @@home_planet
  end

  def IntelligentLife.home_planet=(x)
    @@home_planet = x
  end

  #...
end

class Terran < IntelligentLife
  @@home_planet = "Earth"
  #...
end

class Martian < IntelligentLife
  @@home_planet = "Mars"
  #...
end


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

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

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

这里有一个稍好一些的方法,使用了class_eval 方法:

class IntelligentLife

  def IntelligentLife.home_planet
    class_eval("@@home_planet")
  end

  def IntelligentLife.home_planet=(x)
    class_eval("@@home_planet = #{x}")
  end

  #...
end

class Terran < IntelligentLife
  @@home_planet = "Earth"
  #...
end

class Martian < IntelligentLife
  @@home_planet = "Mars"
  #...
end


puts Terran.home_planet            # Earth
puts Martian.home_planet           # Mars


这个可以打印出我们想要的结果任何IntelligentLife里定义的实例变量,或者实例方法,都会被Terran 和 Martian所继承。

下面的方法可能是最好的方法,我们没有使用类变量,而是使用类实例变量:

class IntelligentLife
  class << self
    attr_accessor :home_planet
  end

  #...
end

class Terran < IntelligentLife
  self.home_planet = "Earth"
  #...
end

class Martian < IntelligentLife
  self.home_planet = "Mars"
  #...
end


puts Terran.home_planet            # Earth
puts Martian.home_planet           # Mars
 


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

这里其实还可以这样做:

module IntelligentLife
    attr_accessor :home_planet
end

class Terran
  class << self
    include IntelligentLife
  end
 self.home_planet = "Earth"
  #...
end

class Martian
  class << self
    include IntelligentLife
  end
  self.home_planet = "Mars"
  #...
end


puts Terran.home_planet            # Earth
puts Martian.home_planet           # Mars




4 使用Continuations 来实现一个生成器

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

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

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

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

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

class Generator

  def initialize
    do_generation
  end

  def next
    callcc do |here|
      @main_context = here;
      @generator_context.call 
    end
  end

  private

  def do_generation
    callcc do |context|
      @generator_context = context;
      return
    end
    generating_loop
  end
  def generate(value)
    callcc do |context|
      @generator_context = context;
      @main_context.call(value)
    end
  end
end

# Subclass this and define a generating_loop

class 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 class...

fib = FibGenerator.new

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的性能不好,因为它保存了太多的状态和上下文...




0
1
分享到:
评论

相关推荐

    Addison Wesley The Ruby Way 2Nd Edition Oct 2006.pdf(英文版)

    通过以上内容的详细讲解,《The Ruby Way》第二版为读者提供了全面且深入的Ruby编程知识体系,无论是对于初学者还是有经验的开发者而言,都是一本非常有价值的参考书籍。此外,读者还可以从作者提供的网站...

    ruby-event-oop

    继承、封装和多态是OOP的三大特性,它们在Ruby中都有强大的支持。 在"ruby-event-oop"中,我们可能关注以下几点: 1. **EventMachine库**:Ruby社区广泛使用的事件库,它提供了一个异步I/O框架,允许开发者编写...

    prototype_oop_javascript_ruby_prototype_

    然而,Ruby提供了一种称为“方法_missing”的特性,它允许在找不到特定方法时执行自定义代码。这在某种程度上模拟了JavaScript原型链的行为。 Ruby中,类通过继承来扩展功能,可以使用`super`关键字调用父类的方法...

    php中高级OOP技术

    在PHP中,面向对象编程(OOP)是高级开发中不可或缺的一部分。OOP技术使得代码更加模块化、可维护和可扩展。以下是对标题和描述中提到的几个关键知识点的详细解释: 1. **序列化(Serializing)**: 序列化是将对象...

    Delphi 5高级编程丛书之一IDE与OOP编程book1

    Delphi 5高级编程丛书之一IDE与OOP编程压缩分卷之一

    探索C语言中的面向对象编程:模拟OOP特性

    尽管C语言不是面向对象的编程语言,但通过结构体、函数指针和手动内存管理等技术,我们仍然可以在C语言中模拟面向对象编程的一些核心特性。这种方法虽然有一定的局限性,但也提供了足够的灵活性,允许我们在C语言中...

    .NET C# OOP面向对象技术

    面向对象编程(Object-Oriented Programming,简称OOP)是软件开发中的一种核心编程范式,C#语言作为.NET框架的主要编程语言,充分利用了OOP的特性。在本主题".NET C# OOP面向对象技术"中,我们将深入探讨C#中的面向...

    C#继承OOP实例

    C#继承OOP实例源码Class 。 `

    eloquent ruby

    - 元编程是Ruby的核心优势之一。 - 动态创建方法和属性。 - 使用评估(Evaluation)和反射(Reflection)技术。 - 扩展和覆盖内置行为。 6. **测试与调试** - Ruby提供了丰富的测试框架,如RSpec和Test::Unit等。 ...

    PHP OOP入门必读

    "PHP5面向对象初步(第二章).php"可能涉及封装的概念,这是OOP的四大特性之一。封装允许我们将数据和操作这些数据的方法绑定在一起,保护内部数据不受外界干扰。通过访问修饰符(如public、private、protected),...

    Ruby编程Ruby Programming

    这本面向初学者和高级读者的指南旨在全面介绍Ruby编程语言的基础及其高级特性,因此我们将从多个角度来解析这些内容。 ### 一、Ruby编程语言简介 #### 1.1 Ruby语言的历史与发展 Ruby是一种动态、面向对象的脚本...

    Ruby语言入门教程v1.0

    第四章深入探讨了面向对象编程的思想,这是理解Ruby的关键之一。教程解释了对象、封装、继承和多态这些OOP核心概念,并通过实际示例展示了如何在Ruby中应用它们。对于希望掌握OOP原理的学习者来说,这部分内容尤为...

    C++(OOP)教材

    继承是OOP的另一个关键特性,允许新类从已有的类(父类)继承属性和方法,同时还可以添加新的特性或修改已有行为。这样,代码复用性大大提升,软件设计的灵活性也得以增强。父类和子类的关系形成了类的层次结构,...

    ruby 经典教程从新手到专家

    《Ruby经典教程:从新手到专家》是一本深入浅出介绍Ruby编程语言的权威指南,由Peter Cooper编写,why the lucky stiff作序推荐,专为Ruby...Ruby作为一种灵活且强大的语言,其学习之旅将为你的编程生涯开启新的篇章。

    From Java to Ruby

    Ruby是一种灵活、简洁且富有表现力的编程语言,它提倡元编程和代码简洁性,与Java的面向对象特性有所不同。 首先,我们要理解Java和Ruby在面向对象编程(OOP)上的差异。Java的OOP体系严谨,类定义严格,而Ruby则...

    新闻快客(纯OOP编写)

    面向对象编程是现代软件开发中的核心编程范式之一,它以类和对象为基本单元,强调数据封装、继承和多态这三大特性。这款应用的创建旨在帮助学员深入理解和实践OOP思想。 首先,我们要理解什么是面向对象编程。OOP的...

    JAVA OOP 第一章作业

    封装是面向对象的三大特性之一,它隐藏了对象的内部实现细节,只对外提供公共接口进行交互。在Java中,我们使用访问修饰符(public, private, protected)来控制字段和方法的可见性,以实现封装。 3. 继承 继承允许...

    learn_oop_ruby:启动School Ruby OOP教程

    在Ruby编程语言中,面向对象编程(Object-Oriented Programming, OOP)是一种核心特性,它使得代码更加结构化和可重用。"learn_oop_ruby:启动School Ruby OOP教程"是一个针对初学者的资源,旨在帮助他们掌握Ruby中的...

Global site tag (gtag.js) - Google Analytics