使用收藏品时遵循得墨忒耳法则?

时间:2013-02-02 21:14:59

标签: ruby-on-rails design-patterns language-agnostic law-of-demeter

在Ruby on Rails(或带有集合的任何其他语言......)中,在查询像计数一样简单的事情时,是否有必要打破Law of 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

2 个答案:

答案 0 :(得分: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对象并查询其关联,从而避免了Demeter违规法。这也允许您稍后更改first_survey?的定义。一个人为的例子是Surveys可以“发布”。然后,可以轻松更改first_survey?的{​​{1}},以仅支持检查传入的Kingdom是否是Survey属性设置为{Survey的{​​{1}} {1}}。

答案 1 :(得分:0)

Definition from Wikipedia

  

更正式地说,Demeter法则的功能需要一种方法   对象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
  

特别是,对象应该避免调用成员的方法   另一个方法返回的对象。对于许多现代面向对象   使用点作为字段标识符的语言,可以说明法律   简单地说“只使用一个点”。也就是说,代码a.b.Method()中断了   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]',' - ')不违反   蒂美

好吧,自从你提出来之后,我会反驳你的陈述。它实际上并不是一个糟糕的规则,因为你在这里忽略了一条非常关键的信息,strip函数返回一个新的字符串对象以及像tr_s一样的downcase。如果你要生成三个额外的对象,我会非常担心任何开发人员看到它。它确实违反了规则,因为您直接访问了会员。你想要考虑什么,我决不是一个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,因为我正在调用myString暴露的API,过度杀伤(可能),最正确(几乎肯定)。