Reddit Mobile Architecture Evolution



Dies ist der erste Artikel, in dem wir über die Architektur der Reddit-Anwendung für iOS sprechen. Hier geht es um Funktionen, die näher an der Benutzeroberfläche arbeiten. Insbesondere der Übergang zur Model-View-Presenter (MVP) -Architektur. Die Vorteile eines solchen Refactorings:

  • Verbesserung der Codeflexibilität, -klarheit und -wartbarkeit, um zukünftiges Wachstum zu unterstützen und Iterationen zu beschleunigen.
  • Die Scroll-Leistung wurde um das 1,58-fache erhöht.
  • Stimulierende Unit-Tests. Die Anzahl der Tests stieg von wenigen auf über 200.

Unten sehen Sie ein visuelles Diagramm unserer Schichtarchitektur. Der erste Artikel konzentriert sich auf die Ebenen "Ansicht" und "Präsentator".


Der ultimative Blick auf unsere geschichtete Architektur

Voraussetzungen für Veränderung


Vor mehr als einem Jahr haben wir den Artikel „Erstellen eines Menübands in einer Reddit iOS-App“ veröffentlicht . Es wurde erläutert, wie ein produktiver, erweiterbarer Feed mit einer bemerkenswerten Sitzungsrate von 99,95% ohne Absturz generiert werden kann. Wir haben erklärt, wie Sie die MVC-Architektur (Model-View-Controller) verwenden und Abstraktionen für Paging-Daten erstellen.

Jetzt ist Reddit gewachsen und wächst als Organisation und als Dienstleistung weiter. Infolgedessen sind die Anforderungen an die Reddit iOS-Anwendung gestiegen. Es sollte mehr Funktionsanforderungen, schnellere iterative Schleifen und höhere Qualitätsstandards unterstützen. Das Entwicklungsteam ist von drei auf mehr als zwanzig Personen angewachsen. Die ursprüngliche MVC-Architektur erfüllt diese Anforderungen kaum, sodass architektonische Änderungen vorgenommen werden mussten.

Das Wesentliche des Problems


Im Laufe der Zeit hat der Code an Flexibilität und Klarheit verloren. In der iOS-Entwicklergemeinde wird die Abkürzung MVC häufig als Massive View Controller entschlüsselt, da View Controller häufig zu göttlichen Objekten mit mehr als tausend Zeilen anschwellen. Trotz all unserer Bemühungen trat das Problem wirklich auf: Die Vererbungshierarchie wurde unangenehm tief, und die Kontrolleure begannen, sich in obskure göttliche Objekte zu verwandeln, die Veränderungen widerstehen.

Wir haben den letzten Nagel in den MVC-Sarg getrieben, als wir beschlossen, die Präsentationsstufe für das Band zu ändern. Das Publikum der Reddit-Anwendung wächst, sodass die Bildlaufleistung zu oft von 60 FPS auf 45 bis 55 FPS herabgesetzt wurde. Dies bedeutet, dass Sie die Bandpräsentationsebene neu schreiben müssen, während Sie die ursprüngliche Implementierung beibehalten. In der vorhandenen MVC-Architektur konnten wir die Bandpräsentationsschicht jedoch nicht neu schreiben, ohne Tausende von Codezeilen zu duplizieren.

Darüber hinaus sind viele Teile der Codebasis schwer zu testen. Der Code befindet sich in der hart getesteten Klasse der Präsentationsschicht, und die Abhängigkeiten sind häufig allein (Singleton) oder in der Klasse selbst fest codiert. Wir wollten die Möglichkeit normaler Tests realisieren.

Weitere Aufgaben für das Refactoring: Die Anzahl der Fehler sollte gering bleiben, die neue Architektur sollte die Grundlage für zukünftiges Wachstum bilden und Funktionen, die von der vorhandenen Infrastruktur abhängen, nicht beeinträchtigen. Das heißt, evolutionäre, nicht revolutionäre Änderungen sind erforderlich.

Übergang zu MVP


Wir haben beschlossen, dass zur Lösung der oben genannten Probleme eine neue Version der Anwendung benötigt wird. Nachdem wir verschiedene Optionen in Betracht gezogen hatten, entschieden wir uns für die Model-View-Presenter (MVP) -Architektur. MVP erfüllt alle diese Kriterien und ist eine bekannte und dokumentierte Architektur, sodass es einfacher ist, Ingenieure auszubilden. Das Konzept der „Ansichtsmodelle“ wird ebenfalls beibehalten. Bei Bedarf können Sie in Presenter Ansichtsmodellobjekte nach dem Prinzip der alleinigen Verantwortung erstellen und diese zum Erweitern unserer Ansichten verwenden.


Model-View-Presenter-Diagramm

Massive View Controller loswerden


Für iOS-Anwendungen wird angenommen, dass Ansichtsobjekte Unterklassen von UIView sind, Controller-Objekte Unterklassen von UIViewController sind und Modellobjekte einfache alte Objekte sind. Wie der Name UIViewController andeutet, werden hier die Ansicht und der Controller in einem Objekt zusammengefasst. Das heißt, das MVC-Modell unter iOS verliert häufig seine Vorteile aufgrund der engen Verbindung zwischen der Präsentationsebene und dem Controller. Interessanterweise erkennt Apple selbst diese Verbindung .


Oft wird die Model-View-Controller-Architektur für iOS zu

In der MVP-Architektur berücksichtigen wir dieses Konzept und formalisieren es, indem wir den UIViewController wirklich als explizites Objekt der Präsentationsschicht betrachten. Das Konzept, einen UIViewController als Präsentationsobjekt mit einem schlechten Ruf zu behandeln, ist in den letzten Jahren populär geworden .

Daher entfernen wir alle überflüssigen Logik in unseren UIViewControllern. Anschließend weisen wir dem Vermittler zwischen der Ansicht und dem Modell Presenter zu. In dieser neuen Rolle sind ihm Ansichtsobjekte wie der UIViewController nicht bekannt. Beachten Sie, dass Presenter über eine Schnittstelle mit der Ansicht interagiert. Theoretisch können Sie die Implementierung einer Ansicht in NSViewController (für MacOS) usw. ändern.


Wir vereinfachen ViewController, indem wir Presenter einführen und Verantwortlichkeiten teilen

Über MVP-Optionen nachdenken


Wie Sie im MVP-Diagramm sehen können, war die Architektur MVC sehr ähnlich. In der Tat gibt es mehr Ähnlichkeiten als Unterschiede. Eine solche Architektur hilft einfach dabei, die korrekte Trennung von Präsentationscode und Geschäftslogik herzustellen, die MVC sucht. Tatsächlich sind alle Ableitungen der Architektur von MV (x) wie MVP, MVVM, MVAdapter und andere einfach unterschiedliche Versionen desselben Konzepts.

Man könnte sich fragen, warum wir MVC komplett aufgegeben haben. Tatsächlich beschreibt Apple verschiedene Arten von Controllern : für Modelle, Vermittler und Koordination. Ehrlich gesagt, könnten wir vielleicht unseren Presenter durch einen anderen Controller ersetzen. Sie entschieden sich jedoch dagegen, da die meisten iOS-Entwickler aus irgendeinem Grund der Ansicht waren, dass der UIViewController ein Synonym für den Controller ist. Mit dem Wort Presenter geben wir ein Signal, dass sich dieses Objekt erheblich von einem herkömmlichen Controller mit bestimmten Funktionen und Eigenschaften unterscheidet.

Verbesserung von Flexibilität, Nachhaltigkeit und Klarheit


"Komposition lieber als Vererbung" ist ein bekanntes Mantra in der Objektprogrammierung. Mit der Vererbung müssen Sie die Zukunft vorhersagen und eine riesige Taxonomie von Objekten erstellen. Wenn jedoch Ihre „ideal“ erstellte Vererbungshierarchie aufgrund unvorhergesehener Änderungen auseinanderzufallen beginnt, ist es schwierig, diese starre Struktur zu ändern. In der Komposition werden Objekte aus anderen Objekten erstellt und delegieren Arbeiten an diese. Dies ist nützlich, da es einfach ist, das Verhalten eines Objekts zur Laufzeit zu ändern, indem Sie einfach die Objekte ändern, aus denen es besteht. Diese zusammengesetzten Objekte sind noch verständlicher, da der Code aus der Vererbungshierarchie in eine Abstraktion gezwungen wird, die sich an einer bestimmten Aufgabe orientiert.

Eine solche Komposition ist einer der Hauptvorteile, die uns die MVP-Architektur gegeben hat. Jetzt können Sie das Verhalten des Controllers ändern, indem Sie einfach die Zusammensetzung eines bestimmten Präsentators ändern. Wir sind jetzt weniger besorgt über die Entschlüsselung der komplexen und starren Vererbungsstruktur. Schließlich sind View Controller und Presenter-Objekte leichter zu verstehen, da sie klarere Aufgaben haben.

Durch die Einführung von Presenter und das Verschieben eines Teils der View-Controller-Logik haben wir die Controller-Vererbungshierarchie vereinfacht. Die folgende Abbildung zeigt, dass wir die GalleryFeedViewController-Klasse entfernen konnten, da wir all diese Logik in den Presenter eingefügt haben. Wie bereits erwähnt, ist eine solche Vererbungshierarchie leichter zu verstehen und weniger starr.


Vereinfachen Sie die Vererbungshierarchie durch Komposition

Freie Änderung an der Implementierung der Präsentationsschicht


Wie bereits erwähnt, begann die Bildlaufleistung von 60 FPS auf 45 bis 55 FPS zu sinken. Aus diesem Grund haben wir uns für die Bandpräsentationsebene für die Verwendung von Texture entschieden . Es handelt sich um eine Open-Source-Plattform auf Apple UIKit-Basis, die die Schnittstellenleistung durch Vorverarbeitung im Hintergrundthread verbessert. In der vergangenen MVC-Architektur konnten wir die Implementierung auf Präsentationsebene nicht ohne viel Codeduplizierung ändern.


Vor der Implementierung von MVP mussten Sie im ViewController fremden Code duplizieren, der nicht mit View zusammenhängt (orange).

Die neue MVP-Architektur ermöglichte die Einführung der Texturunterstützung, anstatt die Dinge von Grund auf neu zu schreiben. Wir haben nur die gesamte Nicht-View-Logik in die generische Presenter-Klasse eingefügt. Dann schrieben sie eine neue Implementierung der c-Textur-Präsentationsschicht und verwendeten den Presenter-Code erneut. Dies gab Unterstützung für beide Implementierungen von View, bis es Zeit war, das Band mit Texture für alle Benutzer bequem auszurollen.


Nach der MVP-Implementierung: Code ohne Ansicht in Shared Presenter verschoben

Was ist das Ergebnis? Das folgende Diagramm zeigt eine Steigerung der Band-Scroll-Leistung. Wir wollten ungefähr 60 FPS bleiben, um ein absolut flüssiges Scrollen zu erreichen.



Unit Testing


Natürlich haben wir Unit-Tests nicht nur wegen MVP implementiert, sondern es war auch ein wichtiger Faktor. Insbesondere die MVP-Architektur hat den Testbereich erweitert, indem Code auf eine Ebene verschoben wurde, auf der die Überprüfung einfacher ist. Ein Nebeneffekt ist, dass die Ansichtsebenen einfacher geworden sind - und daher weniger häufig getestet werden müssen.


Erhöhen des Testbereichs nach dem Verschieben von Nicht-View-Code außerhalb dieser Ebene

Unit-Tests haben die Unterstützung der Codebasis verbessert: Sie ermöglichen es Ihnen, Änderungen sicherer vorzunehmen und das richtige Verhalten zu verstehen. Sie machen den Code auch flexibler und verständlicher, da sie Methoden wie Abhängigkeitsinjektion, Komposition und Abstraktionsprogrammierung fördern. Die Anzahl der Unit-Tests ist von wenigen auf über 200 gestiegen.

Kritische Analyse von MVP in Reddit


Obwohl der Wechsel zu MVP viel geholfen hat, gibt es noch ein paar Dinge zu beachten.

Der Übergang des Bandes zu Texture verursachte neue Probleme mit Threads. Die Anwendung unterstützte die asynchrone Implementierung von View zunächst nicht. Das heißt, Fehler treten unvermeidlich auf, wenn der Ansichtsstatus und der Status der Anwendung nicht übereinstimmen. Beispielsweise kann eine Bandansicht N Datensätze enthalten. Und im Hintergrund-Thread hat sich der Status der Anwendung leise geändert - und enthält jetzt weniger als N Nachrichten. Wenn die Diskrepanz nicht behoben wird, stürzt die Anwendung einfach ab, wenn View versucht, den n-ten Beitrag im Stream anzuzeigen.

Die am schwierigsten zu behebenden Fehler mit Threads. Sie sind schwer zu reproduzieren, daher sind sie schwer zu debuggen. Ich musste die Logik der Anfrage ändern und Daten empfangen, um den Feed anzuzeigen. Insbesondere haben wir "Schutz" implementiert, mit dem Verbot von Änderungen an der Datenquelle, wenn die Ansicht des Bandes einige Änderungen erfährt. Diese und andere kleinere Korrekturen reduzierten die Anzahl der mit der Streaming-Verarbeitung verbundenen Fehler. Das asynchrone Multithreading kann jedoch noch verbessert werden.

Zweitens stellt die Presenter-Ebene einen zusätzlichen „Schritt“ in der Pipeline dar. Dieser Schritt hat seinen Preis in Bezug auf die Erhöhung der Codekomplexität und die Verringerung der Leistung. Manchmal möchten Sie diese Logik nur aus einer Laune heraus oder weil Sie es gewohnt sind, im UIViewController ausführen. Im schlimmsten Fall werden Sie feststellen, dass Presenter einfach als Entität ohne sinnvolle Logik vorhanden ist. In einer solchen Situation scheint der Präsentator seine Existenz nicht zu rechtfertigen.


Manchmal können Sie ohne Presenter von einer Ansichtsebene zu einer RedditCore-Ebene wechseln

Tatsächlich ist unsere Anwendung nicht vollständig in die MVP-Architektur konvertiert. Erstens ist die Konvertierung jedes einzelnen UIViewControllers in einen Presenter zu zeitaufwändig - und nicht evolutionär. Zweitens wird, wie im vorherigen Absatz erwähnt, Presenter manchmal einfach nicht benötigt. Wie wir in unserer Arbeit zur Implementierung von Texture for Ribbon festgestellt haben, eignet sich Presenter hervorragend zur Erleichterung einer massiven MVC oder zur Implementierung von View mit variablem Verhalten oder wenn Sie über eine komplexe Logik verfügen, die überprüft werden muss. Aber manchmal ist der UIViewController so einfach, dass Presenter keinen Sinn ergibt. Es ist also optional. Presenter sollte nur bei Bedarf implementiert werden.

Zusammenfassung und Zukunftspläne


Durch die Überarbeitung der MVP-Architektur in der Reddit iOS-App konnten viele der Aufgaben gelöst werden. Durch die Einführung der Presenter-Schicht haben wir schrittweise die Anwendungsarchitektur entwickelt, um die neue Implementierung der Präsentationsschicht zu unterstützen, ohne andere Funktionen zu stören. Der Code wurde klarer, indem die "massive MVC" erleichtert wurde - die Übertragung von Fremdlogik auf die Presenter-Ebene. Wir haben Entwicklern auch die Möglichkeit gegeben, schneller zu iterieren und neue Funktionen bereitzustellen. Und die Tests deutlich verbessert.

Angesichts all dessen ist es noch ein langer Weg. Wir erstellen weiterhin Presenter-Objekte und verbessern sie. Wir müssen weiterhin fremde Logik von UIViewControllern auf die Presenter-Ebene verschieben. Es ist auch notwendig, dass alle Präsentatoren besser auf das Prinzip der alleinigen Verantwortung ausgerichtet sind. Am Ende entwickeln sich sowohl die Anwendung als auch die Architektur ständig weiter.

Source: https://habr.com/ru/post/de423531/


All Articles