Ответ
MVP (Model-View-Presenter) — это архитектурный паттерн, разделяющий ответственность в приложении на три компонента для улучшения тестируемости и разделения кода.
Компоненты:
- Model — содержит бизнес-логику и данные приложения.
- View — пассивный интерфейс, который только отображает данные и передает пользовательские события Presenter'у. Обычно реализуется
UIViewControllerилиUIViewв iOS. - Presenter — посредник между Model и View. Он получает события от View, обрабатывает их (используя Model), подготавливает данные и обновляет View.
Ключевое отличие от MVC: View в MVP пассивна и ничего не знает о Model. Вся логика отображения и обработки событий перемещена в Presenter, что разрывает прямую связь между View и Model.
Пример на Swift:
// 1. Протокол для View (интерфейс)
protocol LoginViewProtocol: AnyObject {
func showLoading(_ isLoading: Bool)
func showError(message: String)
func navigateToHome()
}
// 2. Presenter
class LoginPresenter {
weak var view: LoginViewProtocol?
private let authService: AuthService // Model-слой
func didTapLoginButton(username: String, password: String) {
view?.showLoading(true)
authService.login(username: username, password: password) { [weak self] result in
self?.view?.showLoading(false)
switch result {
case .success:
self?.view?.navigateToHome()
case .failure(let error):
self?.view?.showError(message: error.localizedDescription)
}
}
}
}
// 3. View (ViewController)
class LoginViewController: UIViewController, LoginViewProtocol {
var presenter: LoginPresenter!
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBAction func loginButtonTapped(_ sender: Any) {
presenter.didTapLoginButton(username: usernameTextField.text ?? "",
password: passwordTextField.text ?? "")
}
// Реализация протокола LoginViewProtocol
func showLoading(_ isLoading: Bool) { /* ... */ }
func showError(message: String) { /* ... */ }
func navigateToHome() { /* ... */ }
}
Преимущества: Высокая тестируемость Presenter'а (без UIKit), четкое разделение ответственности.
Ответ 18+ 🔞
Давай я тебе про этот MVP разжую, а то смотрю, народ путается, как хуй с пальцем. Архитектурный паттерн, блядь, звучит страшно, а на деле всё просто, как три копейки.
Представь себе такую картину. У тебя есть Модель (Model) — это типа наш молчаливый качок, Герасим. Он нихуя не говорит, но всю реальную работу делает. Данные там держит, с сетью общается, бизнес-логику ебашит. Молчит и пашет.
Потом у нас Вью (View) — это наша барышня-истеричка, которая только умеет кричать «ой, кнопку нажали!» или «ой, покажи мне вот эти цифры красиво!». Сама она нихуя не решает, просто интерфейс, лицо приложения. В iOS это обычно наш UIViewController, который превратился из бога в послушную куклу.
И наконец, Презентер (Presenter) — это главный режиссер-постановщик, ебаный мозг всего этого цирка. Он стоит посередине. Вью ему: «Чувак, юзер логин ввел и на кнопку ебнул!». Презентер такой: «Окей, заткнись, я всё решу». Идёт к молчаливому Герасиму (Модели): «Братан, проверь логин/пароль». Получает ответ и командует Вью: «Всё ок, включай крутилку!» или «Э, сука, ошибка, покажи алерт!».
В чём соль, спросишь? А в том, что в классическом MVC наша Вьюха (ViewController) была и режиссёром, и актёром, и грузчиком — пиздец бардак, протестировать ничего нельзя. А тут мы Вьюху опустили до уровня тупой отрисовки. Вся логика, вся «думалка» теперь в Презентере, который нихуя не знает про UIKit и его можно нахуй протестировать юнит-тестами, как миленького. View и Model друг про друга не знают вообще — красота!
Смотри, как это в коде выглядит, без всякой ерунды:
// 1. Это наш контракт для Вьюхи. Чёткий список: «Чего ты от меня хочешь, Презентер?»
protocol LoginViewProtocol: AnyObject {
func showLoading(_ isLoading: Bool) // Покажи крутилку, сука
func showError(message: String) // Ошибку высрала, покажи
func navigateToHome() // Всё чики-пуки, иди на след. экран
}
// 2. А вот и наш царь и бог — Презентер
class LoginPresenter {
weak var view: LoginViewProtocol? // Он знает только про интерфейс Вьюхи, а не про всю её потрошную
private let authService: AuthService // А это наш Герасим (Модель)
// Сюда прилетает событие от Вьюхи
func didTapLoginButton(username: String, password: String) {
view?.showLoading(true) // Командуем Вьюхе: «Включай крутилку, не отвлекай!»
authService.login(username: username, password: password) { [weak self] result in
self?.view?.showLoading(false) // «Выключай уже!»
switch result {
case .success:
self?.view?.navigateToHome() // «Всё ок, вали на следующий экран!»
case .failure(let error):
// «Обосралось что-то, покажи пользователю эту хуйню!»
self?.view?.showError(message: error.localizedDescription)
}
}
}
}
// 3. И наша Вьюха-кукла (ViewController)
class LoginViewController: UIViewController, LoginViewProtocol {
var presenter: LoginPresenter! // Она знает своего Презентера
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
// Пользователь тыкнул кнопку — Вьюха не думает, она стучится к Презентеру
@IBAction func loginButtonTapped(_ sender: Any) {
presenter.didTapLoginButton(username: usernameTextField.text ?? "",
password: passwordTextField.text ?? "")
}
// Далее она просто тупо выполняет команды Презентера, как обученная обезьянка
func showLoading(_ isLoading: Bool) { /* Включаем/выключаем крутилку */ }
func showError(message: String) { /* Показываем алерт с текстом */ }
func navigateToHome() { /* Переходим куда надо */ }
}
Итог, ёпта? Тестируемость — овердохуищная, потому что Презентер — это просто обычный класс, можно его отвязать от Вьюхи и тестировать логику в изоляции. Ответственность разделена — каждый делает своё, не лезет в чужой монастырь. Красота, да и только! Главное — не давай Вьюхе задумываться, а то опять в старый бардак скатишься.