HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-21
1 <p><a>#статьи</a></p>
1 <p><a>#статьи</a></p>
2 <ul><li>28 окт 2019</li>
2 <ul><li>28 окт 2019</li>
3 <li>0</li>
3 <li>0</li>
4 </ul><p>MVP - это паттерн, который используют многие разработчики, даже если не догадываются об этом. Может, вы один из них?</p>
4 </ul><p>MVP - это паттерн, который используют многие разработчики, даже если не догадываются об этом. Может, вы один из них?</p>
5 <p> vlada_maestro / shutterstock</p>
5 <p> vlada_maestro / shutterstock</p>
6 <p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
6 <p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
7 <p>MVP - это паттерн программирования графических интерфейсов. В нём приложение делится на три компонента:</p>
7 <p>MVP - это паттерн программирования графических интерфейсов. В нём приложение делится на три компонента:</p>
8 <ul><li><strong>Model</strong>(Модель) работает с данными, проводит вычисления и руководит всеми бизнес-процессами.</li>
8 <ul><li><strong>Model</strong>(Модель) работает с данными, проводит вычисления и руководит всеми бизнес-процессами.</li>
9 <li><strong>View</strong>(Вид или представление) показывает пользователю интерфейс и данные из модели.</li>
9 <li><strong>View</strong>(Вид или представление) показывает пользователю интерфейс и данные из модели.</li>
10 <li><strong>Presenter</strong>(Представитель) служит прослойкой между моделью и видом.</li>
10 <li><strong>Presenter</strong>(Представитель) служит прослойкой между моделью и видом.</li>
11 </ul><p>Как и другие подобные паттерны (<a>MVC</a>,<a>MVVM</a>), MVP позволяет ускорить разработку и разделить ответственность разных специалистов; приложение удобнее тестировать и поддерживать.</p>
11 </ul><p>Как и другие подобные паттерны (<a>MVC</a>,<a>MVVM</a>), MVP позволяет ускорить разработку и разделить ответственность разных специалистов; приложение удобнее тестировать и поддерживать.</p>
12 <p>Чаще всего его используют разработчики мобильных приложений, однако он гораздо популярнее, чем может показаться. Дело в том, что этот паттерн активно применяется в вебе, хотя там его и называют MVC:</p>
12 <p>Чаще всего его используют разработчики мобильных приложений, однако он гораздо популярнее, чем может показаться. Дело в том, что этот паттерн активно применяется в вебе, хотя там его и называют MVC:</p>
13 Сравнение паттерна MVP и MVC в вебе<p>На схеме выше видно, что приложение, созданное по принципам MVP, работает с помощью связи модели, вида и представителя. Происходит это так:</p>
13 Сравнение паттерна MVP и MVC в вебе<p>На схеме выше видно, что приложение, созданное по принципам MVP, работает с помощью связи модели, вида и представителя. Происходит это так:</p>
14 <ol><li>Вид строит интерфейс и добавляет в него данные из модели.</li>
14 <ol><li>Вид строит интерфейс и добавляет в него данные из модели.</li>
15 <li>Пользователь видит информацию и взаимодействует с интерфейсом.</li>
15 <li>Пользователь видит информацию и взаимодействует с интерфейсом.</li>
16 <li>Вид перехватывает события и передаёт (делегирует) их представителю.</li>
16 <li>Вид перехватывает события и передаёт (делегирует) их представителю.</li>
17 <li>Представитель обрабатывает данные (не всегда) и передаёт их модели.</li>
17 <li>Представитель обрабатывает данные (не всегда) и передаёт их модели.</li>
18 <li>Модель выполняет какие-то операции и обновляется (меняет те или иные свойства).</li>
18 <li>Модель выполняет какие-то операции и обновляется (меняет те или иные свойства).</li>
19 <li>Представитель получает обновлённую модель и передаёт её виду.</li>
19 <li>Представитель получает обновлённую модель и передаёт её виду.</li>
20 <li>Вид строит интерфейс с новыми данными.</li>
20 <li>Вид строит интерфейс с новыми данными.</li>
21 </ol><p><strong>Основное отличие MVP и MVC</strong>в том, что в MVC обновлённая модель сама говорит виду, что нужно показать другие данные. Если же этого не происходит и приложению нужен посредник в виде представителя, то паттерн стоит называть MVP.</p>
21 </ol><p><strong>Основное отличие MVP и MVC</strong>в том, что в MVC обновлённая модель сама говорит виду, что нужно показать другие данные. Если же этого не происходит и приложению нужен посредник в виде представителя, то паттерн стоит называть MVP.</p>
22 <p>Всё это можно сравнить с работой издательства:</p>
22 <p>Всё это можно сравнить с работой издательства:</p>
23 <ol><li>Автор готовит текст (модель).</li>
23 <ol><li>Автор готовит текст (модель).</li>
24 <li>Текст получает издатель (представитель).</li>
24 <li>Текст получает издатель (представитель).</li>
25 <li>Если с текстом всё в порядке, издатель передаёт его в отдел вёрстки (вид).</li>
25 <li>Если с текстом всё в порядке, издатель передаёт его в отдел вёрстки (вид).</li>
26 <li>Верстальщики готовят книгу, которую начинают продавать читателям (пользователи).</li>
26 <li>Верстальщики готовят книгу, которую начинают продавать читателям (пользователи).</li>
27 <li>Если пользователи как-то реагируют на книгу, например, пишут письма в издательство, то работа может начаться заново. Допустим, кто-то может заметить в книге неточность, тогда издатель передаст информацию автору, автор её обновит и так далее.</li>
27 <li>Если пользователи как-то реагируют на книгу, например, пишут письма в издательство, то работа может начаться заново. Допустим, кто-то может заметить в книге неточность, тогда издатель передаст информацию автору, автор её обновит и так далее.</li>
28 </ol><p>Конечно, это не точный алгоритм работы издательства, но для иллюстрации принципов MVP его достаточно.</p>
28 </ol><p>Конечно, это не точный алгоритм работы издательства, но для иллюстрации принципов MVP его достаточно.</p>
29 <p>Так как MVP служит для упрощения разработки графических интерфейсов, рассмотреть его можно на примере WPF-приложения. В качестве вида будут выступать файлы<em>MainWindow.xaml</em>(разметка интерфейса) и <em>MainWindow.xaml.cs</em>(обработчик событий).</p>
29 <p>Так как MVP служит для упрощения разработки графических интерфейсов, рассмотреть его можно на примере WPF-приложения. В качестве вида будут выступать файлы<em>MainWindow.xaml</em>(разметка интерфейса) и <em>MainWindow.xaml.cs</em>(обработчик событий).</p>
30 <p>Начать можно с создания интерфейса авторизации:</p>
30 <p>Начать можно с создания интерфейса авторизации:</p>
31 &lt;Border HorizontalAlignment="Center" VerticalAlignment="Center" Width="250"&gt; &lt;StackPanel&gt; &lt;TextBlock Text="Login form" TextAlignment="Center"/&gt; &lt;DockPanel LastChildFill="True"&gt; &lt;TextBlock Text="Login:"/&gt; &lt;TextBox Name="LoginTextBox"/&gt; &lt;/DockPanel&gt; &lt;DockPanel LastChildFill="True"&gt; &lt;TextBlock Text="Password:"/&gt; &lt;PasswordBox Name="PassBox"/&gt; &lt;/DockPanel&gt; &lt;Button Content="Log in" Name="LoginButton" Click="LoginButton_Click" IsDefault="True"/&gt; &lt;TextBlock Name="MessageBlock"/&gt; &lt;/StackPanel&gt; &lt;/Border&gt;<p>Вот что должно получиться:</p>
31 &lt;Border HorizontalAlignment="Center" VerticalAlignment="Center" Width="250"&gt; &lt;StackPanel&gt; &lt;TextBlock Text="Login form" TextAlignment="Center"/&gt; &lt;DockPanel LastChildFill="True"&gt; &lt;TextBlock Text="Login:"/&gt; &lt;TextBox Name="LoginTextBox"/&gt; &lt;/DockPanel&gt; &lt;DockPanel LastChildFill="True"&gt; &lt;TextBlock Text="Password:"/&gt; &lt;PasswordBox Name="PassBox"/&gt; &lt;/DockPanel&gt; &lt;Button Content="Log in" Name="LoginButton" Click="LoginButton_Click" IsDefault="True"/&gt; &lt;TextBlock Name="MessageBlock"/&gt; &lt;/StackPanel&gt; &lt;/Border&gt;<p>Вот что должно получиться:</p>
32 На изображении к интерфейсу применены стили<p>Теперь можно приступить к файлу MainWindow.xaml.cs:</p>
32 На изображении к интерфейсу применены стили<p>Теперь можно приступить к файлу MainWindow.xaml.cs:</p>
33 public partial class MainWindow : Window { //Создание связи с другими компонентами private Model model; private Presenter presenter; public MainWindow() // Инициализация компонентов { InitializeComponent(); model = new Model(); presenter = new Presenter(model); } private void LoginButton_Click(object sender, RoutedEventArgs e) //Обработчик события нажатия на кнопку входа { this.model = this.presenter.Login(LoginTextBox.Text, PassBox.Password); //Отправка данных представителю и получение обновлённой модели Update(); //Вызов метода обновления интерфейса } private void Update() { MessageBlock.Text = this.model.Message; //Вывод сообщения из модели } }<p>Тут уже заложена необходимая связь между компонентами. Дальше стоит рассмотреть код представителя:</p>
33 public partial class MainWindow : Window { //Создание связи с другими компонентами private Model model; private Presenter presenter; public MainWindow() // Инициализация компонентов { InitializeComponent(); model = new Model(); presenter = new Presenter(model); } private void LoginButton_Click(object sender, RoutedEventArgs e) //Обработчик события нажатия на кнопку входа { this.model = this.presenter.Login(LoginTextBox.Text, PassBox.Password); //Отправка данных представителю и получение обновлённой модели Update(); //Вызов метода обновления интерфейса } private void Update() { MessageBlock.Text = this.model.Message; //Вывод сообщения из модели } }<p>Тут уже заложена необходимая связь между компонентами. Дальше стоит рассмотреть код представителя:</p>
34 public class Presenter { private Model model; //Связь с моделью public Presenter(Model model) { this.model = model; } public Model Login(string login, string password) //Получение данных от вида { login = login.Trim(); //Обработка полученной информации password = password.Trim(); this.model.Login(login, password); //Обновление модели return this.model; //Передача обновлённой модели } }<p>Он получает данные, обрабатывает их и передаёт модели. Модель обновляется, а представитель возвращает её виду.</p>
34 public class Presenter { private Model model; //Связь с моделью public Presenter(Model model) { this.model = model; } public Model Login(string login, string password) //Получение данных от вида { login = login.Trim(); //Обработка полученной информации password = password.Trim(); this.model.Login(login, password); //Обновление модели return this.model; //Передача обновлённой модели } }<p>Он получает данные, обрабатывает их и передаёт модели. Модель обновляется, а представитель возвращает её виду.</p>
35 <p>Вот что происходит в модели:</p>
35 <p>Вот что происходит в модели:</p>
36 public class Model { private List&lt;User&gt; users; private User loggedUser; public Model() { users = new List&lt;User&gt;(); //Создание списка тестовых пользователей users.Add(new User("Name1","Login1","password1")); users.Add(new User("Name2", "Login2", "password2")); users.Add(new User("Name3", "Login3", "password3")); users.Add(new User("Name4", "Login4", "password4")); loggedUser = null; } public void Login(string login, string password) //Метод авторизации { bool hasLogged = false; foreach (User user in this.users) { if (user.Login == login &amp;&amp; user.Password == password) //Поиск совпадений полученных данных с пользовательскими { this.loggedUser = user; //Обновление модели hasLogged = true; break; } } if (!hasLogged) { this.loggedUser = null; } } public string Message { get { return this.loggedUser != null ? $"Nice to see you {this.loggedUser.Name}!" : "Wrong login or password!"; //Вывод сообщения в зависимости от того, успешно ли был авторизован пользователь } } }<p>Тут появляется новая сущность -<em><strong>User</strong></em>. Этот и подобные классы используются в качестве данных, с которыми работает модель. Таких классов может быть сколько угодно: работа с ними ведётся так же, как и вне паттерна MVP. Вот и сам класс<em><strong>User</strong></em>:</p>
36 public class Model { private List&lt;User&gt; users; private User loggedUser; public Model() { users = new List&lt;User&gt;(); //Создание списка тестовых пользователей users.Add(new User("Name1","Login1","password1")); users.Add(new User("Name2", "Login2", "password2")); users.Add(new User("Name3", "Login3", "password3")); users.Add(new User("Name4", "Login4", "password4")); loggedUser = null; } public void Login(string login, string password) //Метод авторизации { bool hasLogged = false; foreach (User user in this.users) { if (user.Login == login &amp;&amp; user.Password == password) //Поиск совпадений полученных данных с пользовательскими { this.loggedUser = user; //Обновление модели hasLogged = true; break; } } if (!hasLogged) { this.loggedUser = null; } } public string Message { get { return this.loggedUser != null ? $"Nice to see you {this.loggedUser.Name}!" : "Wrong login or password!"; //Вывод сообщения в зависимости от того, успешно ли был авторизован пользователь } } }<p>Тут появляется новая сущность -<em><strong>User</strong></em>. Этот и подобные классы используются в качестве данных, с которыми работает модель. Таких классов может быть сколько угодно: работа с ними ведётся так же, как и вне паттерна MVP. Вот и сам класс<em><strong>User</strong></em>:</p>
37 public class User { private string name; private string login; private string password; public User(string name, string login, string password) { this.name = name; this.login = login; this.password = password; } public string Name { get { return this.name; } } public string Login { get { return this.login; } } public string Password { get { return this.password; } } }<p>Теперь можно проверить, как это всё работает:</p>
37 public class User { private string name; private string login; private string password; public User(string name, string login, string password) { this.name = name; this.login = login; this.password = password; } public string Name { get { return this.name; } } public string Login { get { return this.login; } } public string Password { get { return this.password; } } }<p>Теперь можно проверить, как это всё работает:</p>
38 <p>Тот, кто читал другие наши статьи о паттернах разработки графических интерфейсов, мог заметить, что во всех них приложение делится на 3 компонента, которые создаются отдельно. Такой подход позволяет быстрее написать отдельные части программы и при этом сделать их более простыми для понимания.</p>
38 <p>Тот, кто читал другие наши статьи о паттернах разработки графических интерфейсов, мог заметить, что во всех них приложение делится на 3 компонента, которые создаются отдельно. Такой подход позволяет быстрее написать отдельные части программы и при этом сделать их более простыми для понимания.</p>
39 <a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>
39 <a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>