Comments 16
Чем это лучше простого наследования классов без STI?
# пользователь
class Person < ActiveRecord::Base
end
# продавец
class Seller < Person
# тут все те же методы, что и в вашем примере
end
seller = Seller.find(1)
seller.items
seller.first_name
0
По условиям задачи, Person может быть одновременно и Seller, и Buyer. С наследованием не получится.
+1
Простите, только понял, о чем вы. DelegateClass позволит сократить количество запросов к БД, если нужно отобразить информацию о пользователе одновременно как о покупателе, продавце и авторе:
Наследование:
Можно, конечно воспользоваться магией ActiveRecord, но это уже не сработает (скорее всего, возможно есть аналогичный функционал) для mongoid:
С 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
+1
Позвольте не согласиться насчет сокращения запросов к БД — кеширование запросов в АР работает замечательно.
Насчет Монгоид ничего сказать не могу, уже почти год с ним не работаю.
Мне кажется, что DelegateClass больше годится для каких-нибудь декораторов, чем для разделения разросшейся логики для одного класса.
Насчет Монгоид ничего сказать не могу, уже почти год с ним не работаю.
Мне кажется, что DelegateClass больше годится для каких-нибудь декораторов, чем для разделения разросшейся логики для одного класса.
0
Интересно, а вообще привык для подобных задач (разграничения ответственности) использовать агрегацию (в терминах БД — связи один-к-одному). В Person ссылки на Seller, Writer и Buyer, а в них на Person. Выбрать всю информацию одним запросом из Person можно, как и получить одним запросом Seller и связанного с ним Person.
+1
В данном примере смысла в этом нет, Seller, Author, Buyer и Person — это одна сущность, и хранится она должна одной строкой в БД. У Seller, Author и Buyer нет собственных аттрибутов. Это, скорее, роли. Можно было бы использовать STI, но по условиям задачи Person может быть Seller'ом, Author'ом и Buyer'ом одновременно.
0
SOLID это хорошо и правильно, но не следует забывать, что это не догмы, а рекомендации, которые обычно_было_бы_неплохо_учитывать.
Этот код вполне приемлим, и его и следует использовать при малом объёме функционала.
Если же класс разрастается, то в руби есть прекрасный механизм абстрагирования функционала — модули. Выносите логику покупателя/продаца в них и подключайте нужные в класс.
Не хотите всё разом совать в объявление класса, добавляйте только нужные при создании объекта, или вообще по вызову специального метода.
А этот код по мне выглядит и пахнет не как руби, а как сишарп или ява.
В комментариях к оригинальной статье предложили и другое решение через concern, опять же более красивое, чем DelegateClass.
Этот код вполне приемлим, и его и следует использовать при малом объёме функционала.
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?
+4
Методы типа #is_whatever? это не ruby-way. В руби уже есть "?", поэтому префикс is_ излишен.
+3
возможно вы созрели до DCI (data context interaction ) — новая парадигма от создателя MVC :")
0
Видимо это и есть DCI в чистом виде
0
не совсем. в dci есть понятие роли, которую обект динамически принимает в определенном контексте и которая есть вместилище поведения. Приведенный пример как раз в относительно грязном виде (с точки зрения dci) илюстрирует эту парадигму :) mikepackdev.com/blog_posts/24-the-right-way-to-code-dci-in-ruby/
0
А можете пояснить в чем же все-таки разница? Только в том, что мы явно не выделили класс-контекст? Это же принципиально ничего не меняет — в роли контекста у нас метод контоллера. Если юз-крейс где-то еще понадобится, мы его можем безболезненно выделить. YAGNI так сказать.
0
упс. а это перевод :)
0
Извращенец
-3
Only those users with full accounts are able to leave comments. Log in, please.
Я влюбился в DelegateClass