Come scegliere l'architettura iOS appropriata (parte 2)

MVC, MVP, MVVM, VIPER o VIP

Puoi consultare la prima parte qui.

Le principali architetture iOS

Una breve panoramica.

MVC

I livelli MVC sono i seguenti:

M: Business Logic, Network Layer e Data Access Layer

V: UI Layer (cose UIKit, Storyboard, Xibs)

C: coordina la mediazione tra Modello e Vista.

Per capire MVC dobbiamo capire il contesto in cui è stato inventato. MVC è stato inventato ai vecchi tempi di sviluppo Web, dove Views non ha stato. Ai vecchi tempi ogni volta che abbiamo bisogno di un cambiamento visivo nel sito Web, il browser ricarica nuovamente l'intero HTML. All'epoca non esisteva il concetto di mantenimento e salvataggio dello stato di visualizzazione.

Ad esempio, c'erano alcuni sviluppatori che si mescolavano all'interno dello stesso file HTML, PHP e accesso al database. Quindi la motivazione principale di MVC era quella di separare il livello View dal livello Model. Ciò ha aumentato la testabilità del livello Modello. Presumibilmente in MVC, il livello View and Model non dovrebbe sapere nulla l'uno dell'altro. Per rendere ciò possibile, è stato inventato un livello intermedio chiamato Controller. Questo è stato il SRP che è stato applicato.

Un esempio del ciclo MVC:

  1. Un'azione / evento dell'utente nel livello di visualizzazione (ad es. Aggiorna azione) viene generato e tale azione viene comunicata al controller
  2. Il controller che richiede i dati al Model Layer
  3. Modella i dati restituiti al controller
  4. Il controller dice che per la vista aggiorna il suo stato con i nuovi dati
  5. Visualizza aggiorna il suo stato

Apple MVC

In iOS, View Controller è accoppiato a UIKit e alla vista del ciclo di vita, quindi non è MVC puro. Tuttavia, nella definizione MVC, non c'è nulla da dire che il controller non può conoscere l'implementazione specifica di View o Model. Il suo scopo principale è quello di separare le responsabilità del livello Modello dal livello Vista in modo da poterlo riutilizzare e testare il livello Modello in modo isolato.

ViewController contiene la vista e possiede il modello. Il problema è che abbiamo usato per scrivere il codice del controller e il codice di visualizzazione nel ViewController.

MVC crea spesso il problema chiamato Massive View Controller, ma ciò accade solo e diventa una cosa seria nelle app con una complessità sufficiente.

Esistono alcuni metodi che lo sviluppatore può utilizzare per rendere più gestibile il View Controller. Qualche esempio:

  • Estrazione della logica VC per altre classi come l'origine dati dei metodi di visualizzazione tabella e delegare per altri file utilizzando il modello di progettazione delegato.
  • Creare una separazione più distinta delle responsabilità con la composizione (ad es. Dividere il VC in controller di visualizzazione figlio).
  • Utilizzare il modello di progettazione del coordinatore per rimuovere la responsabilità di implementare la logica di navigazione nel VC
  • Utilizzare una classe wrapper DataPresenter che incapsula la logica e trasforma il modello di dati in un output di dati che rappresenta i dati presentati all'utente finale.

MVC vs MVP

Come puoi vedere il diagramma di MVP è molto simile a MVC

L'MVC è stato un passo avanti, ma era ancora caratterizzato da assenza o silenzio su alcune cose.

Nel frattempo, il World Wide Web è cresciuto e molte cose nella comunità degli sviluppatori si sono evolute. Ad esempio, i programmatori hanno iniziato a utilizzare Ajax e caricano solo parti di pagine anziché l'intera pagina HTML contemporaneamente.

In MVC penso che non vi sia nulla che indichi che il Controller non dovrebbe conoscere l'implementazione specifica di View (assenza).

L'HTML faceva parte del livello View e molti casi erano stupidi. In alcuni casi, riceve solo eventi dall'utente e visualizza il contenuto visivo della GUI.

Quando parti delle pagine Web hanno iniziato a essere caricate in parti, questa segmentazione ha portato nella direzione di mantenere lo stato di visualizzazione e una maggiore necessità di una separazione della responsabilità della logica di presentazione.

La logica di presentazione è la logica che controlla il modo in cui l'interfaccia utente deve essere visualizzata e il modo in cui gli elementi dell'interfaccia utente interagiscono insieme. Un esempio è la logica di controllo di quando un Indicatore di caricamento dovrebbe iniziare a mostrare / animare e quando dovrebbe smettere di mostrare / animare.

In MVP e MVVM il View Layer dovrebbe essere stupido senza alcuna logica o intelligenza, e in iOS il View Controller dovrebbe far parte del View Layer. Il fatto che View sia stupido significa che anche la logica di presentazione rimane fuori dal layer View.

Uno dei problemi di MVC è che non è chiaro dove dovrebbe rimanere la logica di presentazione. È semplicemente silenzioso al riguardo. La logica di presentazione deve essere nel livello Visualizza o nel livello Modello?

Se il ruolo del Modello è solo quello di fornire i dati "grezzi", significa che il codice nella Vista sarebbe:

Considera il seguente esempio: abbiamo un utente, con nome e cognome. Nella vista, dobbiamo visualizzare il nome utente come "Cognome, Nome" (ad es. "Flores, Tiago").

Se il ruolo del Modello è quello di fornire i dati "grezzi", significa che il codice nella Vista sarebbe:

let firstName = userModel.getFirstName ()
let lastName = userModel.getLastName ()
nameLabel.text = lastName + “,“ + firstName

Ciò significa che sarebbe responsabilità di View gestire la logica dell'interfaccia utente. Ma ciò rende impossibile la logica dell'interfaccia utente per test unitari.

L'altro approccio è che il Modello esponga solo i dati che devono essere visualizzati, nascondendo qualsiasi logica aziendale dalla Vista. Ma poi, finiamo con i modelli che gestiscono sia la logica aziendale che quella dell'interfaccia utente. Sarebbe unitamente testabile, ma poi il Modello finirà, essendo implicitamente dipendente dalla Vista.

let name = userModel.getDisplayName ()
nameLabel.text = nome

L'MVP ne è chiaro e la logica di presentazione rimane nel livello Presenter. Ciò aumenta la testabilità del livello Presenter. Ora il Model e Presenter Layer sono facilmente testabili.

Normalmente nelle implementazioni MVP, la vista è nascosta dietro un'interfaccia / protocollo e non dovrebbero esserci riferimenti a UIKit nel Presenter.

Un'altra cosa da tenere a mente sono le dipendenze transitive.

Se il controller ha un livello aziendale come dipendenza e il livello aziendale ha un livello di accesso ai dati come dipendenza, allora il controller ha una dipendenza transitiva per il livello di accesso ai dati. Poiché le implementazioni MVP normalmente utilizzano un contratto (protocollo) tra tutti i livelli, non ha dipendenze transitive.

I diversi livelli cambiano anche per ragioni diverse e a velocità diverse. Quindi quando cambi un livello non vuoi che ciò causi effetti / problemi secondari negli altri livelli.

I protocolli sono più stabili delle classi. I protocolli non hanno dettagli di implementazione e con i contratti, quindi è possibile modificare i dettagli di implementazione di un layer senza influire sugli altri layer.

Quindi i contratti (protocolli) creano un disaccoppiamento tra i livelli.

MVP vs MVVM

Diagramma MVVM

Una delle principali differenze tra MVP e MVVM è che in MVP il Presenter comunica con la vista attraverso le interfacce e in MVVM la vista è orientata ai cambiamenti di dati ed eventi.

In The MVP eseguiamo l'associazione manuale tra Presenter e View utilizzando interfacce / protocolli.
In The MVVM realizziamo l'associazione automatica dei dati usando qualcosa come RxSwift, KVO o utilizziamo un meccanismo con generici e chiusure.

In MVVM non abbiamo nemmeno bisogno di un contratto (ad esempio: interfaccia java / protocollo iOS) tra ViewModel e View perché di solito comunichiamo attraverso il modello di progettazione Observer.

L'MVP utilizza il modello delegato perché il livello presentatore delega gli ordini al livello vista, quindi deve conoscere qualcosa sulla vista anche se si tratta solo della firma dell'interfaccia / protocollo. Pensa alla differenza tra i centri di notifica e i delegati di TableView. Il Centro notifiche non ha bisogno di interfacce per creare un canale di comunicazione, ma i delegati di TableView utilizzano un protocollo che le classi dovrebbero implementare.

Pensa alla logica di presentazione di un indicatore di caricamento. In MVP il presentatore esegue ViewProtocol.showLoadingIndicator. In MVVM potrebbe esserci una proprietà isLoading in ViewModel. Il livello Visualizza tramite un'associazione dati automatica rileva quando questa proprietà cambia e si aggiorna da sola. MVP è più indispensabile di MVVM perché il Presenter impartisce ordini.

MVVM riguarda più le modifiche ai dati che gli ordini diretti e facciamo associazioni tra le modifiche ai dati e visualizziamo gli aggiornamenti. Se utilizziamo RxSwift e il paradigma di programmazione reattiva funzionale insieme a MVVM, abbiamo reso il codice ancora meno imperativo e più dichiarativo.

MVVM è più facile da testare rispetto a MVP perché MVVM utilizza il modello di progettazione Observer che trasferisce i dati tra i componenti in modo disaccoppiato.
Quindi possiamo testare semplicemente osservando le modifiche nei dati semplicemente confrontando i due oggetti piuttosto che creare simulazioni dei metodi chiamati per testare la comunicazione tra View e Presenter.

PS: ho fatto alcuni aggiornamenti all'articolo che lo ha fatto crescere molto, quindi è stato necessario dividerlo in tre parti. Puoi leggere la terza parte qui.

La seconda parte finisce qui. Tutti i feedback sono benvenuti. La terza parte parlerà di VIPER, VIP, programmazione reattiva, compromessi, vincoli e senso contestuale.

Grazie per aver letto! Se ti è piaciuto questo articolo, per favore applaudi
così anche altre persone possono leggerlo :)