Rubyでインスタンスメソッドとクラスメソッドを同時にモジュール化する方法

More than 3 years have passed since last update.

多分1.9以前の古いRubyでは動かないんじゃないかなあ と思います。

実行環境はRuby2.2.2


書き方

# 今回使い回したいモジュール

module HogeModule
# includeと書いた時点で呼び出される便利メソッド
def self.included klass
klass.include ::HogeModule::SpecificInclusion
klass.extend ::HogeModule::SpecificExtension
end

# インスタンスメソッドとして追加したいものを書く
module SpecificInclusion
def instance_method
puts 'インスタンスメソッドだよ'
end
end

# クラスメソッドとして追加したいものを書く
module SpecificExtension
def class_method
puts 'クラスメソッドだよ'
end
end
end

# 上のモジュールを取り込みたいクラス
class MyAwesomeClass
include HogeModule
end


なんで必要になったか

Rubyは多重継承を認めない実装系なので、

mix-inな書き方をしつつ同時にクラスメソッドとインスタンスメソッドを定義したいときに便利です。

(例えば、カプセル化した機能がクラスメソッドとインスタンスメソッドの両方から成り立つものだったりとか。)

こうすることで別々にinclude, extendしないといけない書き方を回避でき、

片方の呼び出し忘れによる事故を未然に防ぐことができます。

実際にコードを使うタイミングだと、考えるべきことって少ないほうが幸せじゃん?


Before

class MyLameClass

include HogeModule::SpecificInclusion
extend HogeModule::SpecificExtension
end


イケてない点


  • なんか長い

  • せっかくカプセル化するためにモジュールにまとめたのに、ここだけ別れる

  • 片方だけ書き忘れたりしそう


After

class MyAwesomeClass

include HogeModule
end


注意点


  • インスタンスメソッドとクラスメソッドが提供されたことがわからない


使い方

# クラスメソッドの呼び出し

MyAwesomeClass.instance_method

# インスタンスメソッドの呼び出し
MyAwesomeClass.new.class_method


注意点

この実装に限った話ではないですが、

Rubyのincludeとかextendってmix-inなので、基本的に上書き(オーバーライド)されてしまいます。

Railsとか使っていて、安易にidとかtypeってモジュールのメソッド名に使っていると、何気ないincludeが命取りになったりします。


思ったこと

Rubyの継承とかinclude, extend周り、よくできているなあとは思いつつ、

Rubyの他の部分と比べて、なんか覚えること多くねーか ってのが率直な感想です。

やりたいことはシンプルなのにね。

特異◯◯ というのがそもそもなんとなく既に古くなりつつあって、情報がまとまっていない気がしているも、

それをうまく説明してくれるサイトというのもあんま無い気がします。

(C++とかJavaのstaticとかが分かっていれば話は早いんですが。)

Railsに合わせつつうまく拡張する方法とか、暇があれば紹介しようかな と。

他にいい表現とか、リスクとか見つけた人はコメント貰えると喜びます。