2009-07-13 9 views
2

クラスを設計するときに、有効な状態を維持するロジックをクラスに組み込むべきか、その外部に組み込むべきですか?つまり、無効な状態(値が範囲外など)で例外をスローするか、クラスのインスタンスが構築/変更されているときにこの検証を実行する必要がありますか?C#/オブジェクト指向設計 - 有効なオブジェクト状態を維持する

+0

詳細については、この質問を参照してください:http://stackoverflow.com/questions/88541/business-objects-validation-and-exceptions – Mac

答えて

13

これはクラスに属します。クラスそのもの(およびそれが委任するヘルパー)は、有効または無効の状態を決定する規則を知っているか、またはそれに関係している必要があります。

+0

ありがとうございました。この場合、try ... catch ...のインスタンス化を取り囲むか、「正常に」失敗しますか?例: try {タイプmyType = newタイプ(aValue); } catch(InvalidException e){..}または何らかの「安全」なデフォルトに設定しますか? –

+3

いいえ、それは優雅な失敗ではない、それは嘘です。それについて何かができるまで例外を残しておいてください。 –

+0

さて、私はこれを取得し始めていると思います。私は、 "安全な"デフォルトがうそではなく、try..catchであると仮定していますか? try..catchの場合、私は例外を "食べていません"、私はちょうど処理を残して入れて.. –

4

はい、プロパティは設定時に有効/無効の値をチェックする必要があります。それがそれのためのものです。

2

これは一般にクラス自体に属しますが、ある程度は「有効」という定義にも依存しなければなりません。たとえば、System.IO.FileInfoクラスを考えてみましょう。存在しなくなったファイルを参照している場合は有効ですか?どのようにそれを知っていますか?

3

外部のコードに関係なく、クラスを無効な状態にすることは不可能です。それはそれを明確にすべきです。

一方、それ以外のコードでは、クラスを正しく使用する責任があるため、頻繁に2回チェックすることは意味があります。クラスのメソッドは、好きでないものが渡された場合はArgumentExceptionを投げてしまう可能性があります。呼び出しコードは、入力などを検証するための適切なロジックを用意することで、これが起こらないようにしなければなりません。

さらに複雑ですシステムに関係するクライアントの「レベル」が異なるケース。例はOSです。アプリケーションは「ユーザーモード」で動作し、OSを無効な状態にすることはできません。しかし、ドライバは「カーネルモード」で動作し、アプリケーションによって使用されるサービスを実装するチームの一員であるため、OSの状態を完全に破損する可能性があります。

この種のデュアルレベル配置は、オブジェクトモデルで発生する可能性があります。有効な状態のみを表示するモデルの「外部」クライアントと、それ以外の場合は「無効な」状態と見なすことができる必要がある「内部」クライアント(プラグイン、拡張機能、アドオン)状態遷移の実装に果たす役割があるからです。無効/有効の定義は、クライアントが果たしている役割によって異なります。

+1

ウィジェットを持つエンティティにもガジェットとその逆。両方を同時に(通常は)割り当てることはできないので、少なくとも短期間は無効な状態で生きなければなりません。ほとんどの場合、**無効な状態を維持することはできません。エフェメラルオブジェクトは、格納されていない限り無効です。 – tvanfosson

+1

これは、2つの引数を取るメソッドを定義することができない仮想的な言語ですか? –

+1

@Earwicker - クラスのデータは通常、メソッドではなくプロパティのアクセサーを介して設定されるC#という質問があることに注意してください。有効な状態を維持するために、関連するすべてのプロパティを同時に設定するメソッドが必要であると主張していますか?メソッドの実行中にいくつかの中間の無効状態を許可すると仮定します。これはかなり厄介な要件であると思われます。外部のエンティティに、クラスメソッドと同じ方法で有効なオブジェクトを構築させ、オブジェクトが永続化するまで検証を延期させるのはなぜでしょうか? – tvanfosson

1

私は@Joelに同意します。典型的には、これはクラス内に見出される。しかし、私はプロパティアクセサーが検証ロジックを実装することはありません。むしろ、オブジェクトが永続化されているときに永続化レイヤーを呼び出すための検証メソッドをお勧めします。これにより、検証ロジックを単一の場所にローカライズし、実行される永続化操作に基づいて有効/無効の異なる選択肢を作成できます。たとえば、データベースからオブジェクトを削除する予定がある場合、そのプロパティの一部が無効であることに気をつけますか?おそらく、IDと行のバージョンがデータベースのバージョンと同じである限り、先に進んで削除するだけです。同様に、挿入と更新のルールが異なる場合があります。たとえば、一部のフィールドは挿入時にnullになる可能性がありますが、更新時には必要です。

+0

これはPaul Stovell氏のステークで、彼はこの記事で要約しました。http://www.codeproject.com/KB/cs/DelegateBusinessObjects.aspx – Mac

0

によって異なります。

妥当性検査が単純で、クラスに含まれている情報のみを使用してチェックできる場合は、ほとんどの場合、状態チェックをクラスに追加することが大切です。

ただし、そうすることが本当に可能でないか望ましい場合があります。

大きな例はコンパイラです。プログラムが有効であることを確認するために抽象構文木(AST)の状態をチェックすることは、通常、プロパティの設定者またはコンストラクタによって行われません。代わりに、検証は通常、ツリービジター、または一連の「意味解析クラス」の一連の相互再帰的なメソッドによって行われます。ただしどちらの場合も、プロパティーは値が設定された後に検証されます。

また、古いUI状態に使用されていたオブジェクトでは、無効な値が設定されていると例外をスローするのは悪い考えです(使い勝手の観点から)。これは、WPFデータバインディングを使用するアプリケーションに特に当てはまります。そのような場合、例外を投げるのではなく、何らかの形のフィードバックを顧客に表示する必要があります。

0

クラス内の有効な状態は、クラス不変式の概念で最もよく表現されます。そのクラスのオブジェクトが有効であるために真でなければならないブール式です。

Design by Contractアプローチは、あなたが、クラスCの開発者として、クラス不変条件が成立することを保証すべきであることを示唆している:

  • 建設
  • した後、パブリックメソッドを呼び出した後

これは、オブジェクトがカプセル化された(パブリックメソッドの呼び出し以外で誰もそれを変更することはできません)、パブリックメソッドの入力時、またはデストラクタ(デストラクタ付きの言語)の入力時に不変条件も満たされます。

各パブリックメソッドは、すべてのパブリックメソッドの最後に、クラスによって満たされる前提条件、発信者が満足しなければならない、および事後条件を述べています。前提条件に違反すると、クラスのコントラクトに効果的に違反するため、正しいことができますが、前提条件の違反で呼び出された場合、特定の方法で動作する必要はなく、不変条件を維持する必要もありません。呼び出し元の違反がない場合にその契約を満たすクラスは、という正しいと言うことができます。

正確ではあるがそれに相補的な概念(確かにソフトウェア品質の複数の要素に属する)はの堅牢なです。ここでは、堅牢なクラスはメソッドの前提条件を満たさずにそのメソッドの1つが呼び出されたときにそれを検出します。このような場合、アサーション違反の例外がスローされるため、呼び出し側は、呼び出し側が吹き飛ばしたことを知ることができます。

あなたの質問に答えて、クラスとその呼び出し元の両方は、クラス契約の一部として義務を負います。堅牢なクラスは、契約違反と唾を吐き出すことを検出します。正しい発信者は契約に違反しません。

コードライブラリのパブリックインターフェイスに属するクラスは堅牢なものとしてコンパイルする必要がありますが、内部クラスは堅牢なものとしてテストすることができますが、前提条件のチェックを行わずにリリースされた製品で正しく動作します。これは多くのものとwas discussed elsewhereに依存します。

0

クラスは本当に有効な値を保持する必要があります。これらがコンストラクタまたはプロパティを通じて入力されるかどうかは関係ありません。どちらも無効な値を拒否する必要がありますコンストラクタパラメータとプロパティの両方で同じ検証が必要な場合は、プロパティとコンストラクタの両方の値を検証するために共通のプライベートメソッドを使用するか、プロパティで検証を行い、設定時にコンストラクタ内のプロパティを使用できますローカル変数個人的に共通の検証方法を使用することをお勧めします。

クラスは、無効な値を受け取ると例外をスローする必要があります。全体として、良いデザインはこの出来事の可能性を減らすのに役立ちます。