Пишу приложение с использованием БД - Firebird. Компьютеры у людей не очень мощные и WPF там тормозит. Поэтому необходимо на WinForms (прощай удобный MVVM). Узнал что для удобной работы люди используют MVP.
Есть какой-то вводный материал или статьи нормальные на эту тему, или может собственный опыт у кого есть? Ибо самостоятельное выяснение нормальных результатов не дало.
Ответ
Немного ссылок:
Вводная от Википедии
Model-View-Presenter и сопутствующие паттерны
Особенности реализации MVP для Windows Forms (тут, на мой взгляд, немного наворочено, но для ознакомления тоже подойдет)
Изложу также свой опыт.
Для каждого экрана должно быть три модуля: модель, представление и презентер.
Модель отвечает за работу с данными (загрузка/сохранение). Она является своеобразным фасадом к некоторому источнику данных или к слою доступа к данным. Ее задача -- загружать и сохранять данные согласно задачам конкретного экрана
Представление отвечает за пользовательский интерфейс. Это, по сути, и есть ваш экран. Представление вызывает методы презентера (например, в обработчиках событий), а также предоставляет методы для отображения данных (они вызываются презентером).
Презентер отвечает за взаимодействие между представлением (которое умеет только показывать данные и реагировать на действия пользователя) и моделью (которая знает только про данные). Как правило, это включает в себя логику представления данных, валидацию и другие вещи, тесно связанные с интерфейсом.
Направление ссылок получается следующим: представление <-> презентер -> модель. Важно запомнить, что эта тройка нужна для каждого экрана. Это не что-то единое для всего приложения. Иногда возможны исключения: в случае необходимости единого управления несколькими экранами может быть несколько представлений/моделей и всего один презентер.
Например, на экране есть кнопка "Загрузить заказы". В обработчике кнопки вызывается соответствующий метод презентера -- LoadOrders(). Внутри метода презентера идет обращение к модели, внутри модели непосредственно загружаются данные. После того, как презентер получил от модели данные, он может как-то преобразовать их для показа или выполнить над ними какую-то логику. После этого данные либо возвращаются из метода, либо -- что более канонично -- вызывается метод представления SetOrders(), внутри которого данные уже непосредственно загружаются в какой-либо контрол.
По коду получается следующая структура. Представление:
interface IOrdersView
{
void SetOrders(Order[] orders);
}
class OrdersForm : Form, IOrdersView
{
private OrdersPresenter presenter;
public void OrdersForm()
{
...
// презентер можно создавать в конструкторе,
// а можно иметь отдельный метод инициализации экрана,
// который будет вызываться сразу после создания формы,
// создавать презентер и вызывать у него метод загрузки данных
presenter = new OrdersPresenter(this, new OrdersModel());
}
void btnLoadOrders_Click(...)
{
presenter.LoadOrders();
}
void IOrdersView.SetOrders(Order[] orders)
{
// загружаем данные в контрол
}
}
Презентер:
class OrdersPresenter
{
private readonly IOrdersView view;
private readonly IOrdersModel model;
OrdersPresenter(IOrdersView view, IOrdersModel model)
{
this.view = view;
this.model = model;
}
void LoadOrders()
{
var orders = model.LoadOrders();
view.SetOrders(orders);
}
}
Модель:
interface IOrdersModel
{
Order[] LoadOrders();
}
class OrdersModel : IOrdersModel
{
Order[] IOrdersModel.LoadOrders()
{
// тут логика по загрузке
}
}
Интерфейсы в принципе опциональны, но удобны для тестирования и моков.
Вопросы из комментариев:
А как мне допустим если в таблице (DataGridView) изменили запись,
записать эти изменения в базу. Сразу причем. То есть как только
закончили редактирование сразу в базу.
Отслеживаете событие изменения ячейки/строки, вызываете презентер, передав ему измененную запись, дальше презентер при необходимости валидирует и передает запись на сохранение модели. А модель уже обращается к слою доступа к данным (DAL'у).
В интерфейсе IOrdersView объявлен метод SetOrders(), который принимает
значение типа Order[]. Но что это за тип? Где он описан?
В данном случае это какой-то пользовательский тип. Важное тут -- что SetOrders() принимает некоторые данные, которые готовы для отображения и которые представление (в данном случае форма) знает, как отображать. Это может быть и DataTable, и массив строк. Что угодно.
Во View Вы создаете экземпляр класса OrdersPresenter и в качестве
параметра передаете экземпляр класса OrdersModel. Насколько это
укладывается в концепцию? Разве View и Model не должны быть развязаны
и ничего не знать друг о друге?
В идеале -- да, представление и модель должны быть развязаны и не должны ничего знать друг о друге. Создание экземпляра модели внутри представление является некоторым упрощением, и в целом втискивается в шаблон, поскольку представление не использует модель явным образом. Если же оставаться пуристом, то есть следующие варианты:
Создавать модель внутри презентера. Главный недостаток -- плохая
тестируемость презентера, поскольку невозможно подменить модель своей
реализацией (а в тестах на презентер она всегда подменяется). Обойти
это можно имея в презентере два конструктора -- один создает модель по умолчанию, второй -- принимает модель извне. Хотя и тут найдутся пуристы, утверждающие, что иметь специальные члены, которые используются только в тестах, плохо. Поэтому я иду по простому пути и всегда создаю модель в представлении.
Передавать в представление уже созданный презентер. Т.о. модель будет
инициализирована по крайней мере вне текущего представления. Однако
по большому счету это ничего не дает, т.к. текущее представление
будет открываться из другого представления, и теперь уже другому
представлению нужно будет что-то знать о модели.
Правильно я понял, что если у меня в программе 100 таблиц и мне нужно
100 форм для работы с ними, то для каждой формы я должен содать свой
интерфейс IOrdersModel и класс унаследованный от него, который может
быть уже не Orders, а Person, например и свой Presenter? Интерфейс
типа IOrdersView тоже для каждой формы создавать?
В общем случае да, для каждой XXXForm у вам должны быть XXXView, XXXPresenter и XXXModel. Однако если форм действительно много и они очень однотипные, то, возможно, достаточно будет обобщенных IView
Комментариев нет:
Отправить комментарий