Рефакторинг моделей ActiveRecord с базовым классом по сравнению с базовым модулем

Класс A и B идентичны:

class A < ActiveRecord::Base
 def foo
  puts "foo"
 end
end

class B < ActiveRecord::Base
 def foo
  puts "foo"
 end
end

Какая разница между рефакторингом, подобным этому, с базовым классом:

class Base < ActiveRecord::Base
 def foo
  puts "foo"
 end
end

class A < Base
end

class B < Base
end

в отличие от этого, используя базовый модуль :

module Base
 def foo
  puts "foo"
 end
end

class A < ActiveRecord::Base
 include Base
end

class B < ActiveRecord::Base
 include Base
end

Один предпочтительный вариант для другого?

Ответы

Ответ 1

Существует принципиальное различие между этими двумя методами, которые отсутствуют во всех остальных ответах, и что реализация рельсов ИППП (однонамерное наследование):

http://api.rubyonrails.org/classes/ActiveRecord/Base.html (найдите раздел "Наследование отдельных таблиц" )

В принципе, если вы реорганизуете свой базовый класс следующим образом:

class Base < ActiveRecord::Base
  def foo
    puts "foo"
  end
end

class A < Base
end

class B < Base
end

Затем вы должны иметь таблицу базы данных, называемую "base", с столбцом "type", который должен иметь значение "A" или "B". Столбцы этой таблицы будут одинаковыми для всех ваших моделей, и если у вас есть столбец, принадлежащий только одной из моделей, ваша таблица "оснований" будет денормализирована.

Если вы реорганизуете свой базовый класс следующим образом:

Module Base
  def foo
  puts "foo"
 end
end

class A < ActiveRecord::Base
 include Base
end

class B < ActiveRecord::Base
 include Base
end

Тогда не будет табличных "баз". Вместо этого будет таблица "как" и таблица "bs". Если они имеют одинаковые атрибуты, столбцы должны быть дублированы в обеих таблицах, но если есть различия, они не будут деномализированы.

Итак, если предпочтительнее другого, да, но это специфично для вашего приложения. Как правило, если у них есть те же самые свойства или большое перекрытие, используйте STI (1-й пример), иначе используйте модули (2-й пример).

Ответ 2

Оба эти метода будут работать. Когда вы решаете использовать модуль или класс, вопрос, который у меня есть, заключается в том, что класс вписывается в иерархию объектов, или это просто методы, которые я ищу для повторного использования. Если я просто пытаюсь разложить общий код по причинам DRY, это звучит как модуль. Если действительно существует класс, который вписывается в иерархию, которая имеет смысл сама по себе, я использую класс.

Исходя из фона Java, он обновляется, и я могу решить эти решения.

Ответ 3

У вас больше гибкости с модулем. Цель модуля - охватывать разные типы классов. С помощью другого метода вы запираетесь на Base. Кроме этого, нет большой разницы.

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

Таким образом, ваш выбор состоит в том, чтобы объединиться в длинной цепочке или использовать миксин, который намного чище и легче понять.

Ответ 4

Модуль дает вам больше гибкости в том, что 1) вы можете наследовать только один класс, но можете включать несколько модулей, и 2) вы не можете наследовать из базового класса, не наследуя его суперклассы, но вы можете включить (например, вы можете добавить метод "foo" к другому классу, который не является активной моделью записи).

Другое отличие состоит в том, что в методах в классе Base вы можете вызывать вещи из ActiveRecord:: Base, но вы не могли сделать этого из модуля.

Ответ 5

Это зависит от того, что вы действительно пытаетесь сделать.

  • Переопределение или добавление методов в ActiveRecord:: Base: сделайте это, если вы хотите, чтобы каждая модель ActiveRecord в вашем приложении была respond_to foo.
  • Подкласс ActiveRecord:: Base и каждая модель наследуется от вашего подкласса: Достигает того же, что и 1, но каждая модель в вашем приложении должна распространять нетрадиционный класс, поэтому зачем беспокоиться.
  • include module: Это отлично работает, если только некоторое количество моделей нуждается в доступе к foo. Это в значительной степени то, что делают все эти плагины acts_as_<whatever>.

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