1

Ruby on Rails(またはコレクションを含む他の言語...)内で、カウントのような単純なものをクエリするときに、デメテルの法則違反を解消する必要がありますか?

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「公開」できるかどうかです。次にfirst_survey?、for aKingdomを簡単に変更して、渡された属性が。に設定されSurveyた最初のものであるかどうかのチェックのみをサポートすることができます。Surveypublishedtrue

于 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

特に、オブジェクトは、別のメソッドによって返されるメンバーオブジェクトのメソッドを呼び出さないようにする必要があります。フィールド識別子としてドットを使用する多くの最新のオブジェクト指向言語では、法則は単に「1つのドットのみを使用する」と表現できます。つまり、コード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

私の理解が正しければ、suverys.countプロパティに直接アクセスしていないため、これはDemeterに違反しません。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]'、'-')はデメテルの違反ではないため、実際には1つのドットのみを使用することは非常に悪いルールです。

さて、あなたがそれを提起したので、私はあなたの声明に反対します。ここで非常に重要な情報を見落としているため、実際には悪いルールではありません。strip関数は、tr_sと同様に、新しい文字列オブジェクトと小文字を返します。3つの追加オブジェクトを生成する場合、開発者がそれを見るのは非常に心配です。また、メンバーに直接アクセスしているため、実際にはルールに違反しています。あなたが考慮したいと思うこと、そして決して私は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を呼び出しているためです。

于 2013-02-02T21:23:40.630 に答える