МВП пассивных целях учета представления и модели данных отдельных


Я реализовал триаду MVP, используя паттерн пассивного представления, то есть представление содержит только простые геттеры и сеттеры. Однако у меня возникли проблемы с разделением данных представления и данных модели. В частности, при обработке изменения состояния представления.

Триада используется для того, чтобы пользователь мог выбрать деталь из списка. Список деталей поставляется моделью, причем каждая деталь однозначно идентифицируется уникальным идентификатором.

Предположим, что части выглядят следующим образом:

class Part
{
    int ID; // this code uniquely identifies the part within the model
    String partCode;
    String description;
    double voltage;
}

В вид отображает список пользователю и позволяет ему выбрать часть

Список отображается в представлении DataGridView, а часть выбирается щелчком по строке в представлении dataGridView.

Идентификатор не должен отображаться пользователю, как и напряжение, поэтому модель создает DataTable, который содержит только код детали и описание. Этот объект DataTable присваивается презентатором свойству представления, которое сопоставляется со свойством 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.

Проблема, с которой я столкнулся, заключается в возврате части, выбранной пользователем.

Представление не знает уникального идентификатора, поскольку он не отображается, а другая информация не может быть гарантирована уникальностью - поэтому невозможно однозначно идентифицировать выбранную деталь.

По сути, я пытаюсь преобразовать данные представления (выделенную строку) в данные модели (выделенную часть) без одного компонента, использующего другие данные.

Пока у меня есть следующие решения:

1) представление передается DataTable, который содержит идентификатор, а затем фильтрует отображение, чтобы оно не отображалось пользователю. В этом случае тривиально возвращать идентификатор для выбранной строки. Проблема здесь заключается в том, что я теперь загрязнил вид непроверенной логикой (фильтрация дисплея).

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 part changed.

    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, так как оно тестируемо, удерживает логику вне представления и данные представления вне модели и докладчика.

Как бы вы справились с этим - есть ли лучшее решение?

3 3

3 ответа:

Идентификатор не должен отображаться в потребитель и ни напряжение тока, поэтому модель создает DataTable, который содержит только код детали и описание.

Простое решение: Создайте столбец ID в datatable и спрячьте его в представлении datagrid.

Ранее я опубликовал простое решение; это более подробный ответ на вопрос

Есть ли причина, по которой вы не хотите передавать вид List<Part>?

Вы можете настроить сетку, чтобы скрыть столбец идентификатора и напряжения. Вы можете просто получить выбранный объект из источника привязки в представлении. Презентатор может запросить представление для этого выбора, или представление может вызвать SelectionChanged(Part selected) на презентаторе.

Это будет означать, что вы больше не строго следуете пассивный вид шаблон, ноконтролирующий контроллер , потому что теперь ваш вид знает о модели.

Если вам это не нравится, вы можете ввести модель представления , что вы уже неявно делаете с вашим DataTable. (Кстати, это не обязательно плохо.)

В вашем примере классы моделей знают о моделях представления, потому что у вас есть методы в модели, которые их генерируют. Я бы посоветовал вам обратить это отношение вспять: создайте методы на вашем представлении модели, зависящие от объектов вашей модели. Таким образом, вы будете держать свои классы моделей красивыми и чистыми и независимыми от всех данных пользовательского интерфейса, необходимых в слое представления.

При использовании способа представления модели / контролирующего контроллера рассмотрите возможность отказа от концепции DataTable в пользу простых классов.

EDIT: альтернатива, чтобы сделать вид полностью неосведомленным о модели:

Создайте экземпляр этого класса в presenter, где вы знаете как модель, так и представление модель:

public class PartViewModel
{
  object PartModel { get; set; }
  string Name { get; set; }
  string Description { get; set; }
}

Передайте List<PartViewModel> в качестве источника данных в DataGridView. Выбранный объект PartViewModel можно вернуть презентатору (либо с помощью события, либо с помощью метода). Ведущий знает, что он может привести свойство PartModel обратно к экземпляру детали. Вид не должен ничего знать о модели, как вы говорите, что предпочитаете. Но вы все еще можете использовать простое удостоверение объекта в презентаторе, избегая "сложного" поиска с помощью идентификаторов.

С ведущим обратный вызов:

interface IPartListPresenter
{
  // other methods
  void SelectedPartChanged(PartViewModel nowSelected);
}

Предполагая, что partBindingSource - это bindingsource, к которому подключен gridview, вы можете обрабатывать событие CurrentChanged partBindingSource следующим образом:

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.