Pull to refresh

Comments 16

Чем это лучше простого наследования классов без STI?

# пользователь
class Person < ActiveRecord::Base
end

# продавец
class Seller < Person
# тут все те же методы, что и в вашем примере
end

seller = Seller.find(1)
seller.items
seller.first_name

По условиям задачи, Person может быть одновременно и Seller, и Buyer. С наследованием не получится.
Простите, только понял, о чем вы. DelegateClass позволит сократить количество запросов к БД, если нужно отобразить информацию о пользователе одновременно как о покупателе, продавце и авторе:

Наследование:
def show
  @articles = Author.find(params[:id]).articles.size # раз запрос
  @items = Seller.find(params[:id]).items.size # два запрос
  @buys = Buyer.find(params[:id]).purchased_items.size # три запрос
end


Можно, конечно воспользоваться магией ActiveRecord, но это уже не сработает (скорее всего, возможно есть аналогичный функционал) для mongoid:
def show
  @person = Person.find(params[:id])          # раз запрос
  @articles = @person.becomes(Author).articles.size 
  @items = @person.becomes(Seller).items.size
  @buys = @person.becomes(Buyer).purchased_items.size
end


С DelegateClass никаких лишних запросов:
def show
  @person = Person.find(params[:id])          # раз запрос
  @articles = Author.new(@person).articles.size 
  @items = Seller.new(@person).items.size
  @buys = Buyer.new(@person).purchased_items.size
end
Позвольте не согласиться насчет сокращения запросов к БД — кеширование запросов в АР работает замечательно.

Насчет Монгоид ничего сказать не могу, уже почти год с ним не работаю.

Мне кажется, что DelegateClass больше годится для каких-нибудь декораторов, чем для разделения разросшейся логики для одного класса.
Интересно, а вообще привык для подобных задач (разграничения ответственности) использовать агрегацию (в терминах БД — связи один-к-одному). В Person ссылки на Seller, Writer и Buyer, а в них на Person. Выбрать всю информацию одним запросом из Person можно, как и получить одним запросом Seller и связанного с ним Person.
В данном примере смысла в этом нет, Seller, Author, Buyer и Person — это одна сущность, и хранится она должна одной строкой в БД. У Seller, Author и Buyer нет собственных аттрибутов. Это, скорее, роли. Можно было бы использовать STI, но по условиям задачи Person может быть Seller'ом, Author'ом и Buyer'ом одновременно.
Я больше про объектную модель. В базе её, да, можно проецировать на одну строку таблицы.
SOLID это хорошо и правильно, но не следует забывать, что это не догмы, а рекомендации, которые обычно_было_бы_неплохо_учитывать.
Этот код вполне приемлим, и его и следует использовать при малом объёме функционала.
class Person < ActiveRecord::Base
  #  ...
  has_many :purchased_items        # купленные вещи
  has_many :purchased_transactions # оплата покупок
end


Если же класс разрастается, то в руби есть прекрасный механизм абстрагирования функционала — модули. Выносите логику покупателя/продаца в них и подключайте нужные в класс.
Не хотите всё разом совать в объявление класса, добавляйте только нужные при создании объекта, или вообще по вызову специального метода.

А этот код по мне выглядит и пахнет не как руби, а как сишарп или ява.
class Seller < DelegateClass(Person)
  ...
end

person = Person.find(1)
seller = Seller.new(person)


В комментариях к оригинальной статье предложили и другое решение через concern, опять же более красивое, чем DelegateClass.
class Person
  concern 'person/selling'
end

class Person::Selling < Concern
  def looking_good?
    sold_items > 5 # sold items is a column on person
  end
end

Person.first.selling.looking_good?
Методы типа #is_whatever? это не ruby-way. В руби уже есть "?", поэтому префикс is_ излишен.
возможно вы созрели до DCI (data context interaction ) — новая парадигма от создателя MVC :")
Видимо это и есть DCI в чистом виде
не совсем. в dci есть понятие роли, которую обект динамически принимает в определенном контексте и которая есть вместилище поведения. Приведенный пример как раз в относительно грязном виде (с точки зрения dci) илюстрирует эту парадигму :) mikepackdev.com/blog_posts/24-the-right-way-to-code-dci-in-ruby/
А можете пояснить в чем же все-таки разница? Только в том, что мы явно не выделили класс-контекст? Это же принципиально ничего не меняет — в роли контекста у нас метод контоллера. Если юз-крейс где-то еще понадобится, мы его можем безболезненно выделить. YAGNI так сказать.
да уж видимо ее нет :)
Sign up to leave a comment.

Articles