web-dev-qa-db-ja.com

OO Railsの設計:物を置く場所

私はRailsを本当に楽しんでいます(私は一般的にRESTレスですが)、そしてRubyはとてもオブジェクト指向です。それでも、巨大なActiveRecordサブクラスと巨大なコントローラーを作成する傾向はごく自然です(リソースごとにコントローラーを使用する場合でも)。より深いオブジェクトの世界を作成する場合、クラス(およびモジュール、私は)をどこに配置しますか?ビュー(ヘルパー自体?)、コントローラー、モデルについて質問しています。

Libは大丈夫で、私は 開発環境でリロードするためのいくつかの解決策 を見つけましたが、このようなことを行うより良い方法があるかどうか知りたいです。私は本当にクラスが大きくなりすぎることを心配しています。また、エンジンについてはどうですか?

241
Dan Rosenstark

RailsはMVCの観点から構造を提供するため、最終的にonlyが提供されるモデル、ビュー、およびコントローラーコンテナーを使用することになります。初心者(および一部の中間プログラマー)の典型的なイディオムは、アプリ内のすべてのロジックをモデル(データベースクラス)、コントローラー、またはビューに詰め込むことです。

ある時点で、誰かが「ファットモデル、スキニーコントローラー」のパラダイムを指摘し、中間開発者がコントローラーからすべてを急いでモデルに投入し、アプリケーションロジックの新しいゴミ箱になり始めます。

スキニーコントローラーは実際には良いアイデアですが、当然のことながら、モデルにすべてを入れることは、実際には最善の計画ではありません。

Rubyには、よりモジュール化するための優れたオプションがいくつかあります。かなり一般的な答えは、メソッドのグループを保持するモジュール(通常はlibに格納されている)を使用し、適切なクラスにモジュールを含めることです。これは、複数のクラスで再利用したい機能のカテゴリがあるが、機能が概念的にクラスに関連付けられている場合に役立ちます。

モジュールをクラスに含めると、メソッドはクラスのインスタンスメソッドになるため、メソッドのtonを含むクラスになります。これらは複数のファイルにうまく整理されているだけです。 。

このソリューションは、いくつかのケースでうまく機能します。他のケースでは、コード内でnotモデル、ビュー、またはコントローラーであるクラスの使用を検討する必要があります。

それについて考える良い方法は、「単一の責任原則」です。これは、クラスが単一(または少数)の事柄に対して責任を負うべきだと言うものです。モデルは、アプリケーションからデータベースへのデータの永続化を担当します。コントローラーは、リクエストを受信し、実行可能なレスポンスを返す責任があります。

これらのボックスにうまく収まらない概念(永続性、要求/応答管理)がある場合は、おそらくwould問題のアイデアをどのようにモデル化するかを考えてください。非モデルクラスをapp/classesまたはその他の場所に保存し、次のようにしてそのディレクトリをロードパスに追加できます。

config.load_paths << File.join(Rails.root, "app", "classes")

パッセンジャーまたはJRubyを使用している場合は、おそらく、積極的なロードパスにパスを追加することもできます。

config.eager_load_paths << File.join(Rails.root, "app", "classes")

一番下の行は、Railsでこの質問をしているところに到達したら、Rubyチョップを強化して、そうでないクラスのモデリングを開始するときです。 Railsがデフォルトで提供するMVCクラスのみ。

更新:この回答は、Rails 2.x以降に適用されます。

378
Yehuda Katz

Update:懸念事項の使用が Rails 4の新しいデフォルトとして確認されました

それは本当にモジュール自体の性質に依存します。通常、コントローラー/モデルの拡張機能をアプリ内の/ concernsフォルダーに配置します。

# concerns/authentication.rb
module Authentication
  ...
end    

# controllers/application_controller.rb
class ApplicationController
  include Authentication
end



# concerns/configurable.rb
module Configurable
  ...
end    

class Model 
  include Indexable
end 

# controllers/foo_controller.rb
class FooController < ApplicationController
  include Indexable
end

# controllers/bar_controller.rb
class BarController < ApplicationController
  include Indexable
end

/ libは、一般的な目的のライブラリに適した選択肢です。 libには常にプロジェクト固有のライブラリがあり、アプリケーション固有のライブラリをすべて配置しています。

/lib/myapp.rb
module MyApp
  VERSION = ...
end

/lib/myapp/CacheKey.rb
/lib/myapp/somecustomlib.rb

通常、Ruby/Railsのコア拡張は設定初期化子で行われるため、ライブラリはRails boostrapで1回だけロードされます。

/config/initializer/config.rb
/config/initializer/core_ext/string.rb
/config/initializer/core_ext/array.rb

再利用可能なコードフラグメントの場合、他のプロジェクトで再利用できるように(マイクロ)プラグインを作成することがよくあります。

ヘルパーファイルは通常、ヘルパーメソッドを保持し、オブジェクトがヘルパー(フォームビルダなど)によって使用されることを意図している場合はクラスを保持します。

これは非常に一般的な概要です。よりカスタマイズされた提案を取得したい場合は、特定の例に関する詳細を提供してください。 :)

62
Simone Carletti

...巨大なActiveRecordサブクラスと巨大なコントローラーを作成する傾向は非常に自然です...

「巨大」は気になる言葉です... ;-)

コントローラーはどのように巨大になっていますか?それはあなたが見なければならないものです:理想的には、コントローラーは薄いべきです。薄気味悪い経験則を選んで、コントローラーのメソッド(アクション)ごとに、たとえば5行または6行以上のコードを定期的に持っている場合、おそらくコントローラーが太りすぎていることをお勧めします。ヘルパー関数またはフィルターに移動できる重複はありますか?モデルにプッシュダウンできるビジネスロジックはありますか?

モデルはどのようにして巨大になりますか?各クラスの責任の数を減らす方法を検討すべきですか?ミックスインに抽出できる一般的な動作はありますか?または、ヘルパークラスに委任できる機能の領域ですか?

編集:少し拡大しようとして、うまくいけば何かを歪めないでください...

ヘルパー:app/helpersにあり、主にビューをシンプルにするために使用されます。これらはコントローラー固有(そのコントローラーのすべてのビューでも利用可能)または一般的に利用可能(application_helper.rbのmodule ApplicationHelper)です。

フィルター:いくつかのアクションで同じコード行があるとしましょう(多くの場合、params[:id]などを使用してオブジェクトを取得します)。その重複は、最初に別のメソッドに抽象化され、次にbefore_filter :get_objectなどのクラス定義でフィルターを宣言することにより、アクションから完全に抽象化されます。 ActionController Rails Guide のセクション6を参照してください==宣言型プログラミングを友達にしましょう。

モデルのリファクタリングはもう少し宗教的なことです。 ボブおじさん の弟子は、たとえば SOLID の5つの戒めに従うことを提案します。ジョエルとジェフ 推奨される場合があります より多くのerの「実用的な」アプローチですが、その後は 少し和解 のように見えました。明確に定義された属性のサブセットで動作するクラス内の1つ以上のメソッドを見つけることは、ActiveRecord派生モデルからリファクタリングされる可能性のあるクラスを特定する1つの方法です。

ちなみに、RailsモデルはActiveRecord :: Baseのサブクラスである必要はありません。別の言い方をすれば、モデルはテーブルの類似物である必要はなく、まったく保存されているものに関連する必要さえありません。さらに良いのは、Railsの規則に従ってapp/modelsでファイルに名前を付ける限り(クラス名で#underscoreを呼び出して、Railsが探すものを見つける)、Railsはrequiresを必要とせずにそれを見つけます。

10
Mike Woodhouse

「シンコントローラー」の哲学から生じると思われる脂肪モデルのリファクタリングに関する優れたブログ投稿があります。

http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

基本的なメッセージは「Fat ModelsからMixinを抽出しないでください」で、代わりにサービスクラスを使用します。作成者はそのために7つのパターンを提供します

1
bbozo