淺談物件、訊息與方法


在Ruby中,都是透過物件(Object)來處理資料,字串是物件,陣列是物件,即使整數也是物件。例如,你可以叫整數作些事情,像是作四次重複的動作:
>> 4.times do
?>     puts "Orz"
>> end
Orz
Orz
Orz
Orz
=> 4
>>

在其它支援物件導向的程式語言中,會說這執行了4這個物件上的times方法(Method),不過Ruby的說法是,點運算子將times訊息(Message)傳送給4這個物件,4這個物件執行了對應的times方法,4稱為訊息times接受者(Receiver)。類似地:
>> "abc".upcase
=> "ABC"
>>

在Ruby中,物件可以理解某些訊息集合,並將之對應至可執行的方法,上例中,點運算子右邊的upcase訊息,會傳送給點運算子左邊的"abc"字串(String)物件,而"abc"字串物件執行了對應的upcase方法,"abc"稱為upcase訊息的接受者。

每個物件都是某個類別(Class)的實例(Instance),可以執行class方法,得知物件是哪個類別的實例:
>> 1.class
=> Fixnum
>> "abc".class
=> String
>>

從上例可以得知,整數1是Fixnum類別的實例,"abc"字串是String類別的實例,關於類別的說明,之後還會詳介。物件會有可執行的方法,可以透過methods方法得知有哪些物件有哪些公開方法可以使用:
>> "abc".methods
=> [:<=>, :==, :===, :eql?, :hash, :casecmp, :+, :*, :%, :[], :[]=, :insert, :le
ngth, :size, ...略, :__s
end__]
>>

舉個例子來說,以下建立一個Object實例,並在其上定義了some方法:
>> obj = Object.new
=> #<Object:0x22155a0>
>> obj.methods
=> [:nil?, :===, :=~, :!~, :eql?, :class, :clone, :dup, :taint, :tainted?, :unta
int, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods
, :singleton_methods, :protected_methods, :private_methods, :public_methods, :in
stance_variables, :instance_variable_get, :instance_variable_set, :instance_vari
able_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :res
pond_to?, :extend, :display, :method, :public_method, :define_singleton_method,
:hash, :__id__, :object_id, :to_enum, :enum_for, :gem, :==, :equal?, :!, :!=, :i
nstance_eval, :instance_exec, :__send__]
>> def obj.some
>>     print "do something"
>> end
=> nil
>> obj.methods
=> [:some, :nil?, :===, :=~, :!~, :eql?, :class, :clone, :dup, :taint, :tainted?
, :untaint, ...略, :__send__]
>> obj.some
do something=> nil
>>

在第二次執行obj.methods時,可以看到obj上新增了some方法。執行obj.some時,some訊息傳送給obj參考的實例obj執行了some方法

每個物件建立時,都會有一些基本方法,在這邊先介紹respond_to?send方法。respond_to?可以用來詢問物件,是否能對指定訊息作出回應(Ruby 慣例中,?結束的方法表示傳回true或false的結果),send方法則可以將指定訊息傳送給物件。例如:
>> obj = Object.new
=> #<Object:0x2471a60>
>> obj.respond_to? "some"
=> false
>> def obj.some
>>     print "do some actions"
>> end
=> nil
>> obj.respond_to? "some"
=> true
>> obj.some
do some actions=> nil
>> obj.send "some"
do some actions=> nil
>>

如果物件可以回應訊息,接收到訊息後,就可以執行對應方法,因此上例中,obj.some與obj.send "some",都是執行obj上的some方法作為回應。

以下是個使用respond_to?與send的實際例子:
  • main.rb
obj = Object.new

def obj.some
print "do some actions"
end

def obj.other
print "do other actions"
end

if obj.respond_to? ARGV[0]
obj.send ARGV[0]
end

這個範例可以從命令列引數指定要執行物件上的哪個方法。例如:
> ruby main.rb other
do other actions
> ruby main.rb some
do some actions
>

如果不使用respond_to?與send方法,這個範例的if部份也許必須改寫得很冗長了:
...
case ARGV[0]
when "some"
    obj.some
when "other"
    obj.other
when ...
   ...
   # 條列所有可能呼叫的方法
end

先前談過,print、puts也是方法,執行print、puts沒有撰寫接收者,並不是這些方法不需要接受者,只是省略沒寫而已,如果省略訊息的接收者,訊息接收者就是self
不同環境下,self會參考至不同物件,這是個值得討論的進階議題,之後會再說明