[#44431] コンストラクタの引数について&インスタンス変数の持ち方について — Akira Hayakawa <ruby@...>

度々オブジェクト指向についての質問ですいません。

17 messages 2008/01/04
[#44433] Re: コンストラクタの引数について&インスタンス変数の持ち方について — Yukihiro Matsumoto <matz@...> 2008/01/04

まつもと ゆきひろです

[#44469] pdf 作成 ライブラリ — "Luiz Aoki" <luizruby@...>

はじめまして、青木ルイスと申します、

14 messages 2008/01/12
[#44479] ファイル出力時のprintについて — m-hatake@... 2008/01/16

畠山と申します。

[#44483] format %g の丸めについて — 山崎雄介 <y-yamasaki@...>

山崎(ゆ)です。

14 messages 2008/01/16

[ruby-list:44529] Re: Module内Methodの名前空間

From: rubikitch@...
Date: 2008-01-23 05:01:29 UTC
List: ruby-list #44529
From: "Shingo Hagiwara" <rindou@gmail.com>
Subject: [ruby-list:44528] Module内Methodの名前空間
Date: Wed, 23 Jan 2008 11:38:18 +0900

るびきちです。

> 本題ですが、「名前空間を分けてModuleのMethodを使いたいな」と思い、includeせずに他のクラス内でModuleのMethodを使おうとしました。それで以下のようなコードを書きました。

ClassAでincludeしたくないという意図でいいですか?
クラス内ならばトップレベルの名前空間を汚染しないので、includeしちゃっていいと思いますが。

> Code(1):
> module ModuleA
>   def methodMA()
>     p "methodMA"
>   end
> end
> 
> class ClassA
>   def methodCA02()
>     ModuleA::methodMA()

この呼び出し方はモジュールのクラスメソッドを呼んでいるので、それが存在しないため
NoMethodErrorとなります。

おそらく、この目的からするとモジュール関数にするのがいいと思います。
モジュール関数とは、モジュールのインスタンスメソッドであると同時にクラスメソッドでもあります。

module ModuleA
  def methodMA
    "methodMA"
  end
  module_function :methodMA
end

class ClassA
  def methodCA02
    ModuleA::methodMA
  end
end

class ClassB
  include ModuleA
  def methodCB02
    methodMA
  end
end

ClassA.new.methodCA02           # => "methodMA"
ClassB.new.methodCB02           # => "methodMA"



> のように無事に動きました。次に、「ModuleA::methodMA()でmethodMB()(methodMB()はModuleAのPrivate扱いにしたい)を使いたい」と思い、

private_class_methodでクラスメソッドをprivateにできます。
なお、クラスメソッドを定義するときはself.methodnameとするのがおすすめです。
モジュール名・クラス名を変更してもメソッド定義部分は書き換え不要なので。

module ModuleA
  def self.methodMA
    methodMB()
  end

  def self.methodMB
    "methodMB"
  end
  private_class_method :methodMB   # ここがミソ
end

class ClassA
  def methodCA02
    ModuleA::methodMA
  end
end

ClassA.new.methodCA02      # => "methodMB"
ModuleA.methodMB rescue $! # => #<NoMethodError: private method `methodMB' called for ModuleA:Module>


> と怒られました。同じModule内でのMethod呼び出しなのにと、個人的には分からなくなりました。

「モジュールのインスタンスメソッド」と「モジュールのクラスメソッド(特異メソッド)」の違いです。

> そこで、「Moduleの中身は常にTopから辿らないと駄目なんだ」と思って、

これはモジュールのメソッドの話ではなくて、定数探索の話です。

> Code(4):
> module ModuleA
>   module ModuleAA
>     CONS=1
>   end
>   def ModuleA::methodMA()
>     p ModuleAA::CONS  #ModuleAから辿ってない
>   end
> end

ここのModuleAA::CONSは現在のモジュールModuleAから辿るのでModuleA::ModuleA::CONSが参照されます。
トップレベルからではありません。
ちなみにモジュール(クラス)中から強制的にトップレベルの定数を参照するには
「::TOPLEVEL_CONSTANT」とします。

module Foo
  module Object; end
  Object                        # => Foo::Object
  ::Object                      # => Object
end



> class ClassA
>   def methodCA01()
>     p ModuleA::ModuleAA::CONS
>   end

ClassAにModuleAという定数が定義されていないのでトップレベルのModuleAが参照されます。

リファレンスマニュアルより抜粋

<quote>
=== 定数参照の優先順位

親クラスとネストの外側のクラスで同名の定数が定義されているとネストの外側の定数の方を
先に参照します。つまり、定数参照時の定数の探索順序は、最初にネスト関係を外側に向かっ
て探索し、次に継承関係を上位に向かって探索します。

例:

class Foo
  CONST = 'Foo'
end

class Bar
  CONST = 'Bar'
  class Baz < Foo
    p CONST             # => "Bar"      外側の定数
    # この場合、親クラスの定数は明示的に指定しなければ見えない
    p Foo::CONST        # => "Foo"
  end
end

トップレベルの定数定義はネストの外側とはみなされません。したがってトップレベルの定数
は、継承関係を探索した結果で参照されるので優先順位は低いと言えます。

例:

class Foo
  CONST = 'Foo'
end

CONST = 'Object'

class Bar < Foo
  p CONST               # => "Foo"
end

# 以下のように明示的にネストしていれば規則通り Object の定数
# (ネストの外側)が先に探索される
class Object
  class Bar < Foo
    p CONST             # => "Object"
  end
end

上位のクラス(クラスの継承関係上、およびネストの関係上の上位クラス)の定数と同名の定数
(下の例で CONST) に代入を行うと、上位の定数への代入ではなく、そのクラスの定数の定義に
なります。

例:

class Foo
  CONST = 'Foo'
end
class Bar < Foo
  p CONST               # => "Foo"
  CONST = 'Bar'         # Bar の定数 CONST を*定義*
  p CONST               # => "Bar"  (Foo::CONST は隠蔽される)
  p Foo::CONST          # => "Foo"  (:: 演算子で明示すれば見える)
end
</quote>


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

In This Thread