

ruby初心者です。  
以下のようなrequire部分以外ほぼ同じクラスが二つあるのですが、  
これをもっとすっきり書く方法はありませんか？よろしくお願いいたします。

[code]

    class Foo 
      require 'aaa' #FooとBarはaaaというファイルを読み込むかbbbを読み込むかの違いのみ
      def hogehoge
         :
      end
         :
    end
    
    class Bar 
      require 'bbb'
      def hogehoge
         :
      end
         :
    end
    
[/code]


CC BY-SA 3.0
2016-11-24T09:30:05.143
30647
2016-11-26T00:07:41.277
19701
question
ruby
Ruby　内部で違うファイルをrequireをしているがそれ以外はほぼ同じクラスが二つある


まず、次のことに注意してください。

  * Rubyの`require`は **インタプリンタへのファイルのロード** です。`require`が書かれているコンテキスト(その場所という意味、トップレベルとか、classの中とか、defの中とか)で展開する訳ではありません。class中であろうがmoduleの中であろうが、それこそdefやブロックの中であっても、常にトップレベルにあるものとして読み込まれるため、 **同じ動作をします** 。classの中に入れたからと言って動作が変わるわけではありません。
  * class定義が評価される時は、classコンテキスト上の文は **全て評価されます** 。実行されないのは条件分岐で除外されている物と、defの中身だけです(defによるメソッドの定義自体は評価されます)。
  * Rubyは既に存在するクラスやメソッドの動作を上書きで変更できるため、`require`によって **その後の動作が変わる場合があります** 。

つまり、`require`の動作はclassの中でも外でも同じです。その文ですと、それぞれのclass定義の直前においても動作は変わりません。

次に重要なのは **bbb** によって`Foo`の動作が変わるかどうかです。もし変わらなければ別に`Foo`クラスの定義の前に、 **bbb**
を読み込んでも問題ありません。今回のコードでは条件分岐等で読み込む読み込まないという分岐もありませんので、すべて最初に`require`しても何も問題ありません。つまり、次のように書けます。

[code]

    require 'aaa'
    require 'bbb'
    
    class Foo
      def hogehoge
         :
      end
         :
    end
    
    class Bar
      def hogehoge
         :
      end
         :
    end
    
[/code]

クラスの定義が終わるまでの時間は同じですので、全体が遅くなると言うわけではありません。

さて、問題は **bbb** が読み込まれてるか読み込まれてないかによって`Foo`の動作が変わる場合です。その場合は、次のような問題があります。

  * `require`は一度読み込んでしまったら二度と読み込みません。もし、別の所で **bbb** が読み込まれてしまった場合は、期待した通りの動作になりません。
  * 本来、ライブラリは既存の動作が変わるように作るべきではありません。(標準ライブラリに[mathn](https://docs.ruby-lang.org/ja/latest/library/mathn.html)という動作が変わるライブラリがあるが、非推奨とされてる)

確実に動作を変えたいのであれば、必ず再読み込みを行う`load`を使用してください。

[code]

    require 'aaa'
    
    class Foo
      def hogehoge
         :
      end
         :
    end
    
    load 'bbb' # must load 'bbb' after Foo
    class Bar
      def hogehoge
         :
      end
         :
    end
    
[/code]

ただし、 **bbb** が先に読み込まれていると`Foo`の動作が変わるという場合は、これを防ぐことは困難です。 **bbb**
と`Foo`の実装を見直し、読み込みタイミングに依存しないようにする作るべきでしょう。

機能の有効・無効の条件分岐があるため、有効の時だけ必要なライブラリを`require`するという作りはよくしますが、それ以外の時は、基本的に`require`は全てファイル先頭にまとめて配置します。もし、`Foo`と`Bar`のどちらで必要とされているかわからなくなるのが嫌だというのであれば、`Foo`と`Bar`を別ファイルにすべきでしょう。1ファイルにまとめた簡単な事だけをするツール等を作っているので無ければ、1クラス1ファイルが基本になります。

* * *

さて、これで`require`はなんとかしました。全てファイルの先頭に書くことができたとします。次に共通部分をまとめるですが、色々と方法あるのですが、通常は
**クラスの継承** を使います。`hogehoge`が全く同じだったとすれば、次のように書けるはずです。

[code]

    require 'aaa'
    require 'bbb'
    
    class FooBar
      def hogehoge
         :
      end
         :
    end
    
    class Foo < FooBar
         :
    end
    
    class Bar < FooBar
         :
    end
    
[/code]

`Foo`と`Bar`は`FooBar`を継承しています。別途上書きで定義(オーバーライドという)等しない限り、`Foo`も`Bar`も`hogehoge`メソッドは使えて、その内容は`FooBar`の`hogehoge`メソッドになります。この`FooBar`を`Foo`と`Bar`の
**親クラス** (スーパークラスとも)といい、`Foo`と`Bar`は`FooBar`の **子クラス**
(サブクラスとも)といいます。。このようにクラスの継承を使うことで、共通部分を親クラスにまとめることでき、その子クラスでは差異だけ実装すれば良くなります。

クラスの継承以外に共通部分をまとめる方法として **Mixin**
(ミックスイン)があります。クラスの継承とあわせて入門書には必ず載っているRubyの基本機能の一つですので、詳細は書籍等を参考にしてください。


CC BY-SA 3.0
2016-11-26T00:07:41.277
30675
2016-11-26T00:07:41.277
7347
30647
answer