Пишу приложение с использованием БД - 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, Presenter, IModel, где T -- конкретный тип редактируемой сущности.