ОО дизайн в рельсах: куда положить вещи


Я действительно наслаждаюсь Rails (хотя я вообще беспокойный), и мне нравится, что Ruby очень OO. Тем не менее, тенденция к созданию огромных подклассов ActiveRecord и огромных контроллеров вполне естественна (даже если вы используете контроллер на ресурс). Если бы вы создавали более глубокие объектные миры, куда бы вы поместили классы (и модули, я полагаю)? Я спрашиваю о представлениях (в самих помощниках?), контроллеры и модели.

Либ в порядке, и я нашел некоторые решения чтобы получить его для перезагрузки в среде разработки, но я хотел бы знать, если есть лучший способ сделать это. Я действительно просто беспокоюсь о том, что классы становятся слишком большими. Кроме того, как насчет двигателей и как они вписываются?

4 238

4 ответа:

поскольку Rails обеспечивает структуру с точки зрения MVC, естественно в конечном итоге использовать только контейнеры модели, представления и контроллера, которые предоставляются для вас. Типичная идиома для начинающих (и даже некоторых промежуточных программистов) заключается в том, чтобы втиснуть всю логику в приложение в модель (класс базы данных), контроллер или представление.

в какой-то момент кто-то указывает на парадигму "fat-model, skinny-controller", и промежуточные разработчики спешно вырезают все из их контроллеры и бросают его в модель, которая начинает становиться новым мусорным баком для логики приложения.

тощие контроллеры, на самом деле, хорошая идея, но следствие-положить все в модель, на самом деле не лучший план.

в Ruby, у вас есть несколько хороших вариантов для достижения более модульным. Довольно популярный ответ-просто использовать модули (обычно спрятанные в lib), которые содержат группы методов, а затем включают модули в соответствующий класс. Это помогает в тех случаях, когда у вас есть категории функциональности, которые вы хотите использовать в нескольких классах, но где функциональность все еще условно привязана к классам.

помните, когда вы включаете модуль в класс, методы становятся методами экземпляра класса, поэтому вы все равно получаете класс, содержащий Т из методов, они просто организованы красиво в несколько файлов.

Это решение может работать хорошо внутри в некоторых случаях--в других случаях вы захотите подумать об использовании классов в своем коде, которые не модели, представления и контроллеры.

Если у вас есть концепции, которые не вписываются аккуратно в эти поля (настойчивость, управление запросами/ответами), вы, вероятно, хотите подумать о том, как вы б моделируйте рассматриваемую идею. Вы можете хранить немодельные классы в приложении / классах или в любом другом месте и добавлять этот каталог в свой путь загрузки, выполнив:

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

Если вы используете passenger или JRuby, вы, вероятно, также хотите добавить свой путь к нетерпеливым путям загрузки:

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

В суть в том, что как только вы доберетесь до точки в Rails, где вы зададите этот вопрос, пришло время усилить свои рубиновые отбивные и начать классы моделирования, которые не являются только классами MVC, которые Rails дает вам по умолчанию.

обновление: этот ответ относится к рельсам 2.X и выше.

обновление: использование проблем было подтвердили как новое значение по умолчанию в 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 core extensions обычно имеют место в инициализаторах конфигурации, так что библиотеки загружаются только один раз на Rails boostrap.

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

для многоразовых фрагментов кода я часто создаю (микро)плагины, чтобы я мог повторно использовать их в других проектах.

вспомогательные файлы обычно содержат вспомогательные методы, а иногда и классы, когда объект предназначен для использования помощниками (например, Построителями форм).

это действительно общий обзор. Просьба представить более подробную информацию о конкретных примерах, если вы хотите получить более индивидуальные рекомендации. :)

... тенденция делать огромные Подклассы форм и огромный контроллеры вполне естественны ...

"огромный" - это тревожное слово... ; -)

как ваши контроллеры становятся огромными? Это то, на что вы должны смотреть: в идеале контроллеры должны быть тонкими. Выбирая эмпирическое правило из воздуха, я бы предположил, что если у вас регулярно есть более, скажем, 5 или 6 строк кода на метод контроллера (действие), то ваши контроллеры, вероятно, слишком толстый. Есть ли дублирование, которое может перейти в вспомогательную функцию или фильтр? Есть ли бизнес-логика,которая может быть вытеснена в модели?

как ваши модели становятся огромными? Следует ли вам искать способы сокращения числа обязанностей в каждом классе? Есть ли какие-либо общие модели поведения, которые вы можете извлечь в миксины? Или области функциональности, которые вы можете делегировать вспомогательным классам?

EDIT: попытка немного расширить, надеюсь, ничего не искажая плохо...

помощники: жить в app/helpers и в основном используются для упрощения представлений. Они либо специфичны для контроллера (также доступны для всех представлений для этого контроллера), либо вообще доступны (module ApplicationHelper в application_helper.рубидий.)

фильтры: скажем, у вас есть одна и та же строка кода в нескольких действиях (довольно часто, извлечение объекта с помощью params[:id] или подобные). Это дублирование может быть абстрагировано сначала в отдельный метод, а затем полностью из действий, объявив a фильтр в определении класса, например before_filter :get_object. Раздел 6 в Направляющая Рельсов ActionController пусть декларативное Программирование будет вашим другом.

рефакторинг моделей-это немного больше религиозная вещь. Ученики Дядя Боб предложит, например, чтобы вы следовали пяти заповедям SOLID. Джоэл И Джефф может рекомендовать более, э-э, "прагматичный" подход, хотя они действительно казались мало более примиренный впоследствии. Найти один или несколько методов внутри класса, которые работают на четко определенное подмножество его атрибутов является одним из возможных способов определения классов, может быть вынесен из вашей модели-производной модели.

модели Rails не должны быть подклассами ActiveRecord:: Base, кстати. Или, другими словами, модель не должна быть аналогом таблицы или даже связана с чем-либо, хранящимся вообще. Даже лучше, пока вы называете свой файл в app/models в соответствии с соглашениями Rails (вызов #подчеркивание имени класса, чтобы узнать, что Rails будет искать), Rails найдет его без каких-либо requires необходимо.

вот отличный пост в блоге о рефакторинге жирных моделей, которые, похоже, возникают из" тонкого контроллера " philosphy:

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

основное сообщение - "не извлекайте миксины из жировых моделей", вместо этого используйте классы обслуживания, автор предоставляет 7 шаблонов для этого