38

例外とその使用に関するいくつかの質問と回答を読んでいます。例外は例外、未処理の場合にのみ発生させるべきだという強い意見があるようです。そのため、検証がビジネス オブジェクトでどのように機能するのか疑問に思いました。

オブジェクトのプロパティのゲッター/セッターを持つビジネス オブジェクトがあるとします。値が 10 ~ 20 であることを検証する必要があるとします。これはビジネス ルールであるため、ビジネス オブジェクトに属します。そのため、検証コードがセッターに入っていることを暗示しているようです。これで、UI データがデータ オブジェクトのプロパティにバインドされました。ユーザーは 5 を入力するため、ルールは失敗する必要があり、ユーザーはテキスト ボックスから移動できません。. UI はプロパティにデータバインドされているため、setter が呼び出され、ルールがチェックされて失敗します。ルールが失敗したことを示すためにビジネス オブジェクトから例外を発生させた場合、UI はそれを取得します。しかし、それは例外の好ましい使用法に反しているようです。それがセッターであることを考えると、セッターの「結果」は実際にはありません。

では、検証はどのように機能するのでしょうか?

編集: ここでは、単純化しすぎた例を使用した可能性があります。上記の範囲チェックのようなものは UI で簡単に処理できますが、検証がより複雑な場合、たとえば、ビジネス オブジェクトが入力に基づいて数値を計算し、計算された数値が範囲外の場合は拒否する必要があります。これは、UI に含めるべきではない、より複雑なロジックです。

また、すでに入力されているフィールドに基づいて入力された追加データも考慮されます。たとえば、在庫や現在のコストなどの特定の情報を取得するために、注文にアイテムを入力する必要があります。ユーザーは、この情報を入力してさらに入力するかどうかを決定する必要がある場合があります (注文するユニット数など)。さらなる検証が行われるように。アイテムが有効でない場合、ユーザーは他のフィールドに入力できるようにする必要がありますか? ポイントは何ですか?

4

18 に答える 18

18

データ検証に関するPaul Stovellの注目すべき業績を少し掘り下げたいとします。彼はこの記事で一度に彼の考えをまとめました。私はたまたまこの問題について彼の見解を共有しており、それを自分のライブラリに実装しました。

Nameポールの言葉を借りれば、セッターで例外をスローすることの短所は次のとおりです (プロパティが空であってはならないサンプルに基づく) :

  • 実際に空の名前が必要な場合があります。たとえば、「アカウントの作成」フォームのデフォルト値として。
  • 保存する前にデータを検証するためにこれに依存している場合、データがすでに無効になっているケースを見逃すことになります。つまり、空の名前でデータベースからアカウントをロードし、それを変更しないと、それが無効であることに気付かない可能性があります。
  • データ バインディングを使用していない場合は、try/catchこれらのエラーをユーザーに表示するために、ブロックを含む多くのコードを記述する必要があります。ユーザーがフォームに入力しているときにフォームにエラーを表示しようとすると、非常に困難になります。
  • 例外ではないことに対して例外をスローするのは好きではありません。アカウントの名前を「Supercalafragilisticexpialadocious」に設定しているユーザーは例外ではなく、エラーです。もちろん、これは個人的なことです。
  • 破られたすべてのルールのリストを取得することは非常に困難です。たとえば、一部の Web サイトでは、「名前を入力する必要があります。住所を入力する必要があります。電子メールを入力する必要があります」などの検証メッセージが表示されます。try/catchそれを表示するには、たくさんのブロックが必要になります。

そして、代替ソリューションの基本的なルールは次のとおりです。

  1. 永続化しようとしない限り、無効なビジネス オブジェクトがあっても問題はありません。
  2. 壊れたルールはすべてビジネス オブジェクトから取得できる必要があります。これにより、データ バインディングや独自のコードで、エラーがあるかどうかを確認して適切に処理できます。
于 2008-09-25T12:35:51.680 に答える
9

検証と永続化 (データベースへの保存) のコードが別々にあると仮定すると、次のようになります。

  1. UI は検証を実行する必要があります。ここで例外をスローしないでください。ユーザーにエラーを警告し、レコードが保存されないようにすることができます。

  2. データベースの保存コードは、不正なデータに対して無効な引数の例外をスローする必要があります。この時点ではデータベースの書き込みを続行できないため、ここで行うのは理にかなっています。UI はユーザーが保存できないようにする必要があるため、理想的にはこれが発生しないようにする必要がありますが、データベースの一貫性を確保するためにはそれでも必要です。また、UI データ検証がない UI 以外 (バッチ更新など) からこのコードを呼び出している可能性もあります。

于 2008-09-18T02:14:50.897 に答える
8

私は常にCSLAフレームワークでのロッキーロトカのアプローチのファンでした(チャールズが述べたように)。一般に、セッターによって駆動されるか、明示的なValidateメソッドを呼び出すことによって駆動されるかにかかわらず、BrokenRuleオブジェクトのコレクションはビジネスオブジェクトによって内部的に維持されます。UIは、オブジェクトのIsValidメソッドをチェックするだけで、BrokenRulesの数をチェックし、適切に処理します。または、ValidateメソッドでUIが処理できるイベントを簡単に発生させることもできます(おそらくよりクリーンなアプローチ)。BrokenRulesのリストを使用して、要約形式または適切なフィールドの横に使用するエラーメッセージを表示することもできます。CSLAフレームワークは.NETで記述されていますが、全体的なアプローチはどの言語でも使用できます。

この場合、例外をスローするのが最善の方法ではないと思います。私は間違いなく、例外は例外的な状況のためのものでなければならないと言っている考え方に従いますが、単純な検証エラーはそうではありません。私の意見では、OnValidationFailedイベントを発生させる方がより適切な選択です。

ちなみに、無効な状態のときにユーザーがフィールドを離れないようにするというアイデアは好きではありませんでした。戻って無効なフィールドを修正する前に、フィールドを一時的に離れる必要がある(おそらく最初に他のフィールドを設定する)必要がある状況は非常に多くあります。不必要な不便だと思います。

于 2008-09-18T04:33:28.850 に答える
5

検証をゲッターとセッターの外に移動したい場合があります。すべての検証ルールを実行する IsValid という関数またはプロパティを使用できます。t は、辞書またはハッシュテーブルにすべての「壊れたルール」を入力します。このディクショナリは外部に公開され、それを使用してエラー メッセージを設定できます。

これは、CSLA.Net で採用されているアプローチです。

于 2008-09-18T03:38:00.137 に答える
4

例外は、検証の通常の部分としてスローされるべきではありません。ビジネス オブジェクト内から呼び出される検証は最後の防衛線であり、UI が何かをチェックできなかった場合にのみ実行する必要があります。そのため、他の実行時例外と同様に扱うことができます。

検証ルールの定義と適用には違いがあることに注意してください。ビジネス ロジック レイヤーでビジネス ルールを定義 (つまり、コード化または注釈付け) したい場合がありますが、UI から呼び出して、特定の UI に適した方法で処理できるようにすることができます。処理方法は、フォーム ベースの Web アプリと ajax Web アプリなど、UI によって異なります。Exception-on-set 検証で提供される処理オプションは非常に限られています。

多くのアプリケーションは、JavaScript、ドメイン オブジェクトの制約、データベースの制約などで、検証規則を複製しています。理想的には、この情報は 1 回だけ定義されますが、これを実装するのは困難な場合があり、横方向の思考が必要になります。

于 2008-09-18T00:29:53.567 に答える
3

私は、ビジネスルールに違反する値が渡されたときに、ビジネスオブジェクトが例外をスローする必要があると考える傾向があります。ただし、winforms 2.0データバインディングアーキテクチャはその逆を想定しているため、ほとんどの人はこのアーキテクチャをサポートするために鉄道に乗っています。

私は、ビジネスオブジェクトがwinforms環境だけでなく、複数の環境で使用可能で正しく機能するように構築する必要があるというshabbyrobeの最後の回答に同意します。たとえば、ビジネスオブジェクトはSOAタイプのWebサービス、コマンドラインインターフェイス、ASPで使用できます。 .netなど。オブジェクトは正しく動作し、これらすべての場合に無効なデータからオブジェクトを保護する必要があります。

見落とされがちな側面は、1-1、1-n、またはnnの関係にあるオブジェクト間のコラボレーションを管理する際に発生することでもあります。これらも無効なコラボレーターの追加を受け入れ、チェックする必要がある、またはチェックする必要がある無効な状態フラグを維持するだけです。無効なコラボレーションの追加を積極的に拒否します。私はジルニコラらの合理化されたオブジェクトモデリング(SOM)アプローチに大きく影響されていることを認めなければなりません。しかし、他に何が論理的です。

次は、Windowsフォームの操作方法です。これらのシナリオのビジネスオブジェクトのUIラッパーを作成することを検討しています。

于 2008-09-25T12:08:12.167 に答える
3

実行する検証の種類と場所によって異なります。アプリケーションの各レイヤーは、不良データから簡単に保護でき、簡単すぎて価値がないと思います。

多層アプリケーションと各層の検証要件/機能を検討してください。中間層のObjectは、ここで議論の余地があるようです。

  • データベース
    は、列の制約と参照整合性によって無効な状態から自身を保護します。これにより、アプリケーションのデータベースコードが例外をスローします。

  • オブジェクト

  • ASP.NET/Windowsフォームは、例外を使用せずに
    バリデータールーチンやコントロールを使用してフォームの状態(オブジェクトではない)を保護します(winformsにはバリデーターは付属していませんが、msdnにはそれらの実装方法を説明する優れたシリーズがあります)

ホテルの部屋のリストを含むテーブルがあり、各行に「ベッド」と呼ばれるベッドの数の列があるとします。その列で最も適切なデータ型は、符号なし整数*です。また、「Beds」と呼ばれるInt16*プロパティを持つプレーンなoleオブジェクトもあります。問題は、-4555をInt16に固定できることですが、データをデータベースに永続化しようとすると、例外が発生します。これは問題ありません。ホテルの部屋のベッド数はゼロ未満にすることはできないため、私のデータベースでは、ホテルの部屋のベッド数がゼロ未満であるとは言えません

*データベースで表現できるが、表現できると仮定しましょう* C#でushort
を 使用できることはわかっていますが、この例では、表現できないと仮定しましょう。

オブジェクトがビジネスエンティティを表す必要があるのか​​、それともフォームの状態を表す必要があるのか​​については、多少の混乱があります。確かに、ASP.NETおよびWindowsフォームでは、フォームはそれ自体の状態を完全に処理および検証できます。同じInt16フィールドに入力するために使用されるASP.NETフォームにテキストボックスがある場合は、オブジェクトに割り当てられる前に入力をテストするRangeValidatorコントロールをページに配置している可能性があります。これにより、ゼロ未満の値を入力できなくなり、おそらく30を超える値を入力できなくなります。これは、想像できる最悪のノミが蔓延しているホステルに対応するのに十分であると考えられます。ポストバックでは、おそらく前にページのIsValidプロパティをチェックしているでしょうオブジェクトを構築することで、オブジェクトがゼロ未満のベッドを表すことを防ぎ、セッターが保持してはならない値で呼び出されることを防ぎます。

ただし、オブジェクトは依然としてゼロ未満のベッドを表すことができます。また、検証が統合されたレイヤー(フォームとDB)を含まないシナリオでオブジェクトを使用していた場合は、運が悪いことになります。

なぜあなたはこのシナリオにいるのでしょうか?それはかなり例外的な状況に違いありません!したがって、セッターは無効なデータを受信したときに例外をスローする必要があります。決して投げるべきではありませんが、投げることはできます。オブジェクトを管理してASP.NETフォームを置き換えるWindowsフォームを作成していて、オブジェクトを入力する前に範囲を検証するのを忘れている可能性があります。ユーザーの操作がまったくなく、オブジェクトがマップされているテーブルではなく、データベースの別の関連する領域に保存される、スケジュールされたタスクでオブジェクトを使用している可能性があります。後者のシナリオでは、オブジェクトは無効な状態になる可能性がありますが、他の操作の結果が無効な値の影響を受け始めるまでわかりません。それらをチェックして例外をスローしている場合、それはです。

于 2008-09-18T04:59:27.243 に答える
3

おそらく、クライアント側とサーバー側の両方の検証を検討する必要があります。クライアント側の検証を何かがすり抜けた場合、ビジネス オブジェクトが無効になる場合は、自由に例外をスローできます。

私が使用したアプローチの 1 つは、カスタム属性をビジネス オブジェクト プロパティに適用することでした。これは、検証規則を記述したものです。例えば:

[MinValue(10), MaxValue(20)]
public int Value { get; set; }

その後、属性を処理して使用し、クライアント側とサーバー側の両方の検証メソッドを自動的に作成して、ビジネス ロジックの重複の問題を回避できます。

于 2008-09-17T23:41:21.453 に答える
3

ビジネス オブジェクトは不正な入力に対して例外をスローする必要がありますが、これらの例外は通常のプログラム実行中にスローされるべきではありません。矛盾しているように聞こえるので、説明します。

各 public メソッドはその入力を検証し、正しくない場合は「ArgumentException」をスローする必要があります。(プライベート メソッドは、開発を容易にするために「Debug.Assert()」で入力を検証する必要がありますが、それは別の話です。) パブリック メソッド (およびもちろんプロパティ) への入力の検証に関するこの規則は、アプリケーションのすべてのレイヤーに当てはまります。 .

もちろん、ソフトウェア インターフェイスの要件は、インターフェイスのドキュメントで詳しく説明する必要があります。引数が正しく、例外がスローされないことを確認するのは、呼び出し元のコードの仕事です。つまり、UI は、入力をビジネス オブジェクトに渡す前に。

上記のルールが破られることはほとんどありませんが、ビジネス オブジェクトの検証が非常に複雑になる場合があり、その複雑さを UI に押し付けるべきではありません。その場合、BO のインターフェイスが受け入れるものにある程度の余裕を持たせてから、明示的な Validate(out string[]) 述語を提供して、プロパティをチェックし、変更が必要なものについてフィードバックを提供することをお勧めします。ただし、この場合、明確に定義されたインターフェイス要件がまだあり、例外をスローする必要がないことに注意してください (呼び出しコードが規則に従っていると仮定します)。

この後者のシステムに従って、プロパティ セッターの早期検証を行うことはほとんどありません。これは、そのソフト オフがプロパティの使用を複雑にするためです (ただし、質問に示されている場合は可能です)。(余談ですが、間違ったデータが含まれているという理由だけで、フィールドからタブで移動するのを妨げないでください。フォームをタブで移動できないと、閉所恐怖症になります。すぐに戻って修正します。 、約束します! OK、気分が良くなりました。申し訳ありません。)

于 2008-09-18T03:40:27.807 に答える
3

クライアント側とサーバー側の両方の検証 (またはさまざまなレイヤーでの検証) を強くお勧めします。これは、例外をスローするコストがますます高くなるため、物理層またはプロセス間で通信する場合に特に重要です。また、チェーンの下流で検証を待つほど、無駄な時間が長くなります。

データ検証に例外を使用するかどうかについて。プロセス内で例外を使用することは問題ないと思いますが (まだ好ましくはありません)、プロセス外では、メソッドを呼び出してビジネス オブジェクトを検証し (たとえば、保存する前に)、メソッドに操作の成功と検証エラーを返します。エラーは例外ではありません。

検証が失敗すると、Microsoft はビジネス オブジェクトから例外をスローします。少なくとも、それが Enterprise Library の Validation Application Block の仕組みです。

using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
public class Customer
{
  [StringLengthValidator(0, 20)]
  public string CustomerName;

  public Customer(string customerName)
  {
    this.CustomerName = customerName;
  }
}
于 2008-09-17T23:50:49.090 に答える
3

Paul Stovell の記事で述べたように、IDataErrorInfo インターフェイスを実装することで、ビジネス オブジェクトにエラーのない検証を実装できます。そうすることで、WinForm の ErrorProviderWPF の検証ルールとのバインディングによるユーザー エラー通知が可能になります。オブジェクトのプロパティを検証するロジックは、各プロパティ ゲッターではなく 1 つのメソッドに格納されるため、必ずしも CSLA や Validation Application Block などのフレームワークに頼る必要はありません。

ユーザーがフォーカスをテキストボックスの外に移動できないようにすることに関する限り、まず第一に、これは通常、ベスト プラクティスではありません。ユーザーは順不同でフォームに入力したい場合や、検証ルールが複数のコントロールの結果に依存している場合、別のコントロールを設定するために 1 つのコントロールから抜け出すためだけにダミーの値を入力する必要がある場合があります。AllowValidateつまり、フォームのプロパティをデフォルトに設定EnableAllowFocusChange し、Control.Validating イベントをサブスクライブすることで実装できます。

    private void textBox1_Validating(object sender, CancelEventArgs e)
    {
        if (textBox1.Text != String.Empty)
        {
            errorProvider1.SetError(sender as Control, "Can not be empty");
            e.Cancel = true;
        }
        else
        {
            errorProvider1.SetError(sender as Control, "");
        }
    }

この検証のためにビジネス オブジェクトに格納されたルールを使用することは、フォーカスが変更され、データ バインドされたビジネス オブジェクトが更新される前に Validating イベントが呼び出されるため、少し注意が必要です。

于 2008-12-29T17:09:51.733 に答える
1

あなたのケースで例外をスローしても問題ありません。何かが整数を文字列に設定しようとしているため (たとえば)、このケースを真の例外と見なすことができます。ビューに関するビジネス ルールの知識が不足しているということは、このケースは例外的であると見なし、それをビューに戻す必要があることを意味します。

入力値をビジネス層に送信する前に検証するかどうかはあなた次第ですが、アプリケーション全体で同じ基準に従っている限り、クリーンで読みやすいコードになると思います。

上記のようにSpringフレームワークを使用できますが、リンクされたドキュメントの多くが強く型付けされていないコードの記述を示しているため注意してください.IEでは、コンパイル時に検出できなかったエラーが実行時に発生する可能性があります. これは私ができるだけ避けようとしていることです。

ここで現在行っている方法は、画面からすべての入力値を取得し、それらをデータ モデル オブジェクトにバインドし、値にエラーがある場合は例外をスローすることです。

于 2008-09-17T23:38:07.560 に答える
1

Spring フレームワークのアプローチを検討してみてください。Java (または .NET) を使用している場合は、Spring をそのまま使用できますが、そうでない場合でもそのパターンを使用できます。独自の実装を作成するだけで済みます。

于 2008-09-17T23:17:04.477 に答える
1

データが無効な場合にセッターでイベントを発生させることを検討しましたか? これにより、例外をスローする問題が回避され、オブジェクトの「無効な」フラグを明示的にチェックする必要がなくなります。どのフィールドが検証に失敗したかを示す引数を渡して、再利用可能にすることもできます。

イベントのハンドラーは、必要に応じて適切なコントロールにフォーカスを戻すことができる必要があり、ユーザーにエラーを通知するために必要なコードを含めることができます。また、必要に応じて、イベント ハンドラーの接続を拒否し、検証の失敗を自由に無視することもできます。

于 2008-09-18T03:21:48.460 に答える
1

私の経験では、検証ルールがアプリケーションのすべての画面/フォーム/プロセスにわたって普遍的であることはめったにありません。このようなシナリオは一般的です。追加ページでは Person オブジェクトに姓がなくてもかまいませんが、編集ページでは姓が必要です。そのため、オブジェクトの外部で検証を行うか、ルールをオブジェクトに注入して、コンテキストに応じてルールを変更できるようにする必要があると私は信じるようになりました。有効/無効は、検証後のオブジェクトの明示的な状態、または失敗したルールのコレクションをチェックすることによって導出できる状態である必要があります。失敗したビジネス ルールは例外ではありません。

于 2008-09-18T00:24:36.920 に答える
0

あなたのビジネスモデルがどれだけ重要かによると思います。DDDの道を進みたいのであれば、モデルが最も重要です。したがって、常に有効な状態にする必要があります。

私の意見では、ほとんどの人はドメインオブジェクトでやりすぎ(ビューとの通信、データベースへの永続化など)を試みていますが、場合によっては、より多くのレイヤーと関心の分離、つまり1つ以上のビューモデルが必要になります。次に、ビューモデルに例外なしで検証を適用し(検証は、Webサービス/ Webサイトなどのコンテキストごとに異なる可能性があります)、ビジネスモデル内に例外検証を保持できます(モデルが破損しないようにするため)。ビューモデルをビジネスモデルにマッピングするには、1つ(または複数)のアプリケーションサービスレイヤーが必要です。ビジネスオブジェクトは、NHibernateValidatorなどの特定のフレームワークに関連することが多い検証属性で汚染されるべきではありません。

于 2009-08-27T00:51:16.643 に答える
0

私の意見では、これは例外をスローしても問題ない例です。プロパティには、問題を修正するためのコンテキストがない可能性があります。そのような例外は適切であり、可能であれば呼び出し元のコードが状況を処理する必要があるためです。

于 2008-09-17T23:12:08.823 に答える
0

入力がビジネス オブジェクトによって実装されたビジネス ルールを超える場合、それはビジネス オブジェクトによって処理されないケースだと思います。したがって、例外をスローします。あなたの例ではセッターが5を「処理」しますが、ビジネスオブジェクトはそうしません。

入力のより複雑な組み合わせの場合は、検証メソッドが必要です。そうしないと、非常に複雑な検証があちこちに散らばってしまいます。

私の意見では、許可されている/許可されていない入力の複雑さに応じて、どちらの方法を取るかを決定する必要があります。

于 2008-09-17T23:15:48.613 に答える