このようなコードは「列車事故」(デメテルの法則に違反)ですか?


23

私が書いたいくつかのコードを見てみると、次の構成要素に出会い、考えさせられました。一見、十分きれいに見えます。はい、実際のコードでは、getLocation()メソッドには、取得する場所を正確に説明する、より具体的な名前があります。

service.setLocation(this.configuration.getLocation().toString());

この場合、serviceは、メソッド内で宣言された既知の型のインスタンス変数です。this.configurationクラスコンストラクターに渡されたものであり、特定のインターフェイス(パブリックgetLocation()メソッドを必須とする)を実装するクラスのインスタンスです。したがって、式の戻り値の型this.configuration.getLocation()は既知です。特にこの場合には、それがあるjava.net.URLのに対し、service.setLocation()望んでいますString。2種類の文字列とURLが直接の互換性はありませんので、いくつかの変換の並べ替えは、丸い穴に四角いペグに合わせて必要とされます。

しかし、中に引用されたようにデメテルの法則によればクリーンコード、メソッドFクラスでCが唯一のメソッドを呼び出す必要がCによって作成または引数として渡されるオブジェクトF、およびインスタンス変数に保持されているオブジェクトC。それを超えるもの(toString()メソッド呼び出し自体の結果として作成された一時オブジェクトを考慮する場合を除き、上記の特定のケースでは最後です。この場合、法律全体が議論の余地がないようです)。

上記の制約が与えられた場合、上記のような呼び出しが推奨されない、または拒否されるべきである正当な理由はありますか?それとも、私は過度に細心の注意を払っていますか?

パラメーターとして渡されたオブジェクト(などによって返されるオブジェクト)URLToString()を単に呼び出し、結果を返すメソッドを実装する場合、まったく同じ結果を得るために呼び出しをラップすることができます。効果的には、変換を1ステップ外に移動するだけです。それはどういうわけかそれを受け入れられるでしょうか?(思わないそのすべてが少し動き回る事があるので、それは、任意の違いのいずれかの方法を作るべきではないことを、直感的に、私には。しかし、デメテルの法則の手紙で行く引用として、それはI以来、許容可能ですその後、関数のパラメーターを直接操作します。)toString()URLgetLocation()getLocation()

これがtoString()標準型を呼び出すよりも少しエキゾチックなものである場合、違いが生じるでしょうか?

答えるときserviceは、変数が属する型の動作またはAPIを変更することは実用的ではないことに注意してください。また、議論のために、戻り値の型を変更することgetLocation()も非現実的であるとしましょう。

回答:


34

ここの問題の署名ですsetLocation。それはだstringly型付けされました

詳しく説明すると、なぜそれが期待されるのStringでしょうか?A String、あらゆる種類のテキストデータを表します。有効な場所以外の可能性があります。

実際、これは疑問を提起します:場所とは何ですか?あなたのコードを見ずにどうやっ知るのですか?もしそうならURL、私はこの方法が何を期待するかについてもっと多くを知っているでしょう。
たぶん、それがカスタムクラスであることがより理にかなっているでしょうLocation。OK、最初はそれが何であるかわかりませんが、ある時点で(おそらくthis.configuration.getLocation()、このメソッドが返すものを理解するのに少し時間がかかるでしょう)。
確かに、どちらの場合でも、予想されることを理解するために他の場所を探す必要があります。ただし、後者の場合、a Locationが何であるかを理解すれば、APIを使用できます。前者の場合、a Stringが何であるか(これが予想される)を理解すれば、APIが何を期待しているのかまだわかりません。

ありそうもないシナリオでは、場所があらゆる種類のテキストデータであるということは、テキスト表現を持つあらゆる種類のデータに再解釈します。事実を考えると、これにObjecttoString方法がありますが、コードのクライアントにかなりの信頼を要求しますが、それでもかまいません。

また、これはあなたが話しているJavaであり、設計上ほとんど機能がないことを考慮する必要があります。それtoStringが最後に実際にを呼び出すことを強制するものです。
たとえば静的に型指定されているC#を使用する場合、暗黙のcastの動作を定義することで、実際にその呼び出しを省略することができます。
Objective-Cのような動的に型付けされた言語では、値が文字列のように振る舞う限り誰もが満足するため、実際に変換も必要ありません。

最後の呼び出しtoStringは、Javaの明示性に対する要求によって実際に生成されたノイズよりも、呼び出しよりも少ないと主張することができます。あなたは、メソッドを呼び出している任意の Javaオブジェクトがあり、したがって、あなたは実際には「遠い単位」についての知識をコードしないことにより、最小知識の原則に違反していません。何getLocationが返されても、toStringメソッドがないという方法はありません。

しかし、文字列を実際に最も自然な選択でない限り(または列挙型さえも持たない言語を使用している場合を除き...)、文字列を使用しないでください。


私はあなたに同意する傾向がありますが、彼はすでにサービスAPIを変更できないと言っています。
11

1
@jhocking:彼はできないとは言わなかった。彼は非現実的だと言った。同意しません。API設計の欠陥を回避するコードにベストプラクティスを適用することは、そのような回避策が唯一の可能なオプションである場合にのみ意味があります。ただし、ここではAPIを直接設定するのが最適なオプションです。欠陥を取り除くことは、それらを回避するよりも常に望ましいことです。
-back2dos

とにかく「+1」は、「Javaの明示性に対する要求によって実際に生成されるノイズよりも呼び出しが少ない」
11

1
"stringly typed"の場合でも、これに+1を付けます。私のコードでは、可能な限り意味/意図を表現する型を渡そうとしていますが、サードパーティのライブラリとAPIを使用する場合、そのAPIの作成者が決めたものを使用することで動けなくなることがあります。そして、IIRCは、場所実際には「あらゆる種類のテキストデータ」である可能性がありますが、私が取り組んでいる実装では、URL以外の場所はまったく意味がありません。
CVn

1
APIの変更に関して。これはコンピューターソフトウェアであるため、事実上常に変更が可能です。(他に何もない場合は、常に抽象化レイヤーを作成できます。)しかし、そうすることは、正当化するために短期的および長期的に多大な労力を必要とする場合があります。次に、そのような変更を行うことは完全に可能かもしれませんが、それでも非実用的です。
CVn

21

デメテルの法則は設計指針であり、宗教的に従うべき法則ではありません。

クラスが十分に分離されていると感じるthis.configuration.getLocation()場合、特にあなたが言うように、APIの他の部分を変更することが非現実的である場合、行に何も問題はありません。

デメテルの法則を細かくしても、クライアントが望むものを時間通りに届ける限り、クライアントは完全に幸せになると確信しています。これは、悪いソフトウェアを作成する言い訳ではありませんが、ソフトウェアを開発するときは実用的であることを思い出させてください。


1
以来this.configuration知られているインターフェースのタイプのインスタンス変数であり、そのインターフェイスによって定義され、その上にメソッドを呼び出しても厳密な解釈によれば、微細思われます。はい、KISS、SOLID、YAGNIなどと同様に、それがガイドラインであることを知っています。一般的なソフトウェア開発には、実際には(法律的な意味での)「法律」はほとんどありません。
CVn

4
1 ;-)実用的であるために
TREB

1
私はこのような場合にも痴呆の法則が適用されるべきだとは思わない-構成は基本的にコンテナです。私が今までコンテナの内部を調べたことがあるすべてのAPIで、通常の動作が期待されています。
ローレンペクテル

2

このようなコードを書かないと考えることができる唯一のことは、もしthis.configuration.getLocation()nullが返されたらどうなるでしょうか?これは、周囲のコードと、このコードを使用する対象ユーザーによって異なります。しかし、マルコが言うように-デメテルの法則は経験則です-それは従うのが良いですが、不必要にそうすることであなたの背中を壊さないでください。


場合this.configuration.getLocation()はnullを返します、そして我々は、いずれかの()は、最も可能性の高い遠く、または(b)何かの壊滅的な私はと思われる場合には、その間に起こったことを得なかったでしょうしたいコードが失敗します。したがって、これは一般的に間違いなく有効なポイントですが、この特定のケースでは、適用しないと言ってもかなり安全です。また、このすべてをはるかに超えるものが、この種の予期しない障害に対処するために特別に設計された例外ハンドラです。
CVn

2

デメテルの法則を厳密に従うとは、構成オブジェクトに次のようなメソッドを実装する必要があることを意味します。

function getLocationAsString() {
  return getLocation().toString();
}

しかし、個人的には、これは非常に小さな状況であるため気にしません。とにかく、変更できないAPIのためにあなたの手はちょっと縛られています。プログラミングルールは、選択の余地があるときに何をすべきかに関するものですが、選択の余地がない場合もあります。


1
:それはここで提案同じだc2.com/cgi/wiki?TrainWreckは、教えてくれ「これは、次の目的の動作を表しており、何をすべきかをクライアントに伝えますメソッドを作成します『』原則」を聞かない
heltonbiker
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.