コード臭:自動プロパティ

これは、 ポカヨークデザインに関するシリーズの3番目の投稿です -カプセル化とも呼ばれます。



自動プロパティは、C#で最も冗長な機能の1つです。 多くの人がとても気に入っていることを知っていますが、彼らはあなたが遭遇してはならない問題を解決します。



このように見えるコードは完全に冗長であることに完全に同意します。

private string name; public string Name { get { return this.name; } set { this.name = value; } }
      
      





ただし、この問題の解決策は、このコードを次のように書き換えないことです。

 public string Name { get; set; }
      
      





最初の例のコードの問題は、その儀式ではありません。 問題は、カプセル化を破ることです。 実際に

「[...]ゲッターおよびセッターは、カプセル化または隠蔽情報を提供しません。これらは、プログラミング言語によって合法化されているこれらの原則に違反する方法です。」

ジェームズO.コプリエン&ゲルトルートビョルンヴィグ。 リーンアーキテクチャ。 ワイリー。 2010. p。 134。


私は、自動プロパティには用途があると個人的に信じていますが、見つけることはほとんどありません。 これらは参照型には決して適しておらず、値型に適用されることはほとんどありません。



コードのにおい:参照型の自動プロパティ

まず、参照型によって展開される非常に大きなプロパティのセットを見てみましょう。

参照型の場合、可能な値はnullです。 ただし、Poka-yokeデザインでは、nullは適切な値にはなりません。NullReferenceExceptionsにつながるためです。 Null Objectパターンは、値が未定義の場合に対処するための最良の代替手段です。

つまり、Nameプロパティなどの自動プロパティは適切ではありません。 セッターは、null値(および、場合によっては他の無効な値)から自身を保護するために、ある種の保護式を持たなければなりません。 これは堅実な例です。

 private string name; public string Name { get { return this.name; } set { if (value == null) { throw new ArgumentNullException("value"); } this.name = value; } }
      
      





または、ガード式でnullをチェックし、デフォルト値を提供することもできます。

 private string name; public string Name { get { return this.name; } set { if (value == null) { this.name = ""; return; } this.name = value; } }
      
      





ただし、ゲッターが割り当てられた値以外の値を返すことがあるため、この実装にはPOLA違反が含まれます。 この例外的なケースでセッターからnullが返されるように名前がnullであったかどうかを示すブール型の関連フィールドを追加することでこの問題を修正できますが、これはコードに異なる「におい」をもたらします。



コード臭:値型の自動プロパティ

プロパティタイプが値タイプの場合、値をnullにすることはできないため、ケースの明確性は低下します。 これは、nullのゲートキーパーが適合しないことを意味します。 ただし、単純な型値の消費も不適切な場合があります。 実際、 クラスがこのタイプの値を意識的に受け入れて処理できるのは許容されるだけです。

たとえば、クラスが実際にすべての可能な値の特定のサブセットでのみ機能する場合、保護表現を導入できます。 例を考えてみましょう:

 public int RetryCount { get; set; }
      
      





このプロパティを使用して、特定の操作を実行するための試行回数を設定できます。 問題は、自動プロパティで負の値を設定できることであり、これは無意味です。 考えられる解決策の1つは、セキュリティ式を追加することです。

 private int retryCount; public int RetryCount { get { return this.retryCount; } set { if (value < 0) { throw new ArgumentOutOfRangeException(); } this.retryCount = value; } }
      
      





ただし、多くの場合、プリミティブ型プロパティの開示は、「プリミティブへの執着」の場合がほとんどです。



改訂されたデザイン:保護表現

前に説明したように、自動プロパティの問題を修正する最も速い方法は、保護式を使用してプロパティを実装することです。 これにより、クラスの不変式が適切にカプセル化されます。



改訂された設計:値型プロパティ

自動プロパティが値型である場合、防御的な式の実装は依然として意味があります。 ただし、プロパティが「プリミティブへの執着」の真の症状である場合、最適な代替策は正しい値オブジェクトを導入することです。

例として、次のプロパティを検討してください。

 public int Temperature { get; set; }
      
      





これは、いくつかの理由で貧弱な設計です。 測定単位の意味とは関係なく、無制限の値を割り当てることができます。 -100が割り当てられた場合はどうなりますか? ユニットが摂氏の場合はすべて問題ありませんが、ケルビンの場合は間違いがあります。 ユニットに関係なく、int.MinValueを割り当てようとすると失敗します。

新しいタイプの温度を導入し、プロパティのタイプを入力したものに変更することにより、より持続可能な設計を実現できます。 不変式を保護するだけでなく、新しいクラスは異なる温度測定間の変換をカプセル化することもできます。

ただし、値オブジェクトが参照型として実装される場合、状況は上記の状況と同等になり、nullのチェックが必要になります。 値オブジェクトが値型として実装されている場合、自動プロパティの使用が適切です。

結論:自動プロパティが適切なことはめったにありません。 実際、プロパティタイプが値タイプであり、すべての推定値が有効な場合にのみ有効です。 自動プロパティが適切な場合がいくつかあるため、それらの使用を完全に除外することはできませんが、それらを使用する必要があることを、さらなる調査の理由と見なすべきです。 自動プロパティを使用することは、コードでは「臭い」ですが、アンチパターンではありません。

プロパティはDemeter法則にも違反する可能性があることは注目に値しますが、これは今後の投稿のトピックです。



All Articles