Ответ
Model-View-Presenter (MVP) — хороший выбор, когда нужна лучшая тестируемость и разделение ответственности по сравнению с MVC, но без сложности MVVM.
Ключевые сценарии использования:
- Проекты средней сложности – где бизнес-логика становится объемной, но реактивное программирование не требуется.
- Акцент на модульное тестирование – Presenter содержит логику и не зависит от UIKit, что делает его легким для тестирования.
- Четкое разделение слоев – View отвечает только за отображение, Presenter — за логику представления и обработку действий пользователя.
- Постепенная миграция с MVC – MVP логически следует за MVC, что упрощает рефакторинг.
Пример реализации на Swift:
// View Protocol
protocol LoginViewProtocol: AnyObject {
func showLoading(_ isLoading: Bool)
func showError(message: String)
func navigateToHome()
}
// Presenter
class LoginPresenter {
weak var view: LoginViewProtocol?
private let authService: AuthServiceProtocol
init(authService: AuthServiceProtocol) {
self.authService = authService
}
func loginButtonTapped(email: String, password: String) {
view?.showLoading(true)
authService.login(email: email, 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)
}
}
}
}
Недостаток: Необходимость ручного обновления View и потенциальное увеличение количества протоколов.
Ответ 18+ 🔞
Слушай, а вот этот ваш MVP — он, блядь, как золотая середина, если надоел этот бардак в MVC, но влезать в реактивщину MVVM ещё страшновато. Ну, знаешь, когда уже хочется порядок, но чтобы мозги не вытекали.
Где его, сука, пристраивать?
- Проекты, где уже не игрушки, но ещё не адский enterprise — логика начинает пухнуть, как на дрожжах, но связывать всё реактивными потоками — это овердохуища работы. MVP тут в самый раз.
- Когда начальство орет «Надо тесты, блядь!» — а вся логика у тебя в
ViewControllerприклеена кUIKit. Вот тут Presenter — спасение. Он же нихуя не знает про вьюхи, его голым юнитом потестил и спокоен, как удав. - Чтоб каждый сидел на своей жопе ровно — Вьюха только рисует и тыкает кнопками. Презентер — это такой умный посредник, который всё решает и отдаёт вьюхе готовенькое. Модель... Ну, модель как модель, с данными работает.
- Когда из говняного MVC надо вылезать потихоньку — MVP из той же оперы, только чище. Не надо всё ломать, можно по кусочкам перетаскивать логику в презентеры. Удобно, ёпта!
Смотри, как это выглядит в коде, на примере какой-нибудь авторизации:
// Протокол для Вьюхи. Чтоб не привязываться к конкретной UIViewController-ше.
protocol LoginViewProtocol: AnyObject {
func showLoading(_ isLoading: Bool) // Включи/выключи крутилку, ебанашка
func showError(message: String) // Покажи ошибку, если пользователь — криворукий
func navigateToHome() // Если всё ок — проваливай на следующий экран
}
// Сам Презентер, мозг операции.
class LoginPresenter {
weak var view: LoginViewProtocol? // Ссылка на вьюху, но через протокол, чтоб не привязаться намертво
private let authService: AuthServiceProtocol // Сервис для логина, инжектится, красота!
init(authService: AuthServiceProtocol) {
self.authService = authService
}
// Юзер тыкнул в кнопку "Войти". Вся логика здесь!
func loginButtonTapped(email: String, password: String) {
view?.showLoading(true) // Говорим вьюхе: "Хватит тыкать, жди!"
authService.login(email: email, 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) // Объясняем юзеру, что он опять облажался
}
}
}
}
А минусы-то где? Да вот же они, хитрая жопа! Надо будет вручную обновлять вьюху после каждого чиха. И этих протоколов для вьюх может развестись, как тараканов, если проект большой. Но за тестируемость и порядок иногда стоит и потерпеть, блядь.