我正在按照"胖模型/瘦控制器"开发Rails 5应用程序。图案。当我添加诸如记录和验证之类的东西时,我发现我的模型变得有点过于肥胖。例如,这里是订阅列表的方法的草图......
class SubscriberList < ApplicationRecord
# relationships and validations
def subscribe!(args)
log that a subscription is attempted
begin
do the subscription
rescue errors
log the failure and reason
rethrow
end
log successful subscription
log other details about the subscription
SubscriptionValidationJob.perform_later( new_subscriber )
return new_subscriber
end
end
它越来越多地将日志记录和验证与订阅行为联系起来。我理解我应该通过使用decorators将日志记录和验证移动到draper
来解决这个问题。
我对装饰师没有多少经验。我主要担心的是由于代码使用未修饰的模型时应该使用装饰模型的错误。或相反亦然。接口是相同的,变化是副作用,因此很难检测。
我很想大量使用decorates_association
和decorates_finders
来避免这种情况,但Draper文档说要避免这种情况......
装饰器应该与它们装饰的模型非常相似,因此在控制器动作开始时装饰对象非常诱人,然后在整个过程中使用装饰器。唐&#39;吨
然而,Draper(以及我能找到的大多数Rails + Decorator文章)似乎都专注于视图功能......
因为装饰器被设计为由视图使用,所以您应该只在那里访问它们。操作模型以准备好事物,然后在渲染视图之前的最后一刻进行装饰。这避免了在创建装饰器之后尝试修改装饰器(特别是集合装饰器)时出现的许多常见陷阱。
与视图功能不同,你有一个控制器来确保传递给视图的模型被装饰,我的装饰器用于模型功能。装饰器主要是用于代码组织和易于测试,几乎所有东西都应该使用装饰模型。
使用装饰器添加到模型功能的最佳做法是什么?总是使用装饰模型?更像是将订阅和取消订阅转移到另一个类中更激进了吗?
答案 0 :(得分:1)
我认为这不适合装饰者。在rails装饰器中,主要使用视图中使用的表示逻辑来包装模型对象。它们作为单个对象的扩展,可以让您在逻辑上分离对象的不同任务。
例如:
class User
def born_on
Date.new(1989, 9, 10)
end
end
class UserDecorator < SimpleDelegator
def birth_year
born_on.year
end
end
当谈到像多个对象交互的操作时,装饰器不适合。
您应该关注的是服务对象模式,您可以在其中创建执行单个任务的单一目的对象:
class SubscriptionService
attr_accessor :user, :list
def initialize(user, list)
@user = user
@list = list
end
def self.perform(user, list)
self.new(user, list).perform
end
def perform
@subscription = Subscription.new(user: @user, list: @list)
log_subscription_attempted
if @subscription.create
send_welcome_email
# ...
else
log_failure_reason
# ...
end
@subscription
end
private
def send_welcome_email
# ...
end
def log_subscription_attempted
# ...
end
def log_failure_reason
# ...
end
end
但是你也应该考虑你是否正确地编写模型。在此示例中,您希望将三个模型互连为:
class User
has_many :subscriptions
has_many :subscription_lists, through: :subscriptions
end
class Subscription
belongs_to :user
belongs_to :subscription_list
validates_uniqueness_of :user_id, scope: :subscription_list_id
end
# or topic
class SubscriptionList
has_many :subscriptions
has_many :users, through: :subscriptions
end
每个模型应该处理应用程序中的一个单独的实体/资源。因此,SubscriptionList
模型不应直接参与订阅单个用户。如果你的模型变得很胖,那么可能表明你太过于过分地使用太少的业务逻辑对象,或者数据库设计设置不当。