• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:singletonメソッドへのアクセス)

インスタンスメソッドからsingletonメソッドへのアクセスの方法

このQ&Aのポイント
  • インスタンスメソッドからsingletonメソッドへのアクセスは、self.class.get_hello_wordのように記述する必要があります。
  • インスタンスメソッドからself.get_hello_wordと直接アクセスする方法はありません。
  • 代替として、class << MyClassの構文を使用してsingletonメソッドを定義することもできます。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

> ちなみに、javaや.NETではstatic宣言すれば、同クラス内からだと、メソッド名にプレフィクスを付けなくても使用できるので、それに対してrubyはちょっと不便に感じたことが質問の動機です。 そうですね。Javaや.NETでは確かにこのような小さなユーティリティ関数の類をよくクラスメソッドにしますし、文法上そのように扱えるので便利です。ただ、改めて考えてみると、こうした関数はRubyでは普通はprivateなインスタンスメソッドにしてしまう気がします。 なぜならば、 > もしvalid_password?メソッドがインスタンスメソッドだと、@saltや@passwordに何らかの変更を加えた場合、 valid_password?メソッドが万が一これらを参照している可能性があるので クラスメソッドであっても、クラス変数やクラスのインスタンス変数を参照しているかも知れません。その場合はもっと酷い副作用/非純粋性がありえます。結局@saltや@passwordなどの他の状態に依存していないかどうかは他の部分、例えば規約やドキュメントや、設計上の常識、あるいはソースを見る、などによって担保しなければならないのであって、文法上は保証できません。Javaや.NETでもその気になれば期待に反するコードはいくらでも書けることでしょう。特にRubyの場合はそのDynamismからクラスメソッドにしたところであまり保証度が上がった気がしません。 この手のユーティリティでstaticメンバ関数/staticメソッドを用いるのは、保守性というよりはどちらかというと、多態性を必要としない場合のC++におけるパフォーマンスチューニングの流儀が継承されてきた設計ではないかと思います。 > 実態が1つしか作られないので実行速度が上がる等な理由もありますが、 Javaや.NETでは静的なinvokeは速度が向上する場合もあろうかと思いますが、Rubyではクラスメソッドもクラスに対するシングルトンメソッドなのであって、実行時に動的に解決されますし多態に振る舞いもします。 どちらかというと、特異クラスに対するメソッド呼び出しがメソッドキャッシュに載っていない分パフォーマンスが低下するケースもあるかと思います。ケースバイケースで、プラスになる場合もありマイナスになる場合もあり、少なくとも一般的にパフォーマンスが向上するとは期待できません。 ついでに言うと、 class MyClass (snip)  private  def self.valid_password?(passwd)   (/\A\w{4,12}\Z/ =~ passwd) != nil ? true : false  end end 今、valid_password?はMyClassのpublicなクラスメソッドです。なぜならば、privateはそれ以下の、そのクラスのメソッドをprivateにするものだからです。クラスメソッドはそのクラスの特異クラスのインスタンスメソッドであって、そのクラスのメソッドではないです。従って、privateはdef self.valid_password?には影響を与えていません。 無理に多言語のパフォーマンス特性やオブジェクトモデルを流用した設計にせずともよいのではないでしょうか。この場合素直にMyClassのprivate instance methodにしてはどうでしょう。私ならそうしますし、保守の点はunit testとrdocでカバーします。 それにしても、確かにC++ならvalid_password?はstaticにしていたところです。面白い例をありがとうございました。

ggaogg
質問者

お礼

> こうした関数はRubyでは普通はprivateなインスタンスメソッドにしてしまう気がします。 できればインスタンス内との影響が全く無くて、入力と出力で全てが完結していることが一目瞭然なメソッド定義のしかたがあればと思ったのですが、慣習がそうなら仕方がないですね。 ググってみると、private_class_methodというのがありましたので、privateなクラスメソッドをクラス定義から直に宣言することは可能なようです。 が、やはりMyClassの特異クラスのメソッドとなるので、MyClassのインスタンスメソッドからアクセスすることはできないことに変わりはありませんが・・。 話変わって、慣習ではどうであろうと、やはりなんだかもやもやしていましたので、試行錯誤してみました。これならばvalid_password?メソッドはPasswordHolderのメンバにアクセスしないような気がし(実際には、Enumerableモジュールがincludeされるクラスにeachメソッドが定義されている事を期待するように、あくまでも"気がする”程度ですが・・)、さらにprivateも実現しました。 こんなのは専門家なyuguiさんの目にはどう写るでしょうか。 moduleを実際使ったのは初めてなのですが、普通の使い方なのか、よい使い方なのか、邪道な使い方なのか・・ module PasswordHolderSupport  private  def valid_password?(passwd)   (/\A\w{4,12}\Z/ =~ passwd) != nil ? true : false  end end class PasswordHolder  include PasswordHolderSupport  def initialize(passwd)   if valid_password?(passwd) == false    raise ArgumentError, "使用できないパスワード:#{passwd}"   end   @salt = [rand(9), rand(9)].join   @password = passwd.crypt @salt  end  def authorized?(passwd)   @password == passwd.crypt(@salt)  end end

その他の回答 (1)

回答No.1

 def self.get_hello_word は、定義時点におけるselfに対してシングルトンメソッドget_hello_wordを定義します。今def式はMyClassのclass式の中にあるので、def式が実行された時点でのselfはMyClassです。 一方hello実行時のselfはMyClassのインスタンスであってMyClassではありません。よって定義時のselfであったMyClassにはget_hello_wordがあるが、実行時のselfにはget_hello_wordはないので、self.get_hello_wordはエラーを発生します。 > self.class.get_hello_wordの記述方法よりも簡単な(そうすべき)書き方があれば教えて下さい。 意図することがクラスメソッドであるならば、恐らくこれがそのまま望ましい書き方です。 概念としてのクラスメソッドはRubyの場合クラスに対するシングルトンメソッドとして実現されるわけです。設計上におけるクラスメソッドを実現する分にはシングルトンメソッドや特異クラスについて理解する必要はないので、ご自分で見いだした上のような書き方を「クラスメソッドの決まり事」として利用すると良いでしょう。 クラスメソッドの場合以外のより一般的なシングルトンメソッドを扱うならばシングルトンメソッドとは何かという事を理解して使うべきです。シングルトンメソッドは個別のインスタンスに属するものなので、上に述べたようにそれが定義されたインスタンスは何であるかを考えないといけないわけです。 ただ、もしかしてこの場合シングルトンメソッドは必要ないのではないでしょうか。もう少し具体的な意図をお聞かせいただければ何か付け加えるべきことを言えるかも知れません。

ggaogg
質問者

補足

メソッド定義時と実行時でselfが別物とは驚きました。 しかし、selfはそのオブジェクト自身を指すと考えるとうまく噛み合いますね。 クラスの定義時には、インスタンス化されていないクラス自身の中でメソッド等の解析を実行するのでクラス自身のオブジェクトをselfとして参照し、定義されたクラスがいったんインスタンス化された後はインスタンス化されたメソッドの中での実行なので、selfはインスタンス自身を指す。 こんな感じでしょうか。 それと、シングルトンメソッドが必要になる具体的な例を考えてみました。(妙に時間使ってしまいました・・) class PasswordHolder  def initialize(passwd)   if self.class.valid_password?(passwd) == false    raise ArgumentError, "使用できないパスワード:#{passwd}"   end   @salt = [rand(9), rand(9)].join   @password = passwd.crypt @salt  end  def authorized?(passwd)   @password == passwd.crypt(@salt)  end  private  def self.valid_password?(passwd)   (/\A\w{4,12}\Z/ =~ passwd) != nil ? true : false  end end begin  password_holder = PasswordHolder.new 'hagiwara'  puts password_holder.authorized?('muroi') # -> false  puts password_holder.authorized?('hagiwara') # -> true rescue ArgumentError => e  puts e.message end begin  password_holder = PasswordHolder.new 'kentarou!' # 最後の文字が使用不可  puts password_holder.authorized?('miidori') # 到達しないコード rescue ArgumentError => e  puts e.message # -> 使用できないパスワード:kentarou! end もしvalid_password?メソッドがインスタンスメソッドだと、@saltや@passwordに何らかの変更を加えた場合、valid_password?メソッドが万が一これらを参照している可能性があるので、その影響を考えなくてはなりません(つまり毎回メソッドの中身を見て影響が無いことを確認しなければならない)。その点、シングルトンメソッドはインスタンス変数を一切見ることができないので、self.valid_password?と定義してあった場合は影響が及ぶことが無いことが一目瞭然で、保守性が上がります。 実態が1つしか作られないので実行速度が上がる等な理由もありますが、やはり保守性が一番の理由ですね。 ちなみに、javaや.NETではstatic宣言すれば、同クラス内からだと、メソッド名にプレフィクスを付けなくても使用できるので、それに対してrubyはちょっと不便に感じたことが質問の動機です。

関連するQ&A

専門家に質問してみよう