モジュール関数にするかインスタンスメソッドにするかの判断基準

多態性が必要か否か

FizzBuzzをRubyで解くとする。このとき、いきなり:

100.fizzbuzz

のように、Fixnumのインスタンスメソッドとして実装しようとは思わない。

FizzBuzz.fizzbuzz(100)

のように、FizzBuzzモジュールのモジュール関数として実装するだろう。

しかし、fizzbuzzメソッドの引数としてRangeオブジェクトも渡せるようにしたくなったらどうか。

FizzBuzz.fizzbuzz(100)
FizzBuzz.fizzbuzz(10..100)

の代わりに:

100.fizzbuzz
(10..100).fizzbuzz

としたくなるかもしれない。

この場合、多態性が必要か否かが、モジュール関数にするかインスタンスメソッドにするかの判断基準となる。fizzbuzzは微妙かもしれないが、to_sのようにメソッドが汎用的であればあるほど、インスタンスメソッドへの欲求が強くなる。

インスタンス変数が複数回使われるか否か

Rangeへの対応は不要だが、FizzBuzz出力の区切り文字を指定させたくなったらどうか。

FizzBuzz.fizzbuzz(100, ",")
FizzBuzz.fizzbuzz(100, "\n")

とすべきか:

fizzbuzzer = FizzBuzzer.new
fizzbuzzer.delimiter = ","
fizzbuzzer.fizzbuzz(100)

とすべきか。

もし:

fizzbuzzer = FizzBuzzer.new
fizzbuzzer.delimiter = ","
fizzbuzzer.fizzbuzz(100)
fizzbuzzer.fizzbuzz(200)

のように、@delimiterが複数回使われるのであれば、インスタンスメソッドにするかもしれない。インスタンス変数の使われる回数が多いと予想されればされるほど、インスタンスメソッドへの欲求が強くなる。


なお:

FizzBuzz.fizzbuzz(100, ",")

のようにすると、それぞれの引数の意味が分かりにくくなると『クラス設計の考え方』で指摘されている。

ただ、キーワード引数を使えば、さほど問題にはならないだろう。

FizzBuzz.fizzbuzz(100, delimiter: ",", sleep: 1)

変更に強いWeb-MVC

Railsのコントローラについて、しっくりこない点があった。たとえば、下記のようなコード。

class ExampleController < ActionController::Base
  def show
    @data1 = ExampleModel1.find params[:key1]
    @data2 = ExampleModel2.find params[:key2]
    @data3 = ExampleModel3.find params[:key3]
  end
end

これだと、ビュー側で @data3 の表示が不要になったときに、コントローラのコードをいじる必要が生じる。厳密にはいじらなくてもよいが、いじるかどうか検討する必要は生じる。変更に弱い、ということだ。

ビューがモデルを直接参照していれば、そうはならない。本来、MVCではビューがモデルを参照するのが当たり前。WebアプリのMVCモドキでも、そうするメリットがあるのだ。

結局、コントローラの仕事は「モデルの更新、ビューの決定」のみに留めるべきだと思う。モデルの参照はビューの仕事。


モデルの変更や出力に関わるバリデーションをコントローラで行うのも、変更に弱いやり方だろう。たとえば(必須項目が漏れているなどの)モデルの更新に関わるものであればモデルに任せればよいし、(表示ページ番号などの)出力に関わるものであればビューに任せればよい。コントローラがバリデーションすべきは、更新対象となるモデルや、出力処理を任せるビューを決めるためのパラメータだけだ。

実際には、そのようなパラメータはほぼ使われないだろう。更新対象となるモデルや、出力を任せるべきビューは、アクションごとにおのずと決まるはずだ。つまり、コントローラでバリデーションすべきパラメータはまずない、ということだ。

記事検索
Twitter