読者です 読者をやめる 読者になる 読者になる

プログラミング 美徳の不幸

Ruby, Rails, JavaScriptなどのプログラミングまとめ、解説、備忘録。

設計の悩み

今更かよって感じですが、最近は非常にアプリケーションの設計の難しさを痛感しています。

そもそも設計の難しさを形作るのは大きく2つだと思います。1つは、アプリケーションは生き物であるということ。つまりある時点でベストな設計が、次のイテレーションでは良くない設計になっていることもあると思います。

例えば
◯user.rb

class User

  def sex
    case @sex
    when 1 then "Male"
    when 2 then "Female"
    end
  end
  
  def sex=(str)
    @sex = 1 if str=~/^male/i
    @sex = 2 if str=~/^female/i
  end
end
        

というように、あるユーザの性別をcase文でこのように表現する事はあると思います。で、もしアプリケーションがユーザしか管理しない単純なものであれば、確かにこのような設計でも十分でしょう。しかしもし、次のイテレーションでこのアプリケーションにDress(服)というクラスが導入されて、このDressが男女どちら用なのかを持つことになったらどうするか。

普通にやると上と似たようなコードをまた書かないといけないわけですが、それはコードの重複を生んでしまうので、まぁモジュールの抽出を行うかクラスの抽出を行うべきでしょう。この場合はSexという性別を扱うクラスを作ることになると思います。

というように、今後のアプリケーションの変動に従って最適な構成というのは変わるのが普通でしょう。問題は、そのような設計上の変更が起こった時に速やかにコードをリファクタリングしてテストを通してやることじゃないでしょうか。

でまぁ、上記のことは習慣的にテストを書くとかリファクタリングを意識していれば、努力すればできることだと思うのですが、設計の難しさのもう一つの要因は、設計は人のためにあるという点だと思います。

これは最近まつもとゆきひろさんの本で読んだんですが、アプリケーションをプログラミングするというのはつまりそのアプリケーションのDSLを作ることだそうです。というと、人に読んでもらってわかりやすければ良い設計、そうでなかったら悪い設計ということになるのではないでしょうか。




抽象論はともかく最近一番悩んでるのはRailsのObserverを分岐させる話です。前もちらっと書きましたが、あれから少し悩んで一種のタイプコード(上でいうcase文)のロジックをObserverに混入させるぐらいなら、もうモデルを分けちゃえばいいんじゃないかとも思いました。

つまり
Postというモデルは9割型、その対応するファイルを作成したいんですが、ときどき作成したくないときがある。こういうときに、Post.new(bare:true)としてコールバックをスキップするよりも、Postクラスを継承したBarePostクラスを用意する。

Post.new(bare:true)が最も気持ち悪いのは、このコードを他のモデルから使うときにbare:trueという独自の文法を知って置かなければならないというカプセル化できていない点にあるのです。BarePostはクラス名さえ知っておけば実装を知らなくて済むのでそれはそれで解決策でしょう。

ただ、BarePostクラスの役割は単にObserverの一部をスキップするだけというのだから、それこそクラスをインライン化するべきのような気もするのです。

いろいろ考えた結果、Observerが3分岐するならポリモーフィズム、2分岐ならタイプコードによるスキップというのが現実的妥協じゃなかろうかと思いましたが、どうなんでしょう??