На прошлой неделе я читал несколько статей об использовании WCF, структуре сущности и о том, как транспортировать объекты по всей границе обслуживания. Одна из статей, с которыми я столкнулся, включала демонстрационный проект, который использовал модель представления (MVP).
Изучив демонстрационный проект, я подумал, что было бы интересно написать статью об этом шаблоне. Ваша любимая поисковая система с радостью предоставит вам множество ссылок на другие статьи, которые подробно объясняют этот шаблон.
Для этой статьи я решил предоставить конкретное реализацию и не сосредоточиться на теории, стоящей за схемой.
Давай катимся ...
Конечно, требуется небольшая теория, поэтому давайте убраем это с дороги.
Как вы можете вычесть из имени, шаблон MVP состоит из трех различных частей: модель, представление и докладчик. Каждая из этих частей играет свою собственную роль в создании разделения проблем между презентацией, бизнесом и уровнем доступа к данным.
Модель отвечает за обработку доступа к данным, докладчик связывает с моделью и передает данные из и с ней. Представление получает данные от докладчика и передает данные обратно, он никогда не связывается напрямую с моделью. Докладчик-это промежуточный вид для представления и модели.
Рисунок 1 - Взаимодействие картины MVP

Изображение выше изображает представление как реализация интерфейса. Уровень презентации, будь то ASP.NET, Winforms или WPF -приложения, необходимые для реализации одного или нескольких интерфейсов просмотра. Докладчик, в свою очередь, общается с реализацией представления через этот интерфейс, он ничего не знает о самой реальной реализации.
Это обеспечивает свободную связь и предотвращает зависимость вашего кода от технологии, используемой для презентационного уровня. Идея состоит в том, что вы сможете заменить этот уровень, не влияя на ваш бизнес и логику доступа к данным.
Все это может показаться немного расплывчатым, но все должно прояснить, как мы перейдем к следующим моментам, которые обеспечивают фактическую реализацию этой модели. Для примера будет использоваться приложение ASP.NET.
Самое логичное место для начала - это модель. Поскольку он отвечает за обработку доступа к данным и хранилища, нам сначала необходимо создать хранилище физических данных. Для этого я использовал SQL Server 2005 Express, создал новую базу данных под названием Southwind и добавил одну таблицу под названием клиент. Таблица имеет 3 поля, а именно:
Вот и все для базы данных. Давайте запустим Visual Studio 2008 и создадим новое пустое решение под названием Avppattern. Затем добавьте библиотеку классов под названием «База данных».
Обычно я бы давал проекту название, которое следует за шаблоном «Компания. Продукт. Ливеррика», но ради простоты давайте будем держать его коротким и простым. Также удалите файл автогенерированного класса1.cs после добавления проекта в решение.
Давайте создадим модель из базы данных, используя структуру Entity (EF). Поэтому убедитесь, что вы используете Visual Studio 2008 и установили пакет Service 1 для Visual Studio и .NET Framework 3.5. Вы можете скачать пакеты услуг здесь.
Добавьте модель данных Entity Framework в библиотеку классов, выбрав Add, новый элемент, модель данных ADO.NET Entity из контекстного меню проекта в исследователе решения. Visual Studio теперь покажет мастер модели данных объекта. Назовите модель Southwind и позвольте Visual Studio генерировать модель для вас. Когда его спросили, какие объекты базы данных вы хотите включить в свою модель, просто выберите таблицу клиентов из узла таблицы.
Примечание : если вы не знакомы с созданием моделей данных с помощью структуры объекта, я настоятельно рекомендую это обучающее видео от Алекса Джеймса. Он показывает вам, как создать простую модель данных объекта с нуля.
На рисунке 2 показана полученная модель данных объекта. Модель не может стать намного проще, чем эта. Это делается специально для того, чтобы сделать вещи максимально простыми и сосредоточиться на шаблоне MVP.
Рисунок 2 - Модель данных объекта

Обязательно переименуйте свои EntityTypes и EntitySets под соответствующее имя после того, как модель данных была сгенерирована. Правило эмпирического правила состоит в том, чтобы использовать одно существительное для объекта и множественного числа для объектов. Таким образом, в этом случае наше предприятие должно быть названо клиентом и клиентами объекта. Имя для EntityType уже в порядке, так как оно наследует его от таблицы клиентов, так что просто выберите Customer EntityType и настройте свое свойство «Набор объектов».
Рисунок 3 - Имя набора объектов

Организация объекта автоматически генерирует частичный класс для таблицы клиентов. Вы можете продлить этот частичный класс клиентов, если хотите. Чтобы предотвратить удаление ваших пользовательских дополнений при восстановлении модели, поместите этот код в отдельный файл класса. Это приспособлено для работы с тесно напечатанными наборами данных. Для статьи это не требуется, однако.
Рисунок 4 - Решение исследователя

Теперь, когда модель на месте, давайте добавим бизнес -слой поверх нее, где мы можем определить нашу пользовательскую бизнес -логику. Ради простоты я оставлю количество кода в этом уровне.
Обратите внимание, что эти слои только обеспечивают логическое разделение (n-слой), а не физическое (n-уровне). Все слои находятся на одной и той же машине, хотя вы, безусловно, можете выбрать их по нескольким машинах / уровням и создать действительно распределенное или n-уровневое приложение.
Для настройки бизнес -уровня добавьте новую библиотеку классов в решение и назовите его бизнесом. Затем переименовать файл по умолчанию class.cs в customermanager.cs. Также добавьте ссылки на ранее созданную базу данных библиотеки классов и в сборку System.Data.Enterity.
В листинге 1 отображается класс CustomerManager, который содержит некоторую бизнес -логику для работы с субъектом клиента из модели данных объекта (EDM). Код в значительной степени сам по себе.
Листинг 1 - Класс Customer Manager
using System . Collections . Generic ;
using System . Linq ;
using Database ;
namespace Business
{
public class CustomerManager
{
private readonly SouthwindEntities context ;
#region Constructor(s)
public CustomerManager ( )
{
context = new SouthwindEntities ( ) ;
}
#endregion
#region Methods
// Retrieve a generic list of Customer entities.
// This method will return all the customers found in the Customer table.
public List < Customer > GetCustomers ( )
{
var q = from c in context . Customers
select c ;
return q . ToList ( ) ;
}
#endregion
}
}Рисунок 5 - Обновленное решение

Замечание : Настройка приложения N-уровня будет включать в себя введение уровня сервиса, к которому требуется презентационный уровень. Затем уровень обслуживания использует бизнес -объекты, найденные на бизнес -уровне. Между презентацией и бизнес -уровнем больше нет прямой связи, уровень обслуживания выступает как промежуточный. В будущей статье я рассмотрю это, показывая, как транспортировать EF -объекты по всей границе обслуживания.
Бизнес -слой содержит только один полезный метод, а именно «Список getCustomers ()». Докладчик в шаблоне MVP вызовет этот метод на бизнес -объекте CustomerManager, чтобы доставить данные в представление.
Пример приложения показывает только список клиентов, использующих шаблон MVP. Это может быть немного излишним, но оно сохраняется так же просто, как возможно, по дизайну. Основная цель этого "Hello World!" Тип приложения заключается в том, чтобы понять, как реализовать этот шаблон. Фактическая функциональность, предлагаемая приложением, не так важна.
Фактическая реализация представления (страница ASPX, Winforms, WPF ... и т. Д.) должна будет реализовать интерфейс представления. Реализация представления должна создать экземпляр докладчика и пройти сам в качестве параметра в своем конструкторе. Конструктор докладчика имеет один параметр, который является типом интерфейса представления.
В листинге 2 перечислены интерфейс IVIEW, который мы будем реализовать в ближайшее время на странице ASP.NET ASPX. У него есть одно событие под названием PrepareView. Событие PrepareView использует делегат, подпись которого указывает, что он ничего не возвращает и не принимает параметров.
Представление должно поднять только такие «пустые события», чтобы означать докладчику, что какое -то действие должно быть выполнено. Действие в этом случае означает, что докладчик должен обновить список клиентов, который поддерживает реализация представления. Докладчик может получить доступ к этому списку клиентов через недвижимость клиентов ILIST, которая объявляется как часть интерфейса.
Листинг 2 - Просмотр интерфейса
public delegate void VoidEventHandler ( ) ;
public interface IView
{
event VoidEventHandler PrepareView ;
IList < Customer > Customers { set ; }
}Чтобы добавить интерфейс View к вашему проекту, сначала добавьте новый проект библиотеки класса в решение под названием Presentation. Затем добавьте новый интерфейс и скопируйте и вставьте код, показанный в приведенном выше списке. Библиотека кодов презентации также будет содержать докладчиков и реализацию интерфейсов, которые они реализуют.
Я разделяю интерфейсы представления и докладчики в отдельной библиотеке кодов, чтобы вы могли легко поделиться ими между несколькими «структурами представления», такими как ASP.NET, Winforms, WPF ... и т. Д. На рисунке 6 показано, как я решил организовать эту библиотеку кода.
Рисунок 6 - Обновленное решение

Не забудьте добавить ссылки на базу данных и бизнес -проекты и сборку System.Data.Enterity.
Для последней части шаблона MVP мы должны предоставить докладчику. Класс клиентов -Presenter, показанный в списке 3, придерживается реализации IVIEW в своем конструкторе. Таким образом, он может общаться с взглядом, на самом деле ничего не зная о фактической реализации. Именно эта свободная связь делает шаблон MVP настолько подходящим для различных «структурных структур».
Также в конструкторе все события интерфейса представления подключены к обработчику событий. В этом случае есть только одно событие. Событие PrepareView подключено к обработчику события View_prepareview. Это, в свою очередь, вызывает частный метод докладчика getCustomers (), который возвращает «обновленную» коллекцию клиентов и назначает ее коллекции клиентов, поддерживаемой реализацией View.
Листинг 3 - Класс клиентов Presenter
public class CustomersPresenter : ICustomersPresenter
{
#region Fields
private readonly IView view ;
#endregion
#region Constructor(s)
public CustomersPresenter ( IView view )
{
// Save a reference to the view
this . view = view ;
// Hook up an event handler for the events of the view
view . PrepareView += view_PrepareView ;
}
#endregion
#region Private methods
private List < Customer > GetCustomers ( )
{
return new CustomerManager ( ) . GetCustomers ( ) ;
}
#endregion
#region ICustomersPresenter Members
public virtual void view_PrepareView ( )
{
view . Customers = GetCustomers ( ) ;
}
#endregion
}Приведенный выше докладчик также реализует интерфейс. В примере исходного кода этот интерфейс остается пустым. Я положил его туда только для иллюстративных целей. Вы можете уточнить этот интерфейс, если хотите. Возможно, вам может понадобиться, например, для ваших любимых модульных тестирования, чтобы насмехаться докладчики.
Таким образом, страница View или ASPX в нашем случае должна только реализовать интерфейс представления и запустить событие PrepareView, чтобы получить обновленный список клиентов от докладчика. Само представление не связано с базой данных или бизнес -уровнем напрямую. Докладчик обрабатывает связь с бизнес -уровнем, который получает данные, обращаясь к модели (или уровню доступа к данным, если хотите).
Чтобы закончить эту статью, давайте посмотрим, как все это объединяется в демонстрационном проекте ASP.NET. Добавьте новый проект, используя шаблон проекта веб -приложения ASP.NET в решение. Добавьте ссылки на проекты презентации и базы данных и сборку System.Data.Enterity.
Добавьте Gridview с именем «Gridview1» и кнопку с именем «Btnrefresh» на страницу default.aspx. Добавьте код в листинг 4 в код за страницей.
Листинг 4 - код default.aspx позади
public partial class _Default : System . Web . UI . Page , IView
{
private CustomersPresenter presenter ;
protected override void OnInit ( EventArgs e )
{
presenter = new CustomersPresenter ( this ) ;
}
protected void Page_Load ( object sender , EventArgs e )
{
if ( ! IsPostBack )
{
PrepareView ( ) ;
}
}
protected void btnRefresh_Click ( object sender , EventArgs e )
{
PrepareView ( ) ;
}
#region IView Members
public event VoidEventHandler PrepareView ;
public IList < Database . Customer > Customers
{
set
{
GridView1 . DataSource = value ;
GridView1 . DataBind ( ) ;
}
}
#endregion
}Весь код, стоящий за страницей ASPX, - это реализация интерфейса IVIEW, создать докладчика и передать реализацию IVIEW, будучи самим собой, в его конструктор. Тогда все, что остается, - это запустить событие PrepareView () интерфейса IVIEW в соответствующее время.
Во время создания докладчика обработчик событий автоматически назначен на это событие, которое гарантирует, что, когда он запускается, докладчик знает, как обновить коллекцию клиентов, поддерживаемую страницей. Сама страница ничего не знает о том, откуда поступают эти данные или как они получают. Чем лучше вид.
При просмотре этой страницы в браузере это результат:
Рисунок 7 - демонстрация веб -сайта ASP.NET

Примечание : не забудьте добавить ConnectionString, необходимую для объекта Framework в файл конфигурации Web.Config. Вы можете найти ConnectionString в файле App.config в библиотечном проекте класса базы данных. Он был вставлен там автоматически, когда Visual Studio сгенерировала модель данных объекта.
Конечно, строки соединения, поставляемые в образце исходного кода, не будут работать на вашем компьютере, поскольку они были построены с моей локальной базой данных. Так что обязательно настройте их соответственно.
Рисунок 8 - Обновленное решение

В качестве последнего шага этой статьи давайте создадим представление с помощью приложения Windows Forms, чтобы показать вам, насколько гибким является шаблон MVP. Шаги для этого практически идентичны предыдущему примеру создания веб -сайта ASP.NET. Добавьте новое приложение Windows Forms в свое решение и добавьте ссылки на базу данных и проект презентации и сборку System.Data.Enterity.
Затем добавьте управление DataGridView и кнопкой в форму. Код для формы отображается в списке ниже. Это почти идентично те, которая имеет страницу Default.aspx. Также не забудьте добавить строку подключения к Entity Framework в файл конфигурации app.config.
Листинг 5 - код form1.cs
using System ;
using System . Windows . Forms ;
using Presentation . Presenters ;
using Presentation . ViewInterfaces ;
namespace WindowsFormsApplication
{
public partial class Form1 : Form , IView
{
private CustomersPresenter presenter ;
public Form1 ( )
{
InitializeComponent ( ) ;
presenter = new CustomersPresenter ( this ) ;
}
private void Form1_Load ( object sender , EventArgs e )
{
PrepareView ( ) ;
}
private void btnRefresh_Click ( object sender , EventArgs e )
{
PrepareView ( ) ;
}
#region IView Members
public event VoidEventHandler PrepareView ;
public System . Collections . Generic . IList < Database . Customer > Customers
{
set
{
dataGridView1 . DataSource = value ;
}
}
#endregion
}
}Вуаля, мы наконец закончили. Обратите внимание, что, хотя пример ASP.NET и Winforms почти идентичен в коде, это может быть не так в более сложных приложениях. Типы взаимодействий в этих двух типах пользовательских интерфейсов существенно различны и придумывают докладчика, которого вы можете использовать во всех ситуациях, может быть не так четким.
Рисунок 9 - Демонстрация приложения Windows Forms

Рисунок 10 - Обновленное решение

Для этой статьи классическая интерпретация шаблона MVP была использована и продемонстрирована путем реализации его решением ASP.NET. Оригинальный шаблон MVP считается «в отставке» с тех пор, как Мартин Фаулер объявил об этом. Схема теперь можно разделить на два лагеря, будучи:
Прочитайте эту статью Microsoft Patterns & Practices для получения дополнительной информации.
Я написал эту статью, чтобы обеспечить конкретную реализацию шаблона MVP, когда я исследул ее в то время. Однако было бы разумно, чтобы избежать этого и подождать, пока Microsoft не выпустит фреймворк ASP.NET MVC. Для тех, кто хочет имитировать фреймворк-модель-контроллер (MVC), теперь я предлагаю прочитать эту статью по Microsoft Patterns & Practices.
Примечание : шаблон MVP является производной шаблона контроллера модели (MVC). На момент написания этой статьи Microsoft в настоящее время занята разработкой Framework ASP.NET MVC. При картировании архитектуры для нового проекта веб -сайта я предлагаю проверить эту структуру.
Основное различие между шаблонами MVP и MVC может быть определено тем, кто отвечает за обработку пользовательского ввода, такого как события клавиатуры и MUE. В схеме MVP сам GUI отвечает и должен делегировать их докладчику через события. В шаблоне MVC контроллер отвечает за обработку этих событий.