Меню Рубрики

Mvp c windows forms

Паттерн MVP

— шаблон, который впервые появился в IBM, а затем использовался в Taligent в 1990-х. MVP является производным от MVC, при этом имеет несколько иной подход. В MVP представление не тесно связано с моделью, как это было в MVC. На следующей диаграмме показана структура MVP:

Как видно на этой диаграмме, Presenter занял место контроллера и отвечает за перемещение данных, введенных пользователем, а также за обновление представления при изменениях, которые происходят в модели. Presenter общается с представлением через интерфейс, который позволяет увеличить тестируемость, так как модель может быть заменена на специальный макет для модульных тестов. Также как и для MVC, давайте рассмотрим структуру MVP со стороны бизнес-логики приложения:

В данном случае структура MVP аналогична MVC в том плане, что код доступа к данным находится вне этой модели, а вся бизнес-логика приложения концентрируется в модели (эта схожесть подтверждает то, что MVP является производным от MVC, изменилась только логика представления).

Давайте рассмотрим пример приложения WPF использующего MVP, аналогичный тому, что был рассмотрен в предыдущих статьях (на примере приложения выбора проектов). Конечная структура приложения показана на следующей UML-диаграмме:

Представление использует класс ProjectsView который реализует интерфейс IProjectsView. ProjectsView и IProjectsView содержат все необходимые методы для обновления представления и связи с Presenter, который в свою очередь содержит ссылки на прототипы интерфейсов IProjectsView и IProjectsModel в виде переменных ProjectsPresenter.view и ProjectsPresenter.model. Такая конструкция обеспечивает взаимосвязь между моделью и представлением через Presenter.

Вот как это работает — если пользователь щелкнет на кнопке Update в пользовательском интерфейсе, сработает обработчик события IProjectView.ProjectUpdated, который вызовет метод ProjectsPresenter.view_ProjectUpdated() который обновит модель. При обновлении модели срабатывает обработчик события IProjectsModel.ProjectUpdated, который, через Presenter, обновляет графический интерфейс приложения.

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

Давайте теперь соберем это приложение (оно будет работать аналогично рассмотренному ранее приложению MVC).

Модель

Создайте новый проект WPF в Visual Studio, добавьте ссылку на сборку ProjectBilling.DataAccess, которая рассматривалась в статье RAD и Monolithic. Данная сборка реализует код доступа к данным и во всех рассмотренных проектах остается одинаковой. Добавьте класс ProjectsModel со следующим содержимым:

Класс модели ProjectsModel реализует интерфейс IProjectsModel со следующими членами:

UpdateProject() — этот метод позволяет обновлять проект в текущем состоянии сеанса. Этот метод может быть расширен для поддержки обновления через состояния, но здесь, для простоты это не рассматривается.

GetProjects() — метод, возвращает список проектов.

GetProject() — возвращает проект по ID.

ProjectUpdated — событие, которое вызывается, когда проект был обновлен.

ProjectEventArgs — вспомогательный класс члены которого могут использоваться в обработчике события ProjectUpdated.

Представление

Представление реализовано в файлах ProjectView.xaml и ProjectView.xaml.cs:

Presenter

Presenter включает в себя следующий код:

Здесь используются следующие обработчики событий:

view_DetailsUpdated — обработчик вызывается в ответ на событие IProjectsView.DetailsUpdated и просто вызывает SetEstimateColor() для обновления цвета TextBox со сметной стоимостью проекта. Это позволяет добавить простую логику для приложения.

view_SelectionChanged — обновляет представление при возникновении события IProjectsView.SelectionChanged (выбора нового проекта в ComboBox).

model_ProjectUpdated — вызывается в ответ на IProjectsModel.ProjectUpdate, также вызывая обновление модели и представления.

Главное окно приложения MainWindow.xaml просто позволяет запустить несколько окон с представлением ProjectsView:

Запустив это приложение, вы получите аналогичный результат, как и для MVC рассмотренного ранее. Главный интерес здесь представляет только структура Presenter, который используется в MVP.

Итак, MVP представляет собой большой шаг вперед по сравнению с MVC в нескольких направлениях:

MVP обеспечивает проверяемость состояния и логики представления, перемещая их в Presenter.

Представление жестко отделяется от модели, при этом связь организуется через Presenter. В отличие от MVC, MVP допускает повторное использование бизнес-логики без непосредственного видоизменения модели (за счет использования интерфейса IView). Например, если вы захотите перенести это WPF-приложение на Silverlight, вам потребуется только создать представление в Silverlight, реализующее IProjectsView, а IProjectsPresenter и IProjectsModel можно использовать повторно.

Источник

Как начать пользоваться MVP + WinForms?

Пишу приложение с использованием БД — Firebird. Компьютеры у людей не очень мощные и WPF там тормозит. Поэтому необходимо на WinForms (прощай удобный MVVM). Узнал что для удобной работы люди используют MVP.

Есть какой-то вводный материал или статьи нормальные на эту тему, или может собственный опыт у кого есть? Ибо самостоятельное выяснение нормальных результатов не дало.

2 ответа 2

Особенности реализации MVP для Windows Forms (тут, на мой взгляд, немного наворочено, но для ознакомления тоже подойдет)

Изложу также свой опыт.

Для каждого экрана должно быть три модуля: модель, представление и презентер.

  • Модель отвечает за работу с данными (загрузка/сохранение). Она является своеобразным фасадом к некоторому источнику данных или к слою доступа к данным. Ее задача — загружать и сохранять данные согласно задачам конкретного экрана.
  • Представление отвечает за пользовательский интерфейс. Это, по сути, и есть ваш экран. Представление вызывает методы презентера (например, в обработчиках событий), а также предоставляет методы для отображения данных (они вызываются презентером).
  • Презентер отвечает за взаимодействие между представлением (которое умеет только показывать данные и реагировать на действия пользователя) и моделью (которая знает только про данные). Как правило, это включает в себя логику представления данных, валидацию и другие вещи, тесно связанные с интерфейсом.

Направление ссылок получается следующим: представление презентер -> модель. Важно запомнить, что эта тройка нужна для каждого экрана. Это не что-то единое для всего приложения. Иногда возможны исключения: в случае необходимости единого управления несколькими экранами может быть несколько представлений/моделей и всего один презентер.

Например, на экране есть кнопка «Загрузить заказы». В обработчике кнопки вызывается соответствующий метод презентера — LoadOrders() . Внутри метода презентера идет обращение к модели, внутри модели непосредственно загружаются данные. После того, как презентер получил от модели данные, он может как-то преобразовать их для показа или выполнить над ними какую-то логику. После этого данные либо возвращаются из метода, либо — что более канонично — вызывается метод представления SetOrders() , внутри которого данные уже непосредственно загружаются в какой-либо контрол.

По коду получается следующая структура. Представление:

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

Вопросы из комментариев:

А как мне допустим если в таблице (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 — конкретный тип редактируемой сущности.

Источник

Паттерн MVP для WinForms

Паттерн MVC (Winforms + wpf)
Здравствуйте! Есть форма ColorForm.cs и UserControl1.xaml Подскажите пожалуйста как правильно.

Паттерн MVP
Начал реализовывать паттерн MVP в своем приложении. У меня возник вопрос. Неужели для каждой.

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

Паттерн или алгоритм для копирования
Добрый вечер. Задача такая. Есть две таблицы: А и Б. А пустая таблица, Б с объектами. .

Ну укажите тот класс который нужно вернуть (он же в модели есть),

Решение

Во-первых, где вы во вьюшке обращаетесь к модели?
У вас модель это IOrdersModel, которая содержит один метод LoadOrders. Вы во вьюшке где-то вызываете этот метод? Нет? Значит вы не обращаетесь к модели.

Во-вторых, часто путают модель из MVP и доменную модель. Доменная модель — это объекты предметной области. Иногда также называют бизнес-объектами. И это не тоже самое что Model из MVP. Например Order — это объект доменной модели. Объекты доменной модели могут быть доступны и в презентере и во вьюшке и в модели MVP. Т.о. доменные объекты могут передаваться в View. И View может брать оттуда данные для отображения (через свойства или поля, не важно). Однако View не должно вызывать методы этих классов (а в anemic архитектуре методов у доменных объектов вообще нет).
Иногда создают специальные промежуточные объекты — DTO (data transfer objects). Они фактически содержат те же данные, что и доменные объекты, но при этом в них нет методов и они предназначены для передачи в представление. DTO удобно использовать например для передачи по сети, что бы не передавать тяжеловесные доменные объекты, а также для разрыва прямой зависимости модели и представления на уровне dll.
Есть еще вариант, т.н. PassiveView — это вариант MVP когда во вьюшку передаются только примитивные типы (string, int и т.д.), без передачи доменных объектов, но как на мой взгляд это уже перебор.

Во всем этом деле важно понимать роли Model, Presenter и View во всем этом MVP. Если совсем кратко, то
Model — знает куда данные сохранять и откуда их брать.
View — знает как отобразить данные.
Presenter — знает как данные связаны и знает как их валидировать.

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

Да, именно так. Вы нажимаете кнопку, форма вызывает соотв. метод из своего презентера. Презентер создает другой презентер, передает ему данные, которые нужно отобразить (например тот Order который нужно редактировать). И тот другой презентер показывает свою форму и т.д.
Что касается модели, то строго говоря и модели для каждой формы должны быть свои (тут не про доменную модель, а про модель из MVP).

В целом, тут есть два момента:
1) Нужно вообще понимать, для чего вы используете MVP. Потому что MVP существенно увеличивает объем кода и плодит классы пачками. И в простых проектах это усложняет код. Если у вас не команда на 30 человек, если у вас не ПО с сотней форм, то следует найти себе вескую причину зачем вообще использовать MVP? Если такой причины нет, то MVP лучше и не использовать. По крайней мере, лично я использую MVP очень редко. В основном, в проектах WinForms, я использую простой подход Model-View (можно рассматривать это как вариант MVP, в котором презентер и вьюшка объеденены в одном классе). Иногда такой паттерн также называется Document-View. Если вы используете WPF или ASP, то конечно там нужны MVVM и MVC. Но винформы плохо подходят и к MVC и к MVP, и в формах эти паттерны на мой взгляд бесполезны.

2) При использовании MVP легко удариться в маразм, когда на MessageBox с одной кнопкой Ok создается модель, презентер и форма. Конечно такого делать не нужно. Малые диалоговые окна можно рассматривать просто как чаcть View. В конце концов, кто сказал что view может быть только в одной форме? View как хочет данные, так и отображает. В том числе и в выпадающих окошках — тоже.

Смысл данного интерфейса следующий. Для отображения объекта доменной модели T создается форма или контрол, реализующий интерфейс IView . Вызывающий код создает контрол/форму, показывает его, и вызывает метод Build в который передает объект данных, который нужно редактировать или отобразить. Логику о том, как отобразить объект и о том как его валидировать, берет на себя внутренняя реализация контрола (хотя валидацию можно отдать на откуп другим объектам — либо самой доменной модели, либо валидаторам). Вызывающий код должен лишь передать объект, который нужно редактировать, как и что будет с ним делать IView — его не волнует. Также, View реализует событие Changed, которое сигнализирует о том, что объект поменялся пользователем. Это событие нужно для того, что бы вызывающий код мог отобразить изменения редактируемого объекта и знал о том, что объект поменялся (это избавляет от необходимости реализации интерфейсов типа INotifyPropertyChanged в доменных объектах).
Этот паттерн хорошо совмещается с компонентной моделью, которая принята в winforms. Смысл в том, что для отображения доменных объектов создается отдельный контрол (UserControl), который одновременно и реализует IView.
При этом, внутри этого контрола могут содержаться другие контролы, которые также могут реализовать IVew.

Например, нам нужна форма, отображающая список заказов — Orders. Тогда создаем форму, реализуем в ней IView . Далее, вызывающий код(например главная форма) создает форму (возможно через фабрику), вызывает у нее Build и отображает. Далее, эта форма берет данные из переданного объекта типа Orders, и отображает его в своих контролах. Далее, допустим, юзер кликнул на заказ и хочет его отредактировать. Тогда эта форма, создает контрол (опять же через фабрику, возможно), который реализует IView . Этому контролу передается объект Order, который и будет отредактирован в этом контроле. Контрол форма отображает внутри себя. Если Order содержит в себе еще какие-то части, то этот контрол снова может создать внутри себя контролы для редактирования частей Oerder, которые тоже реализуют IView, и так далее. Также, контрол вызывает событие Changed, для уведомления внешнего кода о том, что объект поменялся. Вызывающая форма может отловить это событие, что бы обновить свой список Orders, который она отображает. Приняв событие о поменявшемся Order, она вызывает уже свое событие Changed, для информирования вышестоящих товарищей.

Итого. Такой подход прост в реализации и то же время достаточно гибок. Простота обеспечивается тем, что в нем нет промежуточных классов типа отдельного презентера, что упрощает код. А главное такой подход хорошо согласуется с событийной моделью winforms. Без бесконечных перевызовов событий в презентер и обратно, как нужно делать в MVP.
Кроме того, подход достаточно гибкий в плане тестирования. Функционал для отображения конкретного объекта доменной модели локализирован в отдельных контролах, и вы можете тестировать работу отдельного контрола независимо. Также поддерживается и DI. Например, вы можете создать фабрику, которая будет создавать различные контролы для отображения определенного типа данных доменной модели.

Почему я предпочитаю View-Model вместо MVP? Помимо вышеперчисленных плюсов, типа совместимости с winforms, можно провести следующие рассуждения:
Паттерн MVP обычно подразумевает, что для каждой формы вам нужно делать отдельную свою тройку Model-View-Presenter. Но тогда возникает вопрос — а каков в этом смысл, кроме эстетического удовольствия? Код распухает, там где была одна форма возникает три дополнительных класса и столько же интерфейсов. А главное — зачем это нужно? Конечно, логика отображения и связи данных разделяется по классам, это как бы хорошо. Но дело в том, что какое либо изменение модели или GUI интерфейса все равно ведет к изменению всех трех классов Model-View-Presenter. В теорию об их независимости я не верю изначально. На практике, при добавлении поля в доменный объект, вам придется менять и вьюшку (сделав там дополнительный чекбоксик), и модель, и презентер, который их связывает. По другому — никак. И спрашивается, а зачем это все нужно? Легче просто поменять код в форме (она же View), который хотя бы локализирован и находится в одном месте, чем лазить по трем классам и трем интерфейсам и заниматься там камасутрой.
Кроме того, нужно брать во внимание следующее — интерфейс одна из самых часто меняемых частей программы. Ядро типа BLL и DAL — вообще могут меняться редко в процессе развития программы. А вот интерфейсы — меняются часто. Поэтому подходить к проектированию интерфейсов слишком серьезно, с бесконечными интерфейсами, бесконечными презентерами и т.д. — на мой взгляд все равно что рисовать картины на песке. Первый же прибой (который будет не один) отправит всю красоту в мусорку.

Когда и чем хорош MVP? Думаю он годится и нужен в больших конторах, где сидят десятки программистов. И тогда можно распределить труд. Вроде того, что одни пишут презентеры, другие — модели, третьи — вьюшки. Да, суммарно количество кода будет больше чем при Model-View, но зато, возможно, они сделают все быстрее, поскольку над проектом может трудиться большее число людей.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

  • Mvk кодек для windows player
  • Mvci driver for toyota windows 10 x64
  • Mvc windows forms c
  • Mustek 12000 sp plus драйвер windows xp
  • Mustek 2448ta plus драйвер windows 7 x64