私が出会ったほとんどの Windows フォームアプリケーションは存在しないか、単体テストのカバレッジが極めて低いものです。また、メンテナンスが非常に難しく、プロジェクト内のさまざまな Form クラスの背後には数百行、数千行ものコードが存在しますが、そうである必要はありません。Windows フォームが「レガシー」テクノロジーだからといって、保守不可能な混乱を招く運命にあるわけではありません。以下に、保守可能でテスト可能な Windows フォームアプリケーションを作成するための10のヒントを紹介します。
1. ユーザーコントロールでUIを分離する
まず、1つのフォームにコントロールを配置しすぎないようにします。通常、アプリケーションのメインフォームは論理的な領域(これを「ビュー」と呼びます)に分割できます。これらの各領域のコントロールを独自のコンテナに配置すれば、作業がずっと楽になります。Windows フォームでは、最も簡単な方法はユーザーコントロールを使用することです。したがって、エクスプローラースタイルのアプリケーションで、左側にツリービュー、右側に詳細ビューがある場合、TreeView を独自の UserControl に配置し、右側に表示される可能性のある各ビューごとに UserControl を作成します。同様に、タブコントロールがある場合、タブコントロール内の各ページごとに別個の UserControl を作成します。
これにより、クラスが扱いにくくなるのを防ぐだけでなく、サイズ変更や Tab キーによるフォーカス移動の設定なども簡単になります。また、必要に応じて UI の一部分全体を簡単に無効にすることもできます。さらに、論理的にグループ化されたコントロールを含む小さな UserControl に UI を分割すると、アプリケーションの UI レイアウトを再設計するのがはるかに容易になることがわかるでしょう。
2. UI 以外のコードをコードビハインドから排除する
Windows フォームアプリケーションでは、フォームのコードビハインドに、ネットワーク、データベース、ファイルシステムにアクセスするコードが常に存在します。これは「単一責任の原則」に著しく違反しています。Form や UserControl クラスの目的はユーザーインターフェースのみであるべきです。したがって、コードビハインドに UI 以外のコードがあるのを検出したら、それを単一責任を持つクラスにリファクタリングします。たとえば、PreferencesManager クラスや、特定の Web サービスを呼び出す役割を持つクラスを作成できます。これらのクラスは依存関係として UI コンポーネントに注入できます(ただし、これは第一歩に過ぎません。この考え方はさらに拡張できます。すぐに説明します)。
3. インターフェースを使用してパッシブビューを作成する
特に有用なテクニックは、作成するすべてのフォームとユーザーコントロールにビューインターフェースを実装させることです。このインターフェースには、ビュー内のコントロールの状態や内容を設定・取得できるプロパティを含めるべきです。また、ボタンのクリックやスライダーの移動など、ユーザー操作を報告するイベントを含めることもできます。目標は、これらのビューインターフェースの実装を完全に受動的にすることです。理想的には、Forms や UserControls のコードビハインドには条件ロジックが一切含まれていないべきです。
以下は、新しいユーザーエントリビューのためのビューインターフェースの例です。このビューの実装は些細なものであるべきです。ビジネスロジックはコードビハインドに属しません(それがどこに属するかは次に説明します)。
interface INewUserView
{
string FirstName { get; set; }
string LastName { get; set; }
event EventHandler SaveClicked;
}
ビューの実装を可能な限りシンプルに保つことで、代替 UI フレームワーク(WPF など)への移行を最大限容易にできます。なぜなら、新しいテクノロジーでビューを再作成するだけで済み、その他のコードはすべて再利用できるからです。
4. プレゼンターを使用してビューを制御する
したがって、すべてのビューを受動的にし、インターフェースを実装したら、アプリケーションのビジネスロジックを実装し、ビューを制御する何かが必要になります。これを「プレゼンター」クラスと呼びます。これは「モデル・ビュー・プレゼンター(MVP)」パターンとして知られています。
モデル・ビュー・プレゼンターでは、ビューは完全に受動的であり、プレゼンターがビューに表示するデータを指示します。また、ビューがプレゼンターと通信することも許可されます。上記の例では、イベントを発生させることで実現していますが、このパターンでは通常、ビューがプレゼンターを直接呼び出すこともできます。
ビューがモデル(ビジネスエンティティ、データベース層などを含む)を直接操作することは絶対に許可されません。MVP パターンに従えば、アプリケーション内のすべてのビジネスロジックを簡単にテストできます。これらのロジックはプレゼンターやその他の非 UI クラスに配置されるからです。
5. エラー報告のためのサービスを作成する
通常、プレゼンタークラスはエラーメッセージを表示する必要があります。しかし、非 UI クラスに単に MessageBox.Show を配置してはいけません。そのメソッドは単体テスト不可能になります。代わりに、サービス(IErrorDisplayService など)を作成し、プレゼンターが問題を報告する必要があるときにそのサービスを呼び出せるようにします。これにより、プレゼンターユニットのテスト可能性を維持し、将来ユーザーにエラーを表示する方法を変更する柔軟性も得られます。
6. コマンドパターンを使用する
アプリケーションに多数のボタンがあり、ユーザーがクリックできるツールバーが含まれている場合、コマンドパターンが非常に適しています。コマンドパターンは、各コマンドに対してクラスを作成することを定めています。これにより、コードを小さなクラスに分割でき、各クラスが単一の責任を持つという大きな利点があります。また、特定のコマンドに関連するすべてのことを集中管理できます。そのコマンドは有効にすべきか? 表示されるべきか? ツールチップやショートカットキーは何か? 実行に特定の権限や許可が必要か? コマンド実行時にスローされた例外はどのように処理すべきか?
コマンドパターンを使用すると、アプリケーション内のすべてのコマンドに共通する各問題の処理方法を標準化できます。コマンドオブジェクトには Execute メソッドがあり、そのコマンドの実行に必要な動作のコードが実際に含まれています。多くの場合、これには他のオブジェクトやビジネスサービスの呼び出しが含まれるため、それらを依存関係としてコマンドオブジェクトに注入する必要があります。コマンドオブジェクト自体は単体テスト可能であり(かつ直接テストするべきです)。
7. IoC コンテナを使用して依存関係を管理する
Presenter クラスや Command クラスを使用していると、それらが依存するクラスの数が時間とともに増加することがわかるでしょう。ここで、Unity や StructureMap などの制御の反転(IoC)コンテナが役立ちます。依存関係の深さに関係なく、ビューやプレゼンターを簡単に構築できるようになります。
8. イベントアグリゲーターパターンを使用する
Windows フォームアプリケーションで非常に有用なもう1つのデザインパターンは、イベントアグリゲーターパターン(「メッセンジャー」や「イベントバス」とも呼ばれます)です。これは、イベントの発生元とイベントの処理者が互いに疎結合である必要がないパターンです。コード内で他の場所で処理する必要がある「イベント」が発生した場合、イベントアグリゲーターにメッセージを公開するだけです。その後、そのメッセージに応答する必要があるコードが、誰が発生させたかを気にすることなく、購読して処理できます。
たとえば、ユーザーが現在 UI のどの位置にいるかの詳細を含む「ヘルプ要求」メッセージを送信します。別のサービスがそのメッセージを処理し、Web ブラウザーでヘルプドキュメントの正しいページを開くようにします。別の例はナビゲーションです。アプリケーションに複数の画面がある場合、「ナビゲーション」メッセージをイベントアグリゲーターに公開し、購読者が新しい画面をユーザーインターフェースに表示することで応答できます。
イベントアグリゲーターは、イベントの公開者と購読者を根本的に分離するだけでなく、単体テストが非常に容易なコードを作成できるという大きな利点もあります。
9. Async と Await を使用したスレッド処理
.NET 4 以降を対象とし、Visual Studio 12 以降を使用している場合、新しい async および await キーワードを使用できることを忘れないでください。これにより、アプリケーション内のスレッドコードが大幅に簡素化され、バックグラウンドタスクの完了後に UI スレッドに自動的に戻る処理が行われます。また、複数の連鎖するバックグラウンドタスクにまたがる例外処理も大幅に簡素化されます。これらは Windows フォームアプリケーションに非常に適しており、まだ使用していないなら試す価値があります。
10. 手遅れになる前に
私がこれまでに説明したすべてのパターンとテクニックは、既存の Windows フォームアプリケーションにリファクタリングで適用できますが、苦い経験から言えるのは、特にフォームのコードビハインドが数千行に達している場合、多大な作業が必要になる可能性があるということです。MVP、イベントアグリゲーター、コマンドパターンなどのパターンを使用してアプリケーションを構築し始めれば、アプリケーションが大きくなるにつれて、メンテナンスの苦痛が大幅に軽減されることに気づくでしょう。また、すべてのビジネスロジックに対して単体テストを実行でき、これは継続的な保守性にとって不可欠です。
原文著者:Mark Heath
原文リンク:https://markheath.net/post/maintainable-winforms
転載元:WeChat公式アカウント OneByOneDotNet
公众号記事リンク:https://mp.weixin.qq.com/s/ks_ghCRxMmOQPYFib0cb3g