LoginSignup
6

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-01-26

多分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に合わせつつうまく拡張する方法とか、暇があれば紹介しようかな と。

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6