iOS
Architecture MVP
Recherche…
Introduction
MVP est un modèle architectural, une dérivation du modèle – View – Controller. Il est représenté par trois composants distincts: Modèle, Vue et Présentateur. Il a été conçu pour faciliter les tests unitaires automatisés et améliorer la séparation des problèmes dans la logique de présentation.
Dans des exemples, vous trouverez un projet simple conçu en fonction du modèle MVP.
Remarques
Composants:
- Le modèle est une interface responsable des données du domaine (à afficher ou à utiliser dans l'interface graphique)
- View est responsable de la couche de présentation (GUI)
- Le présentateur est le "intermédiaire" entre le modèle et la vue. Il réagit aux actions de l'utilisateur effectuées sur la vue, récupère les données du modèle et les formate pour les afficher dans la vue.
Devoir du composant:
Modèle | Vue | Présentateur |
---|---|---|
Communique avec le calque DB | Rend les données | Effectue des requêtes sur le modèle |
Élever des événements appropriés | Reçoit des événements | Données de format du modèle |
Logique de validation très basique | Envoie des données formatées à la vue | |
Logique de validation complexe | ||
Différences entre MVC et MVP :
- View in MVC est étroitement lié au Controller, la partie View du MVP comprend à la fois UIViews et UIViewController
- MVP View est aussi stupide que possible et ne contient pratiquement aucune logique (comme dans MVVM), MVC View possède une certaine logique métier et peut interroger le modèle
- MVP View gère les gestes des utilisateurs et délègue les interactions au présentateur, dans MVC, le contrôleur gère les gestes et les commandes du modèle.
- Le modèle MVP supporte fortement les tests unitaires, MVC a un support limité
- MVC Controller a beaucoup de dépendances UIKit, MVP Presenter n'en a pas
Avantages:
- MVP fait de UIViewController une partie du composant View, il est stupide, passif et moins massif;]
- La plupart de la logique métier est encapsulée en raison des vues stupides, ce qui donne une excellente testabilité. Des objets simulés peuvent être introduits pour tester la partie domaine.
- Les entités séparées sont plus faciles à garder en tête, les responsabilités sont clairement divisées.
Les inconvénients
- Vous allez écrire plus de code.
- Barrière pour les développeurs inexpérimentés ou pour ceux qui ne travaillent pas encore avec le modèle.
Dog.swift
import Foundation
enum Breed: String {
case bulldog = "Bulldog"
case doberman = "Doberman"
case labrador = "Labrador"
}
struct Dog {
let name: String
let breed: String
let age: Int
}
DoggyView.swift
import Foundation
protocol DoggyView: NSObjectProtocol {
func startLoading()
func finishLoading()
func setDoggies(_ doggies: [DoggyViewData])
func setEmpty()
}
DoggyService.swift
import Foundation
typealias Result = ([Dog]) -> Void
class DoggyService {
func deliverDoggies(_ result: @escaping Result) {
let firstDoggy = Dog(name: "Alfred", breed: Breed.labrador.rawValue, age: 1)
let secondDoggy = Dog(name: "Vinny", breed: Breed.doberman.rawValue, age: 5)
let thirdDoggy = Dog(name: "Lucky", breed: Breed.labrador.rawValue, age: 3)
let delay = DispatchTime.now() + Double(Int64(Double(NSEC_PER_SEC)*2)) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: delay) {
result([firstDoggy,
secondDoggy,
thirdDoggy])
}
}
}
DoggyPresenter.swift
import Foundation
class DoggyPresenter {
// MARK: - Private
fileprivate let dogService: DoggyService
weak fileprivate var dogView: DoggyView?
init(dogService: DoggyService){
self.dogService = dogService
}
func attachView(_ attach: Bool, view: DoggyView?) {
if attach {
dogView = nil
} else {
if let view = view { dogView = view }
}
}
func getDogs(){
self.dogView?.startLoading()
dogService.deliverDoggies { [weak self] doggies in
self?.dogView?.finishLoading()
if doggies.count == 0 {
self?.dogView?.setEmpty()
} else {
self?.dogView?.setDoggies(doggies.map {
return DoggyViewData(name: "\($0.name) \($0.breed)",
age: "\($0.age)")
})
}
}
}
}
struct DoggyViewData {
let name: String
let age: String
}
DoggyListViewController.swift
import UIKit
class DoggyListViewController: UIViewController, UITableViewDataSource {
@IBOutlet weak var emptyView: UIView?
@IBOutlet weak var tableView: UITableView?
@IBOutlet weak var spinner: UIActivityIndicatorView?
fileprivate let dogPresenter = DoggyPresenter(dogService: DoggyService())
fileprivate var dogsToDisplay = [DoggyViewData]()
override func viewDidLoad() {
super.viewDidLoad()
tableView?.dataSource = self
spinner?.hidesWhenStopped = true
dogPresenter.attachView(true, view: self)
dogPresenter.getDogs()
}
// MARK: DataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dogsToDisplay.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell")
let userViewData = dogsToDisplay[indexPath.row]
cell.textLabel?.text = userViewData.name
cell.detailTextLabel?.text = userViewData.age
return cell
}
}
extension DoggyListViewController: DoggyView {
func startLoading() {
spinner?.startAnimating()
}
func finishLoading() {
spinner?.stopAnimating()
}
func setDoggies(_ doggies: [DoggyViewData]) {
dogsToDisplay = doggies
tableView?.isHidden = false
emptyView?.isHidden = true;
tableView?.reloadData()
}
func setEmpty() {
tableView?.isHidden = true
emptyView?.isHidden = false;
}
}
Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow