Chossing Message

会社の先輩から『Smalltalkベストプラクティスパターン』って本を借りたのでおっ(^ω^)と思ったパターンをメモっていく。

Chossing Message

さまざまな選択肢のうちのひとつを実行させるにはどうしますか?

Rubyで書くとこういうケースのことだと思う。

class Factory
  def food(type)                                                                                                                                            
    if(type == 'cat')
      'CatFood'
    elsif(type == 'dog')
      'DogFood'
    elsif(type == 'person')
      'カレー'
    end 
  end 
end

f = Factory.new
p f.food('cat')
p f.food('dog')

Factory#foodに文字列を渡すと引数に一致する処理を実行する。
こういうハードコーディングだと問題があって、
既存のロジックをいじらずにバリエーションを追加することがとても難しくなる。増殖する傾向にある。
本では以下のようにするとよいと書いている。

class Cat
  def food
    "CatFood"
  end
end

class Dog
  def food
    "DogFood"
  end
end

class Factory
  def food(obj)
    obj.food
  end
end

tama = Cat.new
poti = Dog.new

f = Factory.new
p f.food(tama)
p f.food(poti)

条件文がなくなった!
クラスが増えていくけどMVCフレームワークでは普通に使えそうだと思った。

いくつかのオブジェクトを用意して、そのうちの1つにメッセージを送り、各オブジェクトがひとつの選択肢を実行するようにしましょう。

ダックタイプ

Railsで使ってみた

このRailsアプリはソーシャルネットワーキングサービスを作っている。イメージはmixiみたいな感じ。

機能

ユーザは記事、辞典、写真、コミュニティを公開できる。
それぞれのコンテンツは非公開状態にすることができ、非公開状態の場合は作成者のみが閲覧できる。

公開の制御するコードをChoosing Messageを使わないで自分なりに書くと
class BaseController < ApplicationController
  def can_not_access_to_private_content
    if not owner? 
      render_404 if @article.try(:private?)
      render_404 if @dic.try(:private?)
      render_404 if @photo.try(:private?)
      render_404 if @comunitie.try(:private?)
    end 
  end
end
ArticlesController < BaseController
  def show
    @article = Article.find(params[:id])                                                                                                                          
    can_not_access_to_draft
  end
end

DictionariesController < BaseController
  def show
    @dic = Dictionary.find(params[:id])                                                                                                                          
    can_not_access_to_draft
  end
end
.
.
.

となってモデルが増える度に同じこと書かないといけない。漏れがでてきそう。

Chossing Messageっぽく書く
class BaseController < ApplicationController
  before_action :set_item, only: [:show]

  def set_item
    item_constant = params[:controller].singularize.capitalize.constantize
    @item = item_constant.find(params[:id])
    can_not_access_to_draft
  end 

  def can_not_access_to_private_content
    if not logged_in?                                                                                                                                       
      render_404 if @item.private?
    end 
  end
end
ArticlesController < BaseController
  def show
  end
end

DictionariesController < BaseController
  def show
  end
end
.
.
.