2007年12月30日

[ruby-list:44425] Re: メソッドのargumentについて

From: Akira Hayakawa <ruby@xxxxx>
Subject: [ruby-list:44424] メソッドのargumentについて
Date: Sun, 30 Dec 2007 14:42:48 +0900

るびきちです。

> 今、オブジェクト指向について悩んでいて、
> メソッドが他の「自作」クラスのメソッドをargumentにとって良いのか?

> と考えています。

Rubyのデータはすべてがオブジェクトです。
だから自作クラスのインスタンスであろうが文字列だろうが整数だろうが、
みんなオブジェクトとして統一的に扱えます!
「クラス」さえもオブジェクトです。
これはRubyの美点のひとつです。

> Rubyは型がありません。
> だから例えば
>
> class My_Class_A
> def initialize(class_b)
> @class_b_instance = class_b
> end
> end
>
> と書いて、class_bのインスタンスを初期化で持つ事にします。
> しかし、ここで
> My_Class_A.new("aaaa")
> として、文字列を入れても、持ててしまいます。
> これはちょっとおかしな事です。

Ruby的にはおかしくもなんともありません。
だって、文字列もオブジェクトですから。

ただ、クラスBのインスタンスを指す変数に「class_b」を使うのはRuby的には不自然です。
なぜならRubyはクラスさえもオブジェクトです。bにしておくのが無難です。

> もし、こういう書き方があるかは知りませんが、
> class My_Class_A
> def initialize(My_Class_B class_b)
> @class_b_instance = class_b
> end
> end
>
> という風にargumentの段階で強要すれば、
> 代入した時に「そりゃダメだよ」と教えてくれます。

できますよ。

class A
def initialize(b)
@b = b
# ここがポイント。is_a?メソッドでクラスがチェックできる。
# クラス「B」もオブジェクトなのでis_a?メソッドの引数として指定する。
raise TypeError, "argument #{b} is not a B object." unless b.is_a? B
end
end

class B
end

a = A.new(B.new) # => #<A:0xb7bbb4f0 @b=#<B:0xb7bbb504>>
# Bオブジェクト以外を引数にしたら例外発生。
a = A.new("string") rescue $! # => #<TypeError: argument string is not a B object.>

> Rubyの場合、
> 1)インスタンスを使ううちに型が合ってない事が発覚する。
> くらいしかエラーが出ないと思います。

上述のようにクラスをチェックすればエラー(例外)を発生できます。
しかし、Ruby的にはクラスチェックはおすすめできません。
JavaやC++等とは違ってRubyのクラスは絶対的な権力を持っているわけではありません。
Rubyの場合は、あくまでも「メソッドに反応するかどうか」がすべてです。
たとえば、以下のメソッドの引数は文字列でも配列でもIOオブジェクトでも受け付けます。
「<<」演算子メソッドに反応するからです。

def append(container, data)
container << data
end
append [1,2], 3 # => [1, 2, 3]
append "abc", "d" # => "abcd"

慣れるまでは違和感があるでしょう。
しかし、それには良さがあるのです。

def write_string(io, string)
io.write string
end

たとえば、IOオブジェクトに文字列を書き込むメソッドを定義したとします。
引数ioはIOオブジェクトかそのサブクラスであるFileオブジェクトしか受け付けないと思うでしょう。
それは違います。1引数メソッドwriteに反応するメソッドならなんでもかまいません。
たとえば文字列をIOオブジェクトのように扱うStringIOクラスのインスタンスでも受け付けます。
StringIOはIOのサブクラスではありませんが、IOクラスのメソッドをすべて持っています。
だから、IOクラスのように振舞えます。
このおかげでテスト時に実際にファイルを用意しなくてもStringIOオブジェクトで代用できます。

このように、クラスをチェックすると上述の利点が失われます。
クラスをチェックするくらいならば、メソッドに反応するかをチェックしましょう。
チェックする場合はメソッドの先頭に以下の行を入れます。
raise TypeError unless io.respond_to? :write


質問の答えは、以下のようになります。

> 1)Rubyでクラスを作る時に、他のクラスのインスタンスをargumentにとる事が許されますか?

できます。

> 2)また、許される場合に、この少し奇妙な仕様は動的言語の仕様ですか?
> の2点でさせてもらおうと思います。

仕様です。静的言語から来た人にとっては動的言語は異文化です。
上で述べたように動的言語には動的言語の良さがありますから、慣れの問題です。
動的言語は静的言語では思いもつかないようなあっと驚く芸当ができるので楽しいですよ^^

--
rubikitch
Blog: http://d.hatena.ne.jp/rubikitch/
Site: http://www.rubyist.net/~rubikitch/

投稿者 xml-rpc : 2007年12月30日 15:21
役に立ちました?:
過去のフィードバック 平均:(0) 総合:(0) 投票回数:(0)
本記事へのTrackback: http://hoop.euqset.org/blog/mt-tb2006.cgi/68198
トラックバック
コメント
コメントする




画像の中に見える文字を入力してください。