Рефакторинг моделей ActiveRecord с базовым классом по сравнению с базовым модулем - PullRequest
13 голосов
/ 10 ноября 2009

Класс 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

Один путь предпочтительнее другого?

Ответы [ 5 ]

24 голосов
/ 11 ноября 2009

Существует фундаментальное различие между этими двумя методами, которые отсутствуют во всех других ответах, и это реализация STI (наследование одной таблицы) в rails:

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

Тогда у вас должна быть таблица базы данных с именем «основы »со столбцом« тип », которая должна иметь значение« А »или« В ». Столбцы в этой таблице будут одинаковыми во всех ваших моделях, и если у вас есть столбец, который принадлежит только одной из моделей, ваша таблица «баз» будет денормализована.

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

Module Base
  def foo
  puts "foo"
 end
end

class A < ActiveRecord::Base
 include Base
end

class B < ActiveRecord::Base
 include Base
end

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

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

5 голосов
/ 10 ноября 2009

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

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

4 голосов
/ 10 ноября 2009

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

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

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

1 голос
/ 10 ноября 2009

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

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

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

1 голос
/ 10 ноября 2009

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

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

...