デスクトップアプリでキャンセル可能なダイアログを作るときのデータのやり取り
はじめに
質問されて、確かに自分もどう作るのが良いのか分かってなかったなぁ(今も正解なのか?は厳密にはわかっていませんが)と思ったので、このタイトルで記事を書いてみました。
デスクトップアプリでOK, キャンセルボタンのあるダイアログを作るとき、OKが押されたらモデルにデータを反映し、キャンセルが押されたら何もしない、という挙動になります。
シンプルにモデルを追加・削除するぐらいなら簡単に作れると思うのですが、編集することになると少しだけ話が変わってきます。追加・削除とは異なり、編集した結果をモデルに反映する必要があるからです。
上記の状況において、メリットデメリットも考えつついくつか自分が考えた設計のバリエーションを疑似コードを用いて紹介します。
こういうのもあるよ!という方がいらっしゃればコメントいただけますと幸いです。
以下のパターンでは原則として MVVM を想定しています。
パラメータを利用するパターン
概要
このパターンではダイアログのVMのコンストラクタにパラメータを渡し、編集した結果を受け取る方式です。
例えば、以下のようなイメージでしょうか。
// NameとDescriptionを持つモデル
public IModel Model {get;set;}
public void ShowDialog()
{
// モデルの名前と説明を編集するダイアログの表示
// コンストラクタに渡した値が初期値となる想定
var vm = new XXXDialogViewModel(Model.Name, Model.Description);
var v = new XXXDialog() { DataContext = vm };
var result = v.ShowDialog();
if(result != null && result == false) return;
// ダイアログの編集結果を反映
Model.Name = vm.Name;
Model.Description = vm.Description;
}
メリット・デメリット
- メリット
- モデルとの依存なく作れるためシンプル
- デメリット
- 編集したいプロパティが増えるほどパラメータが増えていく
- ダイアログの編集結果を反映する手間がかかる
モデルをクローンして利用するパターン
このパターンではダイアログのVMのコンストラクタにモデルのクローンを渡し、編集した結果を受け取る方式です。
// NameとDescriptionを持つモデル
// クローン可能なメソッド(Clone()メソッド)を実装している想定
public IModel Model {get;set;}
public void ShowDialog()
{
// モデルの名前と説明を編集するダイアログの表示
// コンストラクタに渡したクローンを利用して編集結果を受け取る
var model = (IModel)Model.Clone();
var vm = new XXXDialogViewModel(model);
var v = new XXXDialog() { DataContext = vm };
var result = v.ShowDialog();
if(result != null && result == false) return;
// ダイアログの編集結果を反映
Model.Apply(model);
}
メリット・デメリット
- メリット
- パラメータが増えたり、あるいはモデル構造が階層構造で複雑なものだったりしても、モデルを渡しているので引数の変更が不要
- ダイアログの編集結果を反映するのも比較的シンプル
- デメリット
- クローン、反映処理にコストをかけてしまう可能性がある
- おそらくリフレクションを用いるのでそれもコストを書けてしまう要因のひとつになる
- モデルとの関連が生まれる
- クローン、反映処理にコストをかけてしまう可能性がある
DTOを利用するパターン
概要
このパターンではダイアログのVMのコンストラクタにモデルのDTOを渡し、編集した結果を受け取る方式です。
// NameとDescriptionを持つモデル
// DTOを生成できるメソッド(CreateDTO()メソッド)を実装している想定
public IModel Model {get;set;}
public void ShowDialog()
{
// モデルの名前と説明を編集するダイアログの表示
// コンストラクタに渡したDTOを利用して編集結果を受け取る
var dto = Model.CreateDTO();
var vm = new XXXDialogViewModel(dto);
var v = new XXXDialog() { DataContext = vm };
var result = v.ShowDialog();
if(result != null && result == false) return;
// ダイアログの編集結果を反映
Model.Apply(dto);
}
メリット・デメリット
DTOを利用した場合はパラメータとモデルをクローンして利用する場合の中間ぐらいのメリット・デメリットになるイメージです。
- メリット
- パラメータが増えたり、あるいはモデル構造が階層構造で複雑なものだったりしても、DTOを渡しているので引数の変更が不要
- ダイアログの編集結果を反映するのも比較的シンプル
- モデルをクローンするよりは軽量(なはず)
- モデルとの直接的な関連ができない
- デメリット
- クローン、反映処理に(モデルのクローンと比較すれば小さいはずだが)コストをかけてしまう可能性がある
- クラス数がDTOの分だけ増加するため、見通しが段々と悪くなる可能性がある。モデルと合わせてのメンテが必要になる可能性がある
おわりに
色々とパターンを紹介してきましたが、結局何がいいのか?については要件によるため難しいです。
ただ、一個人の意見としては、非機能要件が特別厳しいのでなければ正直モデルをクローンして使うで問題ないのかなとは思っています。
今回は色々設計案を整理するきっかけになったので、また疑問に思ったこと、初心者の頃によくわからなかったことを思い返して記事にしていきたいと思います。