始终使用关联的模型子类

时间:2018-09-20 22:42:35

标签: ruby-on-rails model ruby-on-rails-5

在某些情况下,我具有要向其中添加业务逻辑的基本模型。例如,我可能有这样的事情。

class User {
    String firstname;
    String lastname;
    @JsonAdapter(NullAdapter.class)
    JsonElement opaqueData;
}

class NullAdapter extends TypeAdapter<JsonElement> {

    @Override
    public void write(JsonWriter out, JsonElement value) throws IOException {
        out.jsonValue(new GsonBuilder().serializeNulls().create().toJson(value));
    }

    @Override
    public JsonElement read(JsonReader in) throws IOException {
        // TODO Auto-generated method stub
        return null;
    }

}

通过常规关联方法,订阅和取消订阅都很容易。

class List < ApplicationRecord
  has_many :subscriptions
  has_many :subscribers, though: :subscriptions
end

class Subscriber < ApplicationRecord
  has_many :subscriptions
  has_many :lists, through: :subscriptions
end

class Subscription < ApplicationRecord
  belongs_to :list
  belongs_to :subscriber
end

但是有日志记录,跟踪,指标,挂钩和其他业务逻辑。我可以通过回调来做到这一点。但是我想保持基本模型的简单和灵活。我的愿望是将核心功能与额外的业务逻辑分开。现在,这是为了简化测试。最终,我需要在同一核心之上添加两组不同业务逻辑。

当前,我正在使用服务对象来包装具有所有当前业务逻辑的常见操作。这是一个简单的例子,还有很多。

# Subscribe
list.subscriptions.create(
    subscriber: subscriber
)

# Unsubscribe
list.subscriptions.destroy(subscription)

# Unsub from all lists
subscriber.subscriptions.destroy_all

但是我发现它越来越尴尬。例如,我不能使用自然的class SubscriptionManager def subscribe(list, subscriber) list.subscriptions.create( subscriber: subscriber ) log_sub(subscription) end def unsubscribe(subscription) subscription.list.subscriptions.destroy(subscription) log_unsub_reason(subscription) end def unsubscribe_all(subscriber) subscriber.subscriptions.each do |subscription| unsubscribe(subscription) end subscriber.lists.reset subscriber.subscriptions.reset end end ,但必须小心通过SubscriptionManager方法。 as Pattern,此系统导致难以发现错误。

我正在考虑消除SubscriptionManager,而是编写模型中具有附加逻辑的子类。

subscriber.subscriptions.destroy_all

问题是我发现我必须重复所有关联,以确保托管对象与其他托管对象关联。

有没有更好,更少冗余的方法?

1 个答案:

答案 0 :(得分:0)

我不太了解为什么需要在子类中再次定义关联。但是,我有一个提示,您可以直接在您的Subscription模型中使用。

如果您想保持模型简单,并且不使用回调逻辑重载模型,则可以创建一个callback class来包装该模型将使用的所有逻辑。

为此,您需要创建一个类,例如:

class SubscriptionCallbacks

  def self.after_create(subscription)
    log_sub(subscription)
  end

  def self.after_destroy(subscription)
    log_unsub_reason(subscription)
  end

end

然后在Subscription模型中:

class Subscription < ApplicationRecord
  belongs_to :list
  belongs_to :subscriber

  after_destroy SubscriptionCallbacks
  after_create SubscriptionCallbacks
end

这样,您的模型就可以保持整洁,您可以destroy进行订阅并应用所有自定义逻辑,而无需使用服务。

更新

具体来说,我不明白的是,为什么要在三个模型上进行Single Table Inheritance只是为了向其中之一添加回调。您编写问题的方式,对于三个子类,您将覆盖关联以使用所创建的子类。那真的有必要吗?我认为没有,因为您想要实现的只是将服务重构为回调,以便直接在destroy模型中使用destroy_allSubscription,因此我从这里开始:< / p>

  

但是我发现它越来越尴尬。例如,我不能使用自然的subscriber.subscriptions.destroy_all,但必须小心使用SubscriptionManager方法。

使用conditional callbacks也许就足够了,或者甚至可以使用Subscription模型上的普通回调。

我不知道实际的代码是如何编写的,但是我发现使用“单表继承”来添加回调很棘手。那不会使您的模型“简单而灵活”。

更新2

在回调类中,使用要实现的回调的名称定义方法,并将subscription作为参数传递。在这些方法内部,您可以创建所需的所有逻辑。例如(假设给定type属性,您将使用不同的逻辑):

 class SubscriptionCallbacks

   def after_create(subscription)
     if subscription.type == 'foo'
       log_foo_sub(subscription)
     elsif subscription.type == 'bar'
       log_bar_sub(subscription)
     end
   end

   private

   def log_foo_sub(subscription)
     # Here will live all the logic of the callback for subscription of foo type
   end

   def log_bar_sub(subscription)
     # Here will live all the logic of the callback for subscription of bar type
   end

 end 

这可能是很多逻辑,不会在Subscription模型中编写。您可以照常使用destroydestroy_all,如果if else中未定义订阅类型,则不会发生任何事情。

所有回调逻辑都将包含在callback class中,并且您将添加到subscription模型中的唯一代码是:

 class Subscription < ApplicationRecord
   belongs_to :list
   belongs_to :subscriber

   after_create SubscriptionCallbacks.new
 end