Android Architecture Patterns Partea 1: Model-View-Controller

Florina Muntenescu
Florina Muntenescu

Follow

1 noiembrie, 2016 – 6 min citește

În urmă cu un an, când majoritatea membrilor actualei echipe Android a început să lucreze la upday, aplicația era departe de a fi aplicația robustă și stabilă pe care ne-o doream. Am încercat să înțelegem de ce codul nostru era într-o stare atât de proastă și am găsit doi vinovați principali: schimbarea continuă a interfeței de utilizare și lipsa unei arhitecturi care să susțină flexibilitatea de care aveam nevoie. Aplicația se afla deja la a patra reproiectare în șase luni. Modelul de proiectare ales părea să fie Model-View-Controller, dar atunci era deja un „mutant”, departe de cum ar trebui să fie.

Să descoperim împreună ce este modelul Model-View-Controller; cum a fost aplicat în Android de-a lungul anilor; cum ar trebui aplicat astfel încât să poată maximiza testabilitatea; și câteva dintre avantajele și dezavantajele sale.

Într-o lume în care logica interfeței cu utilizatorul tinde să se schimbe mai des decât logica de business, dezvoltatorii desktop și web aveau nevoie de o modalitate de a separa funcționalitatea interfeței cu utilizatorul. Modelul MVC a fost soluția lor.

  • Model – stratul de date, responsabil pentru gestionarea logicii de afaceri și manipularea API de rețea sau de bază de date.
  • View – stratul de interfață utilizator – o vizualizare a datelor din Model.
  • Controller – stratul logic, primește notificări privind comportamentul utilizatorului și actualizează Modelul după cum este necesar.

Structura clasei Model-View-Controller

Atât Controlerul cât și View depind de Model: Controlerul pentru a actualiza datele, View pentru a obține datele. Dar, cel mai important pentru dezvoltatorii desktop și Web de la acea vreme: Modelul era separat și putea fi testat independent de UI. Au apărut mai multe variante de MVC. Cele mai cunoscute sunt legate de faptul că Modelul este pasiv sau notifică în mod activ faptul că a fost modificat. Iată mai multe detalii:

Model pasiv

În varianta Model pasiv, Controller este singura clasă care manipulează Modelul. În funcție de acțiunile utilizatorului, Controller trebuie să modifice Modelul. După ce Modelul a fost actualizat, Controller va notifica View că și acesta trebuie să se actualizeze. În acel moment, View va solicita datele din Model.

Model-View-Controller – Model pasiv – comportament

Model activ

Pentru cazurile în care Controlerul nu este singura clasă care modifică Modelul, Modelul are nevoie de o modalitate de a notifica Vizualizarea, și alte clase, despre actualizări. Acest lucru se realizează cu ajutorul modelului Observer. Modelul conține o colecție de observatori care sunt interesați de actualizări. View implementează interfața observatorului și se înregistrează ca observator al Modelului.

Model-View-Controller – Model activ – structura clasei

De fiecare dată când Modelul se actualizează, acesta va parcurge, de asemenea, colecția de observatori și va apela metoda update. Implementarea acestei metode în View va declanșa apoi solicitarea celor mai recente date de la Model.

Model-View-Controller – Model activ – comportament

Model-View-Controller în Android

În jurul anului 2011, când Android a început să devină din ce în ce mai popular, au apărut în mod natural întrebări legate de arhitectură. Deoarece MVC era unul dintre cele mai populare modele de interfață utilizator la acea vreme, dezvoltatorii au încercat să îl aplice și în Android.

Dacă căutați pe StackOverflow întrebări de genul „Cum se aplică MVC în Android”, unul dintre cele mai populare răspunsuri a afirmat că în Android, o activitate este atât vizualizatorul, cât și controlorul. Privind înapoi, acest lucru sună aproape nebunesc! Dar, la acel moment, accentul principal era pus pe a face modelul testabil și, de obicei, alegerea implementării pentru View și Controller depindea de platformă.

Cum ar trebui aplicat MVC în Android

În zilele noastre, întrebarea despre cum se aplică modelele MVC are un răspuns mai ușor de găsit. Activitățile, fragmentele și punctele de vedere ar trebui să fie punctele de vedere în lumea MVC. Controlorii ar trebui să fie clase separate care să nu extindă sau să folosească nicio clasă Android, la fel și pentru Modele.

O problemă apare atunci când se conectează Controlorul la Vizualizare, deoarece Controlorul trebuie să îi spună Vizualizării să se actualizeze. În arhitectura pasivă Model MVC, Controller-ul trebuie să dețină o referință la View. Cel mai simplu mod de a face acest lucru, concentrându-se în același timp asupra testării, este de a avea o interfață BaseView, pe care Activity/Fragment/View să o extindă. Astfel, Controlerul ar avea o referință la BaseView.

Avantaje

Patronul Model-View-Controller sprijină foarte mult separarea preocupărilor. Acest avantaj nu numai că mărește testabilitatea codului, dar îl face și mai ușor de extins, permițând o implementare destul de ușoară a unor caracteristici noi.

Classele Model nu au nicio referință la clasele Android și, prin urmare, sunt ușor de testat unitar. Controlerul nu extinde și nu implementează nicio clasă Android și ar trebui să aibă o referință la o clasă de interfață a vizualizării. În acest fel, testarea unitară a Controlerului este, de asemenea, posibilă.

Dacă Vizualizările respectă principiul responsabilității unice, atunci rolul lor este doar de a actualiza Controlerul pentru fiecare eveniment al utilizatorului și doar de a afișa datele din Model, fără a implementa nicio logică de afaceri. În acest caz, testele UI ar trebui să fie suficiente pentru a acoperi funcționalitățile View.

The View Depends On The Controller And On The Model

Dependența View-ului de Model începe să fie un dezavantaj în cazul View-urilor complexe. Pentru a minimiza logica din View, Modelul ar trebui să fie capabil să furnizeze metode testabile pentru fiecare element care ajunge să fie afișat. Într-o implementare activă a Modelului, acest lucru crește exponențial numărul de clase și metode, având în vedere că ar fi necesari observatori pentru fiecare tip de date.

Datorită faptului că View depinde atât de Controller cât și de Model, schimbările în logica UI ar putea necesita actualizări în mai multe clase, scăzând flexibilitatea modelului.

Cine se ocupă de logica UI?

Conform modelului MVC, Controlerul actualizează Modelul, iar Vizualizarea obține datele care urmează să fie afișate din Model. Dar cine decide asupra modului de afișare a datelor? Este modelul sau vizualizarea? Luați în considerare următorul exemplu: avem un User, cu nume și prenume. În View trebuie să afișăm numele utilizatorului ca „Lastname, Firstname” (de exemplu, „Doe, John”).

Dacă rolul Modelului este doar de a furniza datele „brute”, înseamnă că codul din View ar fi:

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

Așa că acest lucru înseamnă că ar fi responsabilitatea View de a gestiona logica UI. Dar acest lucru face ca logica UI să fie imposibil de testat unitar.

Altă abordare este ca Modelul să expună doar datele care trebuie afișate, ascunzând orice logică de afaceri de la View. Dar atunci ajungem să avem modele care se ocupă atât de logica de afaceri, cât și de logica UI. Ar fi testabil la nivel de unitate, dar atunci Modelul sfârșește prin a fi, implicit, dependent de View.

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

Concluzie

În primele zile ale Android, tiparul Model-View-Controller pare să fi derutat o mulțime de dezvoltatori și a dus la un cod care era dificil, dacă nu imposibil de testat la nivel de unitate.

Dependența View-ului de Model și faptul de a avea logică în View a condus baza noastră de cod către o stare din care era imposibil de recuperat fără a refactoriza complet aplicația. Care a fost noua abordare în arhitectură și de ce? Aflați în citind această postare pe blog.

.