C#6.0の新しいnull条件演算子は、Demeterの法則に反しますか?


29

デメテル法則は次のように述べています:

  • 各ユニットは、他のユニットに関する限られた知識のみを持つ必要があります。現在のユニットに「密接に」関連するユニットのみです。
  • 各ユニットは、その友人とのみ会話する必要があります。見知らぬ人と話をしないでください。
  • 身近な友達とだけ話してください。

C#6.0では、null-conditional operatorと呼ばれる新しい演算子が導入されました。私見、それはコーディングを容易にし、読みやすさを向上させます。ただし、クラスフィールドをナビゲートするのが簡単で、すでにヌル(のようなものvar x = A?.B?.C?.D?.E?.F?)をチェックしているため、より結合されたコードを簡単に記述できます。

この新しいオペレーターがデメテルの法則に反すると述べるのは正しいですか?


2
なぜあなたはそれA?.B?.C?.D?.E?.F?を違反すると思うのですか?LoDはドットの数ではなく、呼び出し元のメソッドがそのポイントに違反していない構造に関する情報を持っている場合、そのような呼び出しは完全に受け入れられます。そのようなコード LoDに違反する可能性があるだけでは、そのコードのすべての使用がLoDに違反すると言えません。

14
デメテルの法則はドットカウント運動ではありません」を読んでいますか?この正確な例を説明します。
outis

@outis:優れた読み取り。の形式のすべてのコードがX.Y.Z.W.U「法律」に違反していると言っているわけではありません。しかし、コードを扱った私の経験では、90%の時間は単なる見苦しい結合コードです。
アーサーリッツォ

2
@ArthurRizzoしかし、それはLoDに反するヌル条件演算子の問題ではありません。それが障害のあるコードです。オペレータは、人間がそれを読むことを単純化するための単なるツールです。.?より多くないのLoDに違反しません+-ん。

1
RC Martinは、純粋なデータクラスと動作クラスを区別します。アクセスされたプロパティが動作クラスの内部データを公開している場合、スニペットは確かにLoDに違反していますが、これはnull条件演算子とは関係ありません。とにかく、プロパティは内部データを公開するようにバインドされていません。これは臭いかもしれませんが、LoDに違反しません。RC Martinによれば、スキーマは純粋なデータクラスで完全に有効である可能性があります。
ポールケルチャー

回答:


44

この新しいオペレーターがデメテルの法則に反すると述べるのは正しいですか?

いいえ*


* null条件演算子は、言語および.NETフレームワーク内のツールです。 どのツールも、特定のアプリケーションの保守性を損なう可能性のある方法で悪用され、使用される可能性があります。

しかし、ツール悪用される可能性があるという事実は、必ずしもツール悪用されなければならないということでなく、ツールが保持されている特定の原則に違反していることも意味しません。

デメテルの法則などは、コードの記述方法に関するガイドラインです。ツールではなく、人間を対象としています。そのため、C#6.0言語に新しいツールが含まれているという事実は、コードの記述方法と構造化方法に必ずしも影響を与えません。

新しいツールを使用する場合は、コードを保守することになった人が暴力的なサイコパスになった場合に...として評価する必要があります。繰り返しますが、これはコードを書いている人へのガイダンスであり、使用されているツールに関するものではありません。


foo = new FiveDMatrix(); foo.get(0).get(0).get(0).get(0).set(0,1);それは問題ありません(そして悪化することはありませんfoo[0][0][0][0][0] = 1)...そして、それがLoDに違反しない他の多くの状況です。

@MichaelTその次元のマトリックスを取得し始めると、インデックスをベクトル/タプル/配列自体として扱い、データが実際に格納される方法をマトリックスクラスの内部に心配させることが容易になるようです。(これについて考えると、少なくともカプセル化に関するデメテルの法則が適用されます。)
JAB

(そしてもちろん、この種の実践により、多次元スライスの実装が容易になり、いくつかの本当に強力なマトリックスツールを使用できます。)
JAB

1
@JAB私はちょうど例を考え出そうとしていました。より良いものはおそらくありますDom file = prase("some.xml"); file.get(tag1).getChild().get(tag2).getChild() ...-それはいくつかの愚かなコードの構造を処理する問題です。それは見知らぬ人ではない...そのちょうど愚かな。.?となり非常にこのような構造に有用。

10

並べ替え。

1回のみアクセスする場合(a?.Foo)は、次と同等です:

a == null ? null : a.Foo

ほとんどの人が同意することは、デメテルの法則に違反していないことです。その時点では、読みやすさを向上させるのは単なる構文糖です。

それ以上のものは、おそらくデメテルの法則に違反するでしょう。この機能は、そのような使用法を促進する傾向があります。上記の「良い」使用法だけでは、この種の言語の変更を保証するのに十分ではないと言えるでしょう。

とはいえ、デメテルの法則はそれ自体が法則ではなく、むしろガイドラインであることを覚えておく価値があります。多くのコードがこれに違反しており、うまく機能しています。設計やコードの単純さは、デメテルの法則に違反することによってもたらされるリスクよりも価値がある場合があります。


何がより多くのそれよりも必然のLoD例えばBuilderパターン破壊しない
JKを。

@Telastyn:私たちが話している新しい言語構文はありません支援メソッド呼び出し:a?.Func1(x)?.Func2(y) 演算子を合体nullは何か他のものです。
ベンフォークト

@BenVoigt-ああ、私はそれがフィールド、プロパティ、インデクサーでのみ機能することを示した記事を辞めていました。テストに便利なMSVS2015がありませんでした。あなたが正しいです。
テラスティン

1
a?.Fooはa == nullとまったく同等ではありませんか?null:a.Foo。前者は1回だけ評価し、後者は2回評価します。aがイテレータである場合、それは重要です。
ローレンペクテル

9

いいえ。それ自体の演算子と、それに対する重く連鎖した使用の両方を考えてみましょう。

それ自体.?Aは、左値が存在するクラスの知識と、メソッドによって返される型の知識の量と同じ.A != nullです。Aプロパティが存在することを知り、と比較できる値を返す必要がありますnull

型付けされたプロパティがそうである場合、これはデメテルの法則に違反しているとのみ主張できます。A具象型として強制されることさえありません(その値は派生型である可能性があります)。ここでの結合は最小限です。

考えてみましょうvar x = A?.B?.C?.D?.E?.F

つまり、Anullになる可能性のあるタイプ、またはnullになる可能性のあるタイプ、またはBプロパティを持つ可能性のあるタイプである必要があり、CプロパティのタイプEがnullまたはFプロパティを持つことができます。

つまり、静的に型付けされた言語でこれを行うか、型付けが緩い場合に返される型に制約を適用する必要があります。ほとんどの場合、C#は静的型付けを使用するため、何も変更していません。

その場合、次のコードも法律に違反します。

ExplicitType x;
var b = A.B;
if (b == null)
  x = null;
else
{
  var c = b.C;
  if (c == null)
    x = null;
  else
  {
    var d = c.D;
    if (d == null)
      x = null;
    else
    {
      var e = d.E;
      if (e == null)
        x = null;
      else
        x = e.F;
    }
  }
}

これはまったく同じ。異なる要素のカップリングを使用するこのコードは、カップリングの完全なチェーンを「知る」必要がありますが、そうするためにデメテルの法則に違反しないコードを使用しており、各ユニットは次。


3
+1新しい演算子は、あなたが説明した苦いレシピの単なる構文上の砂糖です。
ロスパターソン

1
まあ、開発者がそのようなコードを書いたら、何かが正しくないことに気づくのは簡単だと思います。演算子は100%の合成シュガーであることは知っていますが、それでも、var x = A?.B?.C?.D?.E?.F最終的には同じであっても、人々はすべてのif / elsよりも快適なものを書く傾向があると思います。
アーサーリッツォ

2
A?.B?.C?.D?.E?.F間違っている可能性が少ないため、何かが正しくないことに気付きやすくなります。Fそのパスを介して取得しようとする必要がありますか、またはそうすべきではありませんが、長いフォームにはエラーが含まれる可能性があります。
ジョンハンナ

@ArthurRizzoしかし、上記の種類のコードをLoD違反に関連付けると、nullチェックが必要ない場合に見逃しやすくなりますA.B.C.D。かなり無関係な詳細(ヌルチェック)に依存する2つの異なるものよりも、1つのもの(チェーンプロパティアクセス)を探す方がはるかに簡単です
ベンアーロンソン

5

オブジェクトは、ビヘイビアをカプセル化したりデータを保持したりする目的で作成でき、オブジェクトは外部コードと共有したり、作成者が個人的に保持したりする目的で作成できます。

動作をカプセル化する目的(共有されているかどうか)、または外部コードと共有されている(動作またはデータをカプセル化する)ために作成されたオブジェクトは、通常、表面インターフェイスからアクセスする必要があります。ただし、データ保持オブジェクトが作成者のみが使用するために作成された場合、「ディープ」アクセスを回避するための通常のデメテルの法則の理由は適用されません。オブジェクト内のデータを格納または操作するクラスの一部が、他のコードの調整を必要とする方法で変更された場合、そのようなコードはすべて更新されることを保証することができます-上記のように、オブジェクトは1つのクラスの排他的使用。

と思いながら?演算子はおそらくより適切に設計されている可能性があります。オブジェクトがネストされたデータ構造を使用する十分な状況があります。LoDに違反する可能性があるという事実は、「。」より悪くないため、演算子に対する引数として受け取られるべきではありません。その点で演算子。


デメテルの法則がオブジェクトを保持するデータに適用されないのはなぜですか?
テラスティン

2
@Telastyn:LoDの目的は、他の何かが操作または気にする可能性のある内部オブジェクトにコードがアクセスした場合に発生する可能性のある問題を回避することです。宇宙の他の何物も内部オブジェクトの状態を操作したり、気にかけたりすることができなければ、そのような問題から守る必要はありません。
supercat

同意するかどうかわかりません。他の物がデータの変更を気にするわけではなく、パスを介して含まれているオブジェクトに結合していることです(2つのオブジェクトとそれらの関係の3つの結合ポイント)。時々、そのカップリングは大したことではないかもしれませんが、それでも私には臭いのようです。
テラスティン

@Telastyn:パスについてのあなたのポイントは良いものですが、私のポイントはうまくいくと思います。複数のパスを介してオブジェクトにアクセスすると、それらのパス間の結合が作成されます。一部のアクセスが浅いパスを介している場合、深いパスを介したアクセスも望ましくない結合を引き起こす可能性があります。ただし、すべてのアクセスが1つの特定のディープパスを介している場合、そのディープパスが結合するものは何もありません。
supercat

@Telastynデータ構造を走査して、データを深く掘り下げても問題ありません。ネストされたメソッド呼び出しとは異なります。データ構造とそれがどのようにネストされているかを知る必要がある場合がありますが、サービスや独自のネストされたサービス/リポジトリなどのオブジェクトについては同じではありません。
PerHornshøj-Schierbeck15年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.