程式扎記: [ Ruby Gossip ] Advance : 類別 - BasicObject

標籤

2014年11月25日 星期二

[ Ruby Gossip ] Advance : 類別 - BasicObject

Source From Here
Preface
先前一直談到,Object 是 Ruby 中所有物件的父類別,如果定義類別時沒有指定父類別,那預設就是繼承 Object,然而從 Ruby 1.9 之後,Object 更上層還有一個 BasicObject類別:
>> class Some; end
=> nil
>> Some.superclass
=> Object
>> Some.superclass.superclass
=> BasicObject

BasicObject
Object 實際上繼承了 BasicObject,並含括了 Kernel 模組,可以使用 ancestors 方法得知類別的父類別與含括的模組。例如:
>> Object.ancestors
=> [Object, Kernel, BasicObject]

BasicObject 上定義了少量的實例方法:
>> BasicObject.instance_methods
=> [:instance_exec, :__send__, :instance_eval, :==, :equal?, :object_id, :__id__, :!, :!=]

instance_methods 可以取得 public、protected 的實例方法清單,如果傳入 false,則可取得該類別階層定義的實例方法。以下可以看出,Object 繼承 BasicObject 後,實際上並沒有定義實例方法:
>> Object.instance_methods(false)
=> []

Object 含括了 Kernel 模組,每個實例預設的實例方法,實際上都是 BasicObject 與 Kernel 模組提供:


BasicObject 幾乎什麼功能都沒有,這代表了你要教它什麼都可以,具體而言,你可以繼承 BasicObject,定義出自己的繼承體系,而不用擔心太多方法名稱衝突問題。例如,你也許會想要有以下的程式碼產生 XML:
  1. xml = Xml.new  
  2. xml.person do  
  3.     xml.name("caterpillar")  
  4.     xml.phone("0970933933")  
  5.     xml.mail("caterpillar@openhome.cc")  
  6.     xml.address do  
  7.         xml.office("台北市....")  
  8.         xml.home("高雄市....")  
  9.     end  
  10. end  
  11. puts xml.list  
產生出以下的XML:
  1.   
  2.   caterpillar  
  3.   0970933933  
  •   caterpillar@openhome.cc  
  •   
      
  •     台北市....  
  •     高雄市....  
  •   
  •   
  •   那麼你可以定義以下的類別:
    - Xml.rb
    1. class Xml < BasicObject  
    2.     attr_reader :list  
    3.     def initialize  
    4.         @list = ""  
    5.         @level = 0  
    6.     end  
    7.   
    8.     def indent  
    9.         @list << " " * @level  
    10.     end  
    11.       
    12.     def method_missing(mth, *args, &block)  
    13.         indent  
    14.         @list << "<#{mth}>"  
    15.         @list << "#{args[0]}" if args[0]  
    16.         @list << "\n" if block  
    17.         @level += 2  
    18.           
    19.         yield if block  
    20.           
    21.         @level -= 2  
    22.         @list << " " * @level if block  
    23.         @list << "</#{mth}>\n"   
    24.     end  
    25. end  
    method_missing 是 BasicObject 定義的 private 方法,如果有個訊息傳送給物件,而在物件的方法查找中都沒有方法可以回應該訊息,就會呼叫 method_missing 方法,傳入方 法、參數與程式區塊。為了突顯以上 Xml 繼承 BasicObject 的好處,可以故意使用與 Kernel 上定義的實例方法相同的名稱:
    1. xml = Xml.new  
    2. xml.hash do  
    3.     xml.public_send("orz")  
    4.     xml.freeze("XD")  
    5.     xml.inspect("zzz")  
    6. end  
    7. puts xml.list  
    由於繼承自 BasicObject,不用擔心會與 Kernel 發生方法上的名稱衝突問題,因而會產生以下的XML:
    1.   
    2.   orz  
    3.   XD  
    4.   zzz  
      

    沒有留言:

    張貼留言

    網誌存檔

    關於我自己

    我的相片
    Where there is a will, there is a way!