1

在 Ruby on Rails(或任何其他具有集合的语言......)中,当查询像计数这样简单的东西时,是否有必要打破Demeter 违反法则?

class Survey
  has_one :kingdom

  def first_survey?
    # Should this be broken according to Law of Demeter?
    self.kingdom.surveys.count == 1
    # -or-
    self.kingdom.surveys_count == 1
  end
end

class Kingdom
  has_many :surveys

  # Is this the "right thing to do", or overkill?
  def surveys_count
    self.surveys.count
  end
end
4

2 回答 2

1

通常当我看到违反德墨忒耳法则时,我问的第一个问题不是“我如何‘避免点’?” 您应该问的问题是“这个违反的功能是否属于其他人?” 在这种情况下,我可能会像这样构造它:

class Survey
  belongs_to :kingdom
end

class Kingdom
  has_many :surveys

  def first_survey?(survey)
    surveys.first == survey
  end
end

kingdom = Kingdom.find(kingdom_id)
first_survey = kingdom.surveys.first
last_survey = kingdom.surveys.last # Assuming there is more than one survey.

kingdom.first_survey?(first_survey) #=> true
kingdom.first_survey?(last_survey)  #=> false

通过以这种方式构造它,Survey不再需要进入Kingdom对象并查询其关联,避免违反得墨忒耳法则。first_survey?这也允许您稍后更改定义。一个人为的例子是如果Surveys可以“发布”。然后可以很容易地将 for a 更改为仅支持检查传入的是否first_survey?是第一个属性设置为 的。KingdomSurveySurveypublishedtrue

于 2013-02-02T23:03:31.827 回答
0

来自维基百科的定义

更正式地说,函数的得墨忒耳定律要求对象 O 的方法 m 只能调用以下类型对象的方法

1. O itself  
2. m's parameters  
3. Any objects created/instantiated within m  
4. O's direct component objects  
5. A global variable, accessible by O, in the scope of m

特别是,一个对象应该避免调用另一个方法返回的成员对象的方法。对于许多使用点作为字段标识符的现代面向对象语言,该法则可以简单地表述为“仅使用一个点”。也就是说,代码 abMethod() 违反了 a.Method() 没有的规律。举个简单的例子,想要狗走路时,不会直接命令狗的腿走路;取而代之的是命令狗,然后狗命令自己的腿。

在上述情况下,是的,这将违反得墨忒耳定律:

 def first_survey?
    # Should this be broken according to Law of Demeter?
    self.kingdom.surveys.count == 1

至于它是否矫枉过正,我说不,但我不喜欢糟糕的代码(即不正确的代码,允许的错误多于应有的错误,我对此有很多争论)。

你想要做的是这样的:

def first_survey?
    self.kingdom.surveys_count == 1

如果我的理解是正确的,这并不违反 Demeter,因为您没有suverys.count直接访问该属性。相反,您要求kingdom返回它拥有的内部属性(我认为这更正确,因为它正在使用对象的 API kingdom

# Is this the "right thing to do", or overkill?
  def surveys_count
    self.surveys.count
  end

这是“正确的做法”,否则外部系统将如何访问该属性?

从评论中

只使用一个点实际上是一个非常糟糕的规则,因为 "string".strip.downcase.tr_s('^[a-z0-9]', '-') 不违反 demeter

好吧,既然你提出来了,我会反驳你的说法。这实际上不是一个坏规则,因为您在这里忽略了一条非常关键的信息,strip 函数返回一个新的字符串对象以及 tr_s 的小写。如果您要生成三个额外的对象,我会非常担心任何看到这一点的开发人员。它确实违反了规则,因为您正在直接访问成员。你想要考虑的,我绝不是 Ruby 专家,是这样的:

class MyString  
{  
     string internal_string;
         def strip()  
         {  
              self.internal_string = self.internal_string.strip
         }  

       def downcase()
       {
         self.internal_string = self.internal_string.downcase
       }  

       def tr_s()
       {  
           self.internal_string = self.internal_string.tr_s
       }  
}  

上面所做的是允许开发人员通过在库函数周围放置一个瘦包装器来调用您的 API。有些人可能会称之为矫枉过正,但它对可以做什么和应该做什么给出了非常明确的界限。现在让这个得墨忒耳得到批准(如果我正确理解法律的话)

myString.strip().downcase().tr_s()

现在可能不清楚的是,这并不违反 Demeter,因为我正在调用公开的 API myString,过度杀伤(也许),最正确(几乎肯定)。

于 2013-02-02T21:23:40.630 回答