別のコントロールへの移動を制限する
Validatingイベントハンドラのコードを一工夫すると、ユーザーが無効な値を修正するまでは別のコントロールに移動できないようにすることができます。コントロールにイベントハンドラを割り当てた先ほどのコードを見ると、このメソッドがCancelEventHandlerにラップされています。このため、textBox1_Validatingメソッドのcatchブロック内でエラーメッセージを設定したすぐ後に、次の行を追加することができます。
e.Cancel = true;
このコード行により、通常はValidatingイベントの後に発生するイベントが発生しなくなります。ユーザー側から見ると、現在のコントロールからカーソルが離れなくなり、エラーを修正するまでは[Tab]キーやマウスを使って別のコントロールに移動できなくなります。Validatingイベントについて詳しくは、MSDNライブラリを参照してください。
ErrorProviderの出力
次に、ErrorProviderが実際にどのような出力をするのかを見てみましょう。図1のシンプルなフォームを見てください。このシンプルなフォームには、バインドされていないDataGridView、2つのTextBoxコントロール、OKボタン、Cancelボタンが含まれています。以降ではこのフォームを中心に話を進めていきます。
先ほど説明したValidatingメソッドを左側のテキストボックスに関連付け、数字以外の文字を入力すると、ErrorProviderは小さいエラーアイコンを表示します。このエラーアイコンをマウスでポイントすると、対応するエラーメッセージ(この場合は「Not an integer value」)がヒントとして表示されます(図2を参照)。
お分かりのように、ErrorProviderを使えば、フォーム入力チェック用のユーザーインターフェイスを簡単に作成することができます。とはいえ、これまでのことをすべて実現するには、フォーム上の個々のコントロールにxxx_Validatingメソッドを手作業でコーディングしなければなりません。Larry Wallが『Programming Perl』(Larry Wall・Tom Christiansen・Jon Orwant 著、Oreilly & Associates Inc、2000年10月)で述べているように、プログラミングの3大美徳の1つは怠け者であることです。
「エネルギーの総支出を減らすために、多大な努力をするように、あなたをかりたてる性質。こうして労力を省くために書いたプログラムは他人も使うようになり、そのプログラムに関する質問にいちいち答えずに済ますためにドキュメントを書くようになる。」
つまり、この場合で言えば、もっと良い方法があるはずだ、ということです。そして、実際にあるのです。そのためには、CleanCode.Forms.Validatorクラスを利用します。
より優れたソリューション
今度は、ErrorProviderを利用しつつ、柔軟性も高い検証エンジンの実装方法を考えてみます。具体的には、エラーメッセージをLabelコントロールに表示するようにします(マウスでポイントするのが嫌いな方向けです)。開発者が限界を感じることがないよう、このエンジンでは、カスタムの検証機能と自動的な検証機能を(統合させないまでも)共存させることができなければなりません。
これを踏まえて、検証の性質とビジネスルールの概念を考えてみましょう。例えば、米国内の電話番号を入力するフォームフィールドがあるとします。この要件の影にあるのが、電話番号が何で構成されているかについての多くの前提条件です。ビジネスルールとしては、次のようなものが挙げられます。
- 数字とハイフンのみを認める。
- ハイフンを使う場合、入力はちょうど12文字(10桁の数字と2つのハイフン)でなければならない。
- ハイフンは3桁目と7桁目の後ろに入れなければならない。
- ハイフンを使わない場合、入力はちょうど10桁でなければならない。
一般に、ビジネスルールとは、すべてのビジネスに当てはまるものではなく、個別のビジネスに当てはまるものと考えられていますが、ここで挙げたルールも、データの意味を問うという広い意味でのビジネスルールです。今度は、米国内の電話番号という汎用的な特徴ではなく、このサンプルビジネスのみに当てはまるビジネスルールをもう1つ追加します。
- 市外局番はカリフォルニア州のものだけでなければならない。
フォームの入力を処理する多くのソフトウェアアプリケーションは、次の仮想コードのようなロジックを使って、ビジネスルールのチェックをフォームの処理と一緒にハードコーディングしています。
read phoneNumber
if (phoneNumber contains only digits and hyphens) and
getAreaCode(phoneNumber) is on the California list of area codes
then
store phoneNumber
else
flag invalid phone number
しかし、この会社がオレゴン州やワシントン州にも拡大するとしたらどうなるでしょうか。ビジネスルールの変更に伴い、コードの変更も必要になります。これは、9月のレイバーデイが過ぎてもまだ夏用の白い靴を履いているのと同じくらい格好悪いことであり、きちんとした人々――つまりここで言えばクリーンなコード――ならば絶対にしてはならないことです。データをプログラムの外部に置くソフトウェア開発者はたくさんいますが、ルールを外部化する人は意外に少数です。そしてここが、うまく設計された検証エンジンの力の見せどころになります。ビジネスルールをプログラムの外部に置くことによって、ソフトウェア設計者ではなくビジネスアナリストによる管理を実現することができます。
従って、本稿の検証エンジンが最終的に目指すところは次のようになります。
- ビジネスルールを外部でカプセル化する。
- フォーム全体で利用できるErrorProviderまたはコントロール固有のLabelのいずれかを使って検証エラーを明示する。
- 共存する検証エンジンの機能範囲を越えたカスタムな検証を実現できるようにする。


