rails on ruby,ruby on rails 之对象模型与方法(一)

include的使用
在类定义中,引入模块,使模块中的方法成为类的实例方法

extend的使用
也是在类定义中引入模块,使模块中的方法成为类的类方法

命名空间使用小结
一般来说,在模块定一种定义一个类使得这个类能在自己独立的namespace里。这样你的类就不会因为和其它模块中的类重名而出问题。

module Foo
    class Joy
       def initialize(one, two)
           puts "one: [#{one}] two: [#{two}]"
       end
    end
end

module Bar
    class Joy
      def initialize(something)
         puts "Do #{something} already!"
      end
    end
end  

这样的话,我们就不能这样定义Joy类了:

Joy.new('a', 'b')

因为你的当前namespace中没有Joy这个类,但是,你可以这样定义:

Foo::Joy.new('a', 'b')

或者

Bar::Joy.new('a crossword puzzle')

因为Joy在模块Foo和Bar中都定义了。作为一个模块的编写者,或者一些类似的类,你可能会把它们放到一个模块定义中。但是作为模块的使用者,每次都敲这么多字母可能比较烦人,所以我们可以使用include方法,如上文所示,在类定义中,引入模块,使模块中的方法成为类的实例方法:

include Bar 或者include Foo

祖先链的概念:
在上面的命名空间中,假如我们include的顺序为:

include Foo
include Bar

那么joy会指向Bar,这是为什么呢?那就要说到祖先链的概念。先来看一段伪码:

module Printable
  def print
     #...
  end

  def prepare_cover
     #...
  end
end

module Document 
   def print_to_screen
     prepare_cover
     format_for_screen
     print
   end

   def format_for_screen
      #...
   end
   def print 
      #...
   end
end

class book
   include Document
   include Book
end                   

首先明确一下祖先链的概念,实际上,祖先链也是包括模块的。当一个模块(类)包含一个模块(类)时,ruby会把这个模块加入到该类的祖先链中。在这里,先抛出一个问题,那就是ruby中类和模块到底有什么区别,后面应该好好总结一下,在这里先暂时认为他们的功能效果是一样的。

Created with Raphaël 2.1.2 b Book Printable Document object Kernel Basic Object

这里的祖先链已经比较明确了,Book没有明确的超类,它是继承自Object类, 而Object类又包含了kernel模块并继承自BasicObject。Book类包含Docunment模块,Ruby为Document模块创建一个包含类,并把它加入到Book类的祖先链上,位置正好位于Book类之上。紧接着,Book类又包含Printable模块,按照逻辑顺序,所以Document在祖先链上逻辑顺序就又上涨了一位,就出现如我们图中所示的祖先链的顺序了。

下面,我们来调用一下这条祖先链上的逻辑,所以究竟会调用哪一个模块上的print方法呢?:

b = Book.new
b.print_to_screen

当我们调用b.print_to_screen方法时,对象b成为self,并且开始进行方法的查找。Ruby在Document模块中找到了print_to_screen这个方法,并且这个方法还调用了其他方法,并且这个方法还调用了其他方法,这就包括了print方法。在ruby中。没有明确指定接收者的调用都会作用于self。因此又开始从Book类(self所属的类)开始方法的查找,知道找到名为print的方法。在祖先链最近的一处定义就是Printable#print,这个print方法就被调用了。

动态方法
这里动态方法是指教我们动态地调用和定义方法,消除繁复的代码。其实调用一个方法实际上就是给一个对象发送一个消息。下面是send方法的使用用例:

methods/dynamic_call.rb
class Myclass
   def my_method(my_args)
       my_arg * 2
   end
end

obj = MyClass.new
obj.my_method(3)

当然,我们也可以使用Object#send方法代替点标识符来调用MyClass#my_method方法:
obj.send(:my_method, 3)       

在send方法里面,我们想要调用的方法变成来参数,这样就可以在代码运行的最后一刻决定调用哪个方法,这个技巧被称为动态派发。

method_missiing方法
在ruby中,编译器并不检查方法调用的行为,这意味着你可以调用一个并不存在的方法,例如:

methods/method_missing.rb:

   class Laywer; end
   nick = Lawyer.new
   nick.talk.sample

<NOMethodError>

我们先来看看这个方法是这么查找的。回想一下我们的祖先调用链,处在最顶端的是BasicObject类。首先Ruby回到nick对象的类中查询它的实例方法,如果那里没有的话,Ruby就会沿着祖先链去查找,最终会来到BasicObject类。

找不到talk_sample方法,ruby就会在nick对象上调用一个名为method_missing的方法。method_missing这个方法是BasicObject的一个私有实例方法,而所有的对象都继承自BasicObject类,所以它的所有对象都可用。一般来说,我们是不能直接调用私有方法的,但是可以通过send方法来调用。

nick.send :method_missing, :my_method
<NoMethodError>

我们刚刚做的工作就是就是ruby解释器所做的工作。我们告诉对象,“我试着调用你的一个名为my_method方法,但是你不明白我想干什么。“BasicObject#method_missing方法会抛出一个NoMethodError进行响应,这是它全部的工作。它就像是一个无主信件的集中处,所有无法投递的消息最后都会来到这里。

现在我们来试着覆写(overwrite)一下method_missing的方法:

class Lawyer
   def method_missing(method, *args)
     puts "You called: #{method} (#{args.join(',')})"
     puts "(You also passed it a blocked)" if block_given?
   end
end

bob = Lawyer.new
bob.talk_sample('a','b') do
     puts 'block test' 
end          

覆写method_missing方法可以让你调用实际上并不存在的方法。

静态语言方法和动态语言方法
正如我们所知,代码中的对象总是在不停地交谈,有些语言(如java,和c++)的编译器会控制这些交谈。对每一次方法调用,编译器都会检查接收对象是否有一个匹配的方法。这称之为静态类型检查(static type checking),这称为动态语言(static language)。在动态语言(比如Python 和 Ruby)中,则没有这样一个像警察一样的编译器。因此,我们可以在Laywer对象上调用talk_simple方法,系统不会发出任何警告,直到这个调用真正被执行才会报错。这是Laywer类才会抱怨说自己根本没有这个方法。

猜你喜欢

转载自blog.csdn.net/zyhmz/article/details/80058694