ActiveRecord关联 - 在哪里放置功能?

时间:2013-06-30 18:09:48

标签: ruby-on-rails ruby activerecord sinatra

我正在为以下情况寻找一些最佳实践建议。

我有以下骨架ActiveRecord模型:

# user.rb
class User < ActiveRecord::Base
  has_many :country_entries, dependent: destroy
end

# country_entry.rb
class CountryEntry < ActiveRecord::Base
  belongs_to :user
  validates :code, presence: true
end

现在假设我需要为特定用户获取以逗号分隔的CountryEntry代码列表。问题是,我在哪里放这个方法?有两种选择:

# user.rb
#...
  def country_codes
    self.country_entries.map(&:code)
  end
#...

-OR -

# country_entry.rb
#...
  def self.codes_for_user(user)
    where(user_id: user.id).map(&:code)
  end
#...

因此API将是:@current_user.country_codes - 或 - CountryEntry.codes_for_user(@current_user)


似乎将代码放在country_entry.rb中将所有内容分离一点,但它使API变得更加丑陋。关于这个问题的任何一般或个人经验最佳实践?

4 个答案:

答案 0 :(得分:4)

  1. 实例方法VS类方法:如果方法是针对实例的,那么当然最好是实例方法。

  2. 在Coutry模型中的用户模型VS中:用户模型获胜。 Demeter法则仅在Ruby中提出一个点。如果你有机会这样做,当然最好遵循。

  3. 结论:你的第一种方法获胜。

    # user.rb
    def country_codes
      self.country_entries.map(&:code)
    end
    

    添加:Demeter法则参考

    http://en.wikipedia.org/wiki/Law_of_Demeter

    http://rails-bestpractices.com/posts/15-the-law-of-demeter

    http://devblog.avdi.org/2011/07/05/demeter-its-not-just-a-good-idea-its-the-law/

答案 1 :(得分:3)

现在这真是一个有趣的问题。它有很多答案; - )

从你最初的问题我建议你把代码放在协会本身

class User < ActiveRecord::Base
  has_many :country_entries do
    def codes
      proxy_association.owner.country_entries.map(&:code)
    end
  end
end

所以你可以做这样的事情

list_of_codes = a_user.country_entries.codes

现在很明显这违反了Law of Demeter。 因此,建议您最好在User对象上提供一个方法,如此

class User < ActiveRecord::Base
  has_many :country_entries do
    def codes
      proxy_association.owner.country_entries.map(&:code)
    end
  end

  def country_codes
    self.country_entries.codes
  end
end

显然,Rails世界中没有人关心得墨忒耳定律,所以请耐心等待。

至于将代码放入CountryEntry类中,我不知道为什么要这样做。如果您只能向用户查找国家/地区代码,我认为无需创建类方法。无论如何,如果你手边有一个用户,你只能查看该列表。

如果有许多不同的对象可以拥有country_entries关联,那么将它作为类方法放入CountryEntry是有意义的。

我最喜欢的是LOD和类方法的组合,以便重复使用。

class User < ActiveRecord::Base
  has_many :country_entries

  def country_codes
    CountryEntry.codes_for_user(self)
  end
end

class CountryEntry < ActiveRecord::Base
  belongs_to :user
  validates :code, presence: true

  def self.codes_for_user(some_id)
     where(ref_id: some_id).map(&:code)
  end
end

答案 2 :(得分:2)

就API开发人员而言,从两个提案中获得,添加到用户模型似乎非常简单。鉴于问题:

  

现在假设我需要为特定用户获取以逗号分隔的CountryEntry代码列表。

上下文由用户组成,我们想要获取代码列表。自然的“入口点”似乎是一个用户对象。

另一种看待问题的方法是在责任方面(因此链接到Demeter的@robkuz条目)。 CountryEntry实例负责提供其代码(可能还有一些其他内容)。 CountryEntry类基本上负责提供其所有实例共有的属性和方法,而不是(好)。获取逗号分隔代码列表是CountryEntry个实例的专用用法,只有User个对象显然需要关注。在这种情况下,责任属于当前用户对象。旁观者眼中的价值......

这与线程上的大多数答案都是内联的,虽然在目前为止的解决方案中,您没有得到以逗号分隔的代码列表,而是一组代码。


就性能而言,请注意,由于懒惰评估,可能存在差异。只是一个注意事项---对ActiveRecord更熟悉的人可以对此发表评论!

答案 3 :(得分:0)

我认为@current_user.country_codes在这种情况下是更好的选择,因为它会更容易在您的代码中使用。