MVP パッシブ ビュー - ビュー データとモデル データを分離する
-
10-10-2019 - |
質問
パッシブビューパターンを使用してMVPトライアドを実装しました。ビューには単純なゲッターとセッターのみが含まれます。しかし、ビューデータとモデルデータを分離するのに苦労しています。特にビューステートの変更を処理する場合。
トライアドは、ユーザーがリストからパーツを選択できるようにするために使用されます。パーツのリストはモデルによって提供され、各パーツは一意の ID によって一意に識別されます。
パーツが次のようになっているとします。
class Part
{
int ID; // this code uniquely identifies the part within the model
String partCode;
String description;
double voltage;
}
ビューはユーザーにリストを表示し、パーツを選択できるようにします。
リストは DataGridView に表示され、dataGridView 内の行をクリックしてパーツを選択します。
ID はユーザーに表示されず、電圧も表示されないため、モデルは、partCode と説明のみを含む DataTable を作成します。この DataTable は、プレゼンターによって、DataGridView の DataSource プロパティにマップされるビュー上のプロパティに割り当てられます。
class Presenter
{
IView _view;
IModel _model;
//...///
_view.Data = _model.GetFilteredData();
}
class Model
{
public DataTable GetFilteredData()
{
// create a DataTable with the partCode and Description columns only
// return DataTable
}
}
class View //winform
{
public DataTable Data
{
set
{
this.dataGridView.Source = value;
}
}
}
ここまでは順調ですね。ビューには、DataGridView でフィルター処理されたデータが表示されます。
私が抱えている問題は、ユーザーが選択した部分を返すことです。
ビューには一意の ID が表示されず、他の情報が一意であることが保証されないため、その ID が認識されません。そのため、選択されたパーツを一意に識別することはできません。
基本的に、1つのコンポーネントを使用せずに他のデータを使用してビューデータ(選択した行)をモデルデータ(選択した部分)に変換しようとしています。
これまでのところ、次の解決策があります。
1) ビューには、ID を含む DataTable が渡され、ユーザーに表示されないように表示がフィルターされます。これにより、選択した行の ID を返すことが簡単になります。ここでの問題は、テストされていないロジック (表示のフィルタリング) でビューを汚染していることです。
2) ビューは行インデックスを返し、モデルはこのインデックスを元のデータの行と照合します。これは、ビュー内の順序が決して変更されないようにすることを意味し、可能ではありますが、ビューでデータを表示 (および操作) できる方法が制限されます。これにより、モデルがビュー データ (行インデックス) で汚染されます。
public int RowIndexSelected { get; private set; }
//...//
private void gridParts_CellEnter(object sender, DataGridViewCellEventArgs e)
{
if (SelectedPartChangedEvent != null)
{
RowIndexSelected = e.RowIndex;
SelectedPartChangedEvent();
}
}
3) (2) のバリエーション。プレゼンターとビューの間に配置するアダプター オブジェクトを作成します。行から ID への変換コードをモデルからアダプターに移動します。次に、プレゼンターは dataGridAdapters パーツ変更イベントを処理します。
public PartSelectDataGridAdapter(IPartSelectView view, PartCollection data)
{
_view = view;
_data = data;
_view.SelectedPanelChangedEvent += HandleSelectedPartChanged;
}
void HandleSelectedPartChanged()
{
int id = _data[_view.RowIndexSelected].ID;
if (SelectedPartChanged != null)
{
SelectedPartChanged(id);
}
}
現時点では、テスト可能であり、ロジックがビューから除外され、データがモデルとプレゼンターから除外されるため、3 に向けて学習しています。
これにどう対処しますか?もっと良い解決策はありますか?
解決
IDはユーザーに表示されることはなく、電圧も表示されないため、モデルはパートコードと説明のみを含むデータテーブルを作成します。
簡単な解決策: する データテーブルにID列を作成し、 データグリッドビューで非表示にします.
他のヒント
以前に簡単な解決策を投稿しました。これは質問に対するより詳細な回答です
合格したくない理由はありますか List<Part>
景色に?
ID と電圧の列を非表示にするようにグリッドを構成できます。選択したオブジェクトをビュー内のバインディング ソースから簡単に取得できます。プレゼンターは、この選択についてビューにクエリを実行することも、ビューが SelectionChanged(Part selected)
プレゼンターについて。
それはあなたがもはや厳密に従っていないことを意味します 受動的な見方 パターンですが、 統括コントローラー, これでビューがモデルについて認識したためです。
これが気に入らない場合は、 ビューモデル, これはすでに DataTable で暗黙的に実行されています。(ちなみに、これは必ずしも悪いことではありません。)
あなたの例では、ビューモデルを生成するメソッドがモデルにあるため、モデルクラスはビューモデルについて知っています。この関係を逆にすることをお勧めします。モデル オブジェクトに依存するメソッドを View Model に作成します。こうすることで、モデル クラスを適切でクリーンな状態に保ち、プレゼンテーション層で必要なすべての UI データから独立した状態に保つことができます。
ビュー モデル/監視コントローラーの方法を使用する場合は、単純なクラスを優先して DataTable の概念を削除することを検討してください。
編集:ビューがモデルを完全に無視するようにする別の方法:
モデルとビュー モデルの両方がわかっているプレゼンターでこのクラスのインスタンスを構築します。
public class PartViewModel
{
object PartModel { get; set; }
string Name { get; set; }
string Description { get; set; }
}
渡す List<PartViewModel>
DataGridView へのデータソースとして。選択した PartViewModel オブジェクトをプレゼンターに返すことができます (イベントまたはメソッドを使用して)。プレゼンターは、PartModel プロパティを Part インスタンスにキャストして戻すことができることを知っています。あなたが好むと言うように、ビューはモデルについて何も知る必要はありません。ただし、プレゼンターでは引き続き単純なオブジェクト ID を使用でき、ID を使用した「複雑な」検索を回避できます。
プレゼンターのコールバックを使用する場合:
interface IPartListPresenter
{
// other methods
void SelectedPartChanged(PartViewModel nowSelected);
}
partBindingSource がグリッドビューが接続されているバインディングソースであると仮定すると、partBindingSource の CurrentChanged イベントを次のように処理できます。
private void partBindingSource_CurrentChanged(object sender, EventArgs e)
{
_presenter.SelectedPartChanged(partBindingSource.Current as PartViewModel);
}
プレゼンター内:
public void SelectedPartChanged(PartViewModel nowSelected)
{
if(nowSelected == null)
{
return;
}
part myPart = (Part) nowSelected.Part;
// dos stuff
}
お役に立てれば。
ここで全体の概念を少し誤解していると思います。
これを処理するのはモデルではなくプレゼンターです。モデルは自分自身の責任のみに集中すべきです。そうでない場合は、ビューとモデルを近づけすぎます。
私の提案は、テーブルに非表示の列を保持し、選択した行のイベントをプレゼンターに渡し、プレゼンターに処理を任せることです。
これがMVPの正しい使い方になります。