iOS
MVP-architectuur
Zoeken…
Invoering
MVP is een architecturaal patroon, een afleiding van de Model – View – Controller. Het wordt weergegeven door drie verschillende componenten: Model, View en de Presenter. Het is ontworpen om geautomatiseerde unit-testen te vergemakkelijken en de scheiding van problemen in presentatielogica te verbeteren.
In voorbeelden vindt u een eenvoudig project gebouwd met het MVP-patroon in gedachten.
Opmerkingen
componenten:
- Model is een interface die verantwoordelijk is voor de domeingegevens (die moeten worden weergegeven of anderszins opgevolgd in de GUI)
- Weergave is verantwoordelijk voor de presentatielaag (GUI)
- Presenter is de "middelman" tussen Model en Beeld. Het reageert op de acties van de gebruiker die worden uitgevoerd in de weergave, haalt gegevens op uit het model en maakt deze op voor weergave in de weergave
Componenttaken:
Model | Visie | Presentator |
---|---|---|
Communiceert met DB-laag | Gegevens worden weergegeven | Hiermee voert u zoekopdrachten uit naar het model |
Geschikte evenementen organiseren | Ontvangt evenementen | Formatteert gegevens van Model |
Zeer eenvoudige validatielogica | Verzendt geformatteerde gegevens naar de weergave | |
Complexe validatielogica | ||
Verschillen tussen MVC en MVP :
- View in MVC is nauw gekoppeld aan de Controller, het View-gedeelte van de MVP bestaat uit zowel UIViews als UIViewController
- MVP View is zo dom mogelijk en bevat bijna geen logica (zoals in MVVM), MVC View heeft wat bedrijfslogica en kan het model opvragen
- MVP View verwerkt gebruikersgebaren en delegeert interactie naar de Presenter, in MVC verwerkt de Controller gebaren en opdrachten Model
- MVP-patroon ondersteunt in hoge mate Unit Testing, MVC heeft beperkte ondersteuning
- MVC Controller heeft veel UIKit-afhankelijkheden, MVP Presenter heeft er geen
Voors:
- MVP maakt UIViewController een onderdeel van de View-component, het is dom, passief en ... minder massief;]
- Het grootste deel van de bedrijfslogica is ingekapseld vanwege de domme weergaven, dit geeft een uitstekende testbaarheid. Er kunnen onechte objecten worden geïntroduceerd om het domeingedeelte te testen.
- Gescheiden entiteiten zijn gemakkelijker in hoofd te houden, verantwoordelijkheden zijn duidelijk verdeeld.
Cons
- Je zult meer code schrijven.
- Barrière voor onervaren ontwikkelaars of voor degenen die nog niet met het patroon werken.
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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow