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

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

244
Dan Rosenstark 1 Июл 2009 в 15:18

4 ответа

Лучший ответ

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

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

На самом деле компактные контроллеры - это хорошая идея, но, как следствие, объединение всего в модели - не лучший план.

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

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

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

Хороший способ подумать об этом - это «принцип единой ответственности», который гласит, что класс должен нести ответственность за одну (или небольшое количество) вещей. Ваши модели отвечают за сохранение данных из вашего приложения в базе данных. Ваши контроллеры несут ответственность за получение запроса и возврат надежного ответа.

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

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

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

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

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

Обновление: этот ответ относится к Rails 2.x и выше.

384
Adam Zerner 12 Сен 2017 в 21:16

Вот отличный пост в блоге о рефакторинге толстых моделей, которые, кажется, возникли из философии «тонкого контроллера»:

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

Основное сообщение: «Не извлекайте миксины из жирных моделей», используйте вместо этого служебные классы, автор предлагает 7 шаблонов для этого.

1
bbozo 15 Сен 2015 в 07:58

... тенденция к созданию огромных подклассов ActiveRecord и огромных контроллеров вполне естественна ...

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

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

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

РЕДАКТИРОВАТЬ: Попытка немного расшириться, надеюсь, ничего не искажает слишком сильно ...

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

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

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

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

10
Community 18 Янв 2021 в 12:38

Обновление : использование проблем было подтверждено как новое значение по умолчанию в Rails 4.

Это действительно зависит от характера самого модуля. Я обычно помещаю расширения контроллера / модели в папку / issues в приложении.

# 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.

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

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

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

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

62
Simone Carletti 27 Дек 2012 в 11:02