Modelli di architettura Android parte 1: Model-View-Controller

Florina Muntenescu
Florina Muntenescu

Follow

Nov 1, 2016 – 6 min read

Un anno fa, quando la maggior parte dell’attuale team Android ha iniziato a lavorare a upday, l’applicazione era lontana dall’essere l’app robusta e stabile che volevamo che fosse. Abbiamo cercato di capire perché il nostro codice era in così cattiva forma e abbiamo trovato due colpevoli principali: il continuo cambiamento della UI e la mancanza di un’architettura che supportasse la flessibilità di cui avevamo bisogno. L’app era già alla quarta riprogettazione in sei mesi. Il design pattern scelto sembrava essere Model-View-Controller ma allora era già un “mutante”, lontano da come dovrebbe essere.

Scopriamo insieme cos’è il pattern Model-View-Controller; come è stato applicato negli anni in Android; come dovrebbe essere applicato per massimizzare la testabilità; e alcuni dei suoi vantaggi e svantaggi.

In un mondo in cui la logica dell’interfaccia utente tende a cambiare più spesso della logica di business, gli sviluppatori desktop e web avevano bisogno di un modo per separare le funzionalità dell’interfaccia utente. Il pattern MVC era la loro soluzione.

  • Model – il livello dei dati, responsabile della gestione della logica di business e della gestione della rete o del database API.
  • View – il livello UI – una visualizzazione dei dati dal Model.
  • Controller – il livello della logica, viene informato del comportamento dell’utente e aggiorna il Model quando necessario.

Struttura della classe Modello-Vista-Controller

Quindi, questo significa che sia il Controllore che la Vista dipendono dal Modello: il Controllore per aggiornare i dati, la Vista per ottenere i dati. Ma, la cosa più importante per gli sviluppatori desktop e Web di quel tempo: il Modello era separato e poteva essere testato indipendentemente dall’UI. Sono apparse diverse varianti di MVC. Le più conosciute sono legate al fatto che il modello sia passivo o notifichi attivamente che è cambiato. Ecco ulteriori dettagli:

Modello passivo

Nella versione del modello passivo, il controllore è l’unica classe che manipola il modello. In base alle azioni dell’utente, il Controllore deve modificare il Modello. Dopo che il Modello è stato aggiornato, il Controllore notificherà alla Vista che anch’essa deve aggiornarsi. A quel punto, la View richiederà i dati dal Model.

Modello-Vista-Controllore – Modello passivo – comportamento

Modello attivo

Per i casi in cui il Controllore non è l’unica classe che modifica il Modello, il Modello ha bisogno di un modo per notificare alla Vista, e ad altre classi, gli aggiornamenti. Questo si ottiene con l’aiuto del pattern Observer. Il modello contiene una collezione di osservatori che sono interessati agli aggiornamenti. La View implementa l’interfaccia dell’osservatore e si registra come osservatore del Model.

Model-View-Controller – active Model – class structure

Ogni volta che il Model si aggiorna, itera anche attraverso la collezione di osservatori e chiama il metodo update. L’implementazione di questo metodo nella View attiverà quindi la richiesta degli ultimi dati dal Model.

Model-View-Controller – modello attivo – comportamento

Model-View-Controller in Android

Nel 2011 circa, quando Android iniziò a diventare sempre più popolare, apparvero naturalmente delle domande sull’architettura. Dato che MVC era uno dei pattern UI più popolari a quel tempo, gli sviluppatori hanno cercato di applicarlo anche ad Android.

Se si cerca su StackOverflow per domande come “Come applicare MVC in Android”, una delle risposte più popolari afferma che in Android, un’attività è sia la vista che il controllore. Guardando indietro, questo suona quasi folle! Ma, a quel punto, l’enfasi principale era sul rendere il modello testabile e di solito la scelta dell’implementazione per la Vista e il Controllore dipendeva dalla piattaforma.

Come dovrebbe essere applicato MVC in Android

Oggi, la domanda su come applicare i modelli MVC ha una risposta che è più facile da trovare. Le Attività, i Frammenti e le Viste dovrebbero essere le Viste nel mondo MVC. I Controllori dovrebbero essere classi separate che non estendono o usano nessuna classe Android, e lo stesso vale per i Modelli.

Un problema sorge quando si collega il Controllore alla Vista, poiché il Controllore deve dire alla Vista di aggiornare. Nell’architettura passiva Model MVC, il Controller ha bisogno di tenere un riferimento alla View. Il modo più semplice per farlo, mentre ci si concentra sui test, è quello di avere un’interfaccia BaseView, che l’Activity/Fragment/View estenderebbe. Così, il controller avrebbe un riferimento alla BaseView.

Avantaggi

Il pattern Model-View-Controller supporta altamente la separazione delle preoccupazioni. Questo vantaggio non solo aumenta la testabilità del codice, ma lo rende anche più facile da estendere, permettendo un’implementazione abbastanza facile di nuove funzionalità.

Le classi Model non hanno alcun riferimento alle classi Android e sono quindi semplici da testare. Il Controller non estende o implementa alcuna classe Android e dovrebbe avere un riferimento a una classe di interfaccia della View. In questo modo, il test unitario del Controller è anche possibile.

Se le View rispettano il principio di responsabilità singola, allora il loro ruolo è solo quello di aggiornare il Controller per ogni evento dell’utente e solo visualizzare i dati dal Modello, senza implementare alcuna logica di business. In questo caso, i test UI dovrebbero essere sufficienti a coprire le funzionalità della vista.

La vista dipende dal controllore e dal modello

La dipendenza della vista dal modello comincia ad essere un lato negativo nelle viste complesse. Per minimizzare la logica nella vista, il modello dovrebbe essere in grado di fornire metodi testabili per ogni elemento che deve essere visualizzato. In un’implementazione attiva del Model, questo aumenta esponenzialmente il numero di classi e metodi, dato che sarebbero necessari degli osservatori per ogni tipo di dati.

Dato che la View dipende sia dal Controller che dal Model, i cambiamenti nella logica UI potrebbero richiedere aggiornamenti in diverse classi, diminuendo la flessibilità del pattern.

Chi gestisce la logica UI?

Secondo il pattern MVC, il Controller aggiorna il Model e la View riceve i dati da visualizzare dal Model. Ma chi decide come visualizzare i dati? È il Modello o la Vista? Consideriamo il seguente esempio: abbiamo un User, con nome e cognome. Nella View abbiamo bisogno di visualizzare il nome dell’utente come “Lastname, Firstname” (ad esempio “Doe, John”).

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

String firstName = userModel.getFirstName(); 
String lastName = userModel.getLastName(); nameTextView.setText(lastName + ", " + firstName)

Quindi questo significa che sarebbe responsabilità della View gestire la logica UI. Ma questo rende la logica UI impossibile da testare unitariamente.

L’altro approccio è quello di avere il modello che espone solo i dati che devono essere visualizzati, nascondendo qualsiasi logica di business dalla vista. Ma poi, ci ritroviamo con modelli che gestiscono sia il business che la logica UI. Sarebbe testabile a livello di unità, ma poi il modello finisce per essere implicitamente dipendente dalla vista.

String name = userModel.getDisplayName(); nameTextView.setText(name);

Conclusione

Nei primi giorni di Android il pattern Model-View-Controller sembrava aver confuso un sacco di sviluppatori e portato a codice difficile, se non impossibile da testare a livello di unità.

La dipendenza della View dal Model e l’avere la logica nella View ha portato il nostro codice base ad uno stato dal quale era impossibile recuperare senza rifattorizzare completamente l’app. Qual è stato il nuovo approccio nell’architettura e perché? Scopritelo leggendo questo post sul blog.