3

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

我有以下骨架 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
#...

-或者-

# 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

4 回答 4

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

  2. 在用户模型中 VS 在 Coutry 模型中:用户模型获胜。得墨忒耳法则仅在 Ruby 中建议一个点。如果您有机会这样做,当然最好遵循。

结论:您的第一种方法获胜。

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

加:得墨忒耳法则参考

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/

于 2013-06-30T20:08:25.743 回答
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

现在显然这违反了得墨忒耳法则。因此,最好建议您在 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 世界中没有人关心 Demeter 法则,所以对此持保留态度。

至于将代码放入 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
于 2013-07-02T15:29:23.020 回答
2

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

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

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

另一种看待问题的方式是根据职责(因此链接到 Demeter 上的@robkuz 条目)。一个CountryEntry实例负责提供它的代码(也许还有一些其他的东西)。一个CountryEntry类基本上负责提供其所有实例共有的属性和方法,仅此而已(好吧)。获取逗号分隔的代码列表是对CountryEntry实例的一种特殊用法,显然只有User对象关心。在这种情况下,责任属于当前用户对象。旁观者眼中的价值...

这与线程上的大多数答案是一致的,尽管在到目前为止的解决方案中,您没有得到一个逗号分隔的代码列表,而是一个代码数组。


在性能方面,请注意,由于延迟评估,可能也存在差异。只是一个注释——更熟悉 ActiveRecord 的人可以对此发表评论!

于 2013-07-03T09:11:38.620 回答
0

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

于 2013-06-30T18:17:54.150 回答