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.2bBookPrintableDocumentobjectKernelBasic 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類纔會抱怨說自己根本沒有這個方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章