
インスタンスメソッドからsingletonメソッドへのアクセスが、思ったようにできません。
はじめは、次のように記述すれば動作すると思っていました。
(MyClass.get_hello_wordをインスタンスメソッドから呼び出し)
class MyClass
def hello
puts self.get_hello_word # エラー発生源
end
def self.get_hello_word
'hello every one'
end
end
MyClass.new.hello # エラー
しかし、実際には次のように記述しなければ動きませんでした。
class MyClass
def hello
puts self.class.get_hello_word # self.classのワンクッション
end
def self.get_hello_word
'hello every one'
end
end
MyClass.new.hello
そもそも、
def self.get_hello_word
と定義したんだから、参照する時も同様にできてもいいのではないかと思うんですけれど、何か認識が間違っているのでしょうか。
だとしたら、(勘で言いますと)
class << MyClass
def get_hello_word
'hello every one'
end
end
の文法の考え方がからんでいるような気がするのですが・・
ちんぷんかんぷんです。
どうかこの辺りの知識を教えて頂けないでしょうか。
あと、self.class.get_hello_wordの記述方法よりも簡単な(そうすべき)書き方があれば教えて下さい。
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にしていたところです。面白い例をありがとうございました。
> こうした関数は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
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の場合クラスに対するシングルトンメソッドとして実現されるわけです。設計上におけるクラスメソッドを実現する分にはシングルトンメソッドや特異クラスについて理解する必要はないので、ご自分で見いだした上のような書き方を「クラスメソッドの決まり事」として利用すると良いでしょう。
クラスメソッドの場合以外のより一般的なシングルトンメソッドを扱うならばシングルトンメソッドとは何かという事を理解して使うべきです。シングルトンメソッドは個別のインスタンスに属するものなので、上に述べたようにそれが定義されたインスタンスは何であるかを考えないといけないわけです。
ただ、もしかしてこの場合シングルトンメソッドは必要ないのではないでしょうか。もう少し具体的な意図をお聞かせいただければ何か付け加えるべきことを言えるかも知れません。
この回答への補足
メソッド定義時と実行時で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が見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
CSSのhtmlへの紐付けについ...
-
100万件越えCSVから条件を満た...
-
一週間用のカレンダー
-
Ruby require ライブラリー
-
ruby OpenURI::Meta
-
ruby while式
-
ruby loopメソッド 変数(再喝)
-
ruby 配列
-
ruby loopメソッド 変数
-
ruby クラス・オブジェクト・イ...
-
ルビー言語 ライブラリー 追記
-
ruby raise句
-
ruby begin句
-
ruby ensure句
-
ルビー言語 ライブラリー(再々...
-
ルビー言語 csvファイル 続き(...
-
ルビー言語 csvファイル 続き
-
ルビー言語 ライブラリー
-
ルビー言語 csvファイル part2
-
ルビー言語 ライブラリー
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
教えてください。vb5.0
-
Csvファイルの最終行を取得する...
-
実行時エラー450:引数の数が一...
-
get() と find() の違いについて
-
Pythonで複数のメソッドをまと...
-
エラー「メソッドまたはデータ...
-
A1の値をファイル名に指定した...
-
エクセルVBA オートフィルタで...
-
VBAで型が一致しないエラー(バ...
-
エラー・・
-
メソッド定義の構文のdefって
-
VBからAccessのデータベースを...
-
singletonメソッドへのアクセス
-
Rails4でJSONのParseErrorを判定
-
ハッシュのハッシュのソート
-
「arg」は何の略?
-
What class are you in? には何...
-
class roomとclassroom どちら...
-
pythonのerrorコード
-
HSTLやSSTL等のI/Oピン
おすすめ情報