Articles

Android Architecture Patterns Part 2:Model-View-Presenter

Florina Muntenescu
Florina Muntenescu

Follow

Nov 2, 2016 · 6 min read

It’s about time we developers start thinking about how we can apply good architecture patterns in our Android apps. Abychom s tím pomohli, Google nabízí plány architektury Android, kde jsme s Erikem Hellmanem spolupracovali na vzorku MVP & RxJava. Podívejme se na to, jak jsme to aplikovali, a na výhody a nevýhody tohoto přístupu.

zde jsou role každé komponenty:

  • Model — datová vrstva. Zodpovědný za zpracování obchodní logiky a komunikace s sítě a databázové vrstvy.
  • zobrazit-vrstva uživatelského rozhraní. Zobrazí data a upozorní moderátora na akce uživatele.
  • Presenter-načte data z modelu, použije logiku uživatelského rozhraní a řídí stav zobrazení, rozhodne, co se má zobrazit a reaguje na oznámení vstupu uživatele z pohledu.

vzhledem k tomu, že View a Presenter úzce spolupracují, musí mít vzájemný odkaz. Aby Presenter unit testovatelné s JUnit, Pohled je získávána a rozhraní pro to používá. Vztah mezi moderátorem a jeho odpovídající Zobrazení je definována v Contract rozhraní třídy, dělá kód čitelnější a spojení mezi oběma srozumitelnější.

Model-View-Presenter třídní struktury

Model-View-Presenter Vzor & RxJava v Android Architektury Plány

plán vzorku je „to Do“ aplikace. To umožňuje uživateli vytvářet, číst, aktualizovat a mazat“ dělat “ úkoly, stejně jako použít filtry do zobrazeného seznamu úkolů. RxJava se používá k přesunu z hlavního vlákna a ke zvládnutí asynchronních operací.

Model

Model pracuje se vzdálenými a lokálními zdroji dat, aby získal a uložil data. Zde se řeší obchodní logika. Například při požadavku na seznam TaskS se Model pokusí načíst je z lokálního zdroje dat. Pokud je prázdný, dotazuje se na síť, uloží odpověď do místního zdroje dat a poté seznam vrátí.

načítání úkolů se provádí pomocí RxJava:

public Observable<List<Task>> getTasks(){ 
...
}

Model přijímá jako parametry v konstruktoru rozhraní lokální a vzdálené zdroje dat, což je Model zcela nezávislé na jakémkoli Android třídy a tak snadno se jednotka testování s JUnit. Například, test, který getTasks požaduje data z místního zdroje, provedli jsme následující test:

@Mock 
private TasksDataSource mTasksRemoteDataSource;
@Mock
private TasksDataSource mTasksLocalDataSource;
...
@Test
public void getTasks_requestsAllTasksFromLocalDataSource() {
// Given that the local data source has data available
setTasksAvailable(mTasksLocalDataSource, TASKS);
// And the remote data source does not have any data available
setTasksNotAvailable(mTasksRemoteDataSource);
// When tasks are requested from the tasks repository
TestSubscriber<List<Task>> testSubscriber =
new TestSubscriber<>();
mTasksRepository.getTasks().subscribe(testSubscriber); // Then tasks are loaded from the local data source
verify(mTasksLocalDataSource).getTasks();
testSubscriber.assertValue(TASKS);
}

Zobrazit

Zobrazit pracuje s Presenter pro zobrazení dat a upozorní Moderátor o akce uživatele. V aktivitách MVP mohou být fragmenty a vlastní zobrazení Androidu pohledy. Naše volba byla použít fragmenty.

všechny pohledy implementují stejné rozhraní BaseView, které umožňuje nastavení presenteru.

public interface BaseView<T> {
void setPresenter(T presenter);
}

Zobrazit upozorní Moderátor, že je připraven být aktualizován voláním subscribemetoda Presenter v onResume. Zobrazení volá presenter.unsubscribe() v onPause, aby oznámilo presenteru, že již nemá zájem o aktualizaci. Pokud provádění Pohledu je Android custom view, pak subscribeunsubscribe metody musí být volán na onAttachedToWindowonDetachedFromWindow. Akce uživatele, jako kliknutí na tlačítko, spustí odpovídající metody v presenteru, což je ten, který rozhodne, co se má stát dál.

pohledy se testují pomocí espressa. Na obrazovce statistik je například třeba zobrazit počet aktivních a dokončených úkolů. Test, který zkontroluje, že je to provedeno správně první staví některé úkoly v TaskRepository; pak spustí StatisticsActivity a kontroly obsahu zobrazení:

@Before 
public void setup() {
// Given some tasks
TasksRepository.destroyInstance();
TasksRepository repository = Injection.provideTasksRepository( InstrumentationRegistry.getContext()); repository.saveTask(new Task("Title1", "", false));
repository.saveTask(new Task("Title2", "", true)); // Lazily start the Activity from the ActivityTestRule
Intent startIntent = new Intent();
mStatisticsActivityTestRule.launchActivity(startIntent);
}@Test
public void Tasks_ShowsNonEmptyMessage() throws Exception {
// Check that the active and completed tasks text is displayed
Context context = InstrumentationRegistry.getTargetContext();
String expectedActiveTaskText = context
.getString(R.string.statistics_active_tasks);
String expectedCompletedTaskText = context
.getString(R.string.statistics_completed_tasks); onView(withText(containsString(expectedActiveTaskText)))
.check(matches(isDisplayed()));
onView(withText(containsString(expectedCompletedTaskText)))
.check(matches(isDisplayed()));
}

Moderátor

Presenter a jeho odpovídající Zobrazení jsou vytvářeny Činnost. Odkazy na View a na TaskRepository – Model – jsou dány konstruktoru presenteru. Při implementaci konstruktoru Presenter zavolá metodu zobrazení setPresenter. To lze zjednodušit při použití rámce pro vstřikování závislostí, který umožňuje vstřikování Presenterů do odpovídajících pohledů, což snižuje spojení tříd. Implementace ToDo-MVP s dýkou je pokryta jiným vzorkem.

všechny Presentery implementují stejné rozhraní BasePresenter.

public interface BasePresenter {
void subscribe();
void unsubscribe();
}

Když subscribe metoda je volána, Moderátor začne požadovat data z Modelu, pak platí UI logika přijatých dat a nastaví ji na Pohled. Například v StatisticsPresenter, jsou všechny úkoly požadované od TaskRepository – pak obnovené úkoly jsou použita pro výpočet počtu aktivních a dokončených úkolů. Tato čísla budou použita jako parametry pro metodu zobrazení showStatistics(int numberOfActiveTasks, int numberOfCompletedTasks).

test jednotky, který zkontroluje, zda je metoda showStatistics volána se správnými hodnotami, je snadno implementovatelná. Jsme zesměšňovat TaskRepositoryStatisticsContract.View a dát posmívali objekty jako parametry konstruktoru StatisticsPresenter objekt. Implementace testu je:

@Test 
public void loadNonEmptyTasksFromRepository_CallViewToDisplay() {
// Given an initialized StatisticsPresenter with
// 1 active and 2 completed tasks
setTasksAvailable(TASKS); // When loading of Tasks is requested
mStatisticsPresenter.subscribe(); // Then the correct data is passed on to the view
verify(mStatisticsView).showStatistics(1, 2);
}

úlohou metody unsubscribe je vymazat všechny odběry presenteru, čímž se zabrání úniku paměti.

kromě subscribe a unsubscribe každý Presenter vystavuje jiné metody, které odpovídají akcím uživatele v zobrazení. Například AddEditTaskPresenter, přidá metody, jako je createTask, to by být volána, když uživatel stiskne tlačítko, které vytvoří nový úkol. Tím je zajištěno, že všechny akce uživatele – a následně veškerá logika uživatelského rozhraní-procházejí Presenterem a mohou být testovány jednotkou.

nevýhody vzoru Model-View-Presenter

vzor Model-View-Presenter s sebou přináší velmi dobré oddělení obav. I když je to určitě profesionál, při vývoji malé aplikace nebo prototypu se to může zdát jako režie. Chcete-li snížit počet použitých rozhraní, někteří vývojáři odstraní třídu rozhraní Contract a rozhraní pro Presenter.

jedna z úskalí MVP se objeví při přesunu logiky UI do presenteru: toto se nyní stává vševědoucí třídou s tisíci řádků kódu. Chcete-li to vyřešit, rozdělte kód ještě více a nezapomeňte vytvořit třídy, které mají pouze jednu odpovědnost a jsou testovatelné jednotkami.

závěr

vzor Model-View-Controller má dvě hlavní nevýhody: za prvé, pohled má odkaz na řadič i Model; a za druhé, neomezuje manipulaci s logikou UI na jednu třídu, přičemž tato odpovědnost je sdílena mezi řadičem a pohledem nebo modelem. Model-View-Presenter pattern řeší oba tyto problémy tím, že rozbije připojení, které Zobrazení má s Modelem a vytváří pouze jednu třídu, která zpracovává vše, co souvisí s předložením View — Presenter: jediné třídy, která je snadné, aby unit test.

Co když chceme architekturu založenou na událostech, kde pohled reaguje na změny? Zůstaňte naladěni na další vzory vzorkované v plánech architektury Android, abyste zjistili, jak to lze implementovat. Do té doby si přečtěte o naší implementaci modelu-View-ViewModel pattern v aplikaci upday.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *