pipeline-Design
- Eine Entscheidung erfahrener Teammitglieder.
- Der Vorteil von Pipeline besteht darin, dass
VSeedden Ausführungsfluss jedes Charttyps unabhängig steuern kann. Durch gutes Design kann die Implementierung jedes Charttyps entkoppelt und zugleich lokal wiederverwendbar sein, während jede Chartklasse jedes Detail präzise kontrollieren kann. Genau das bringt Pipeline, und genau das brauchtVSeedam meisten. - Im Vergleich dazu lassen sich die Nachteile des Pipeline-Musters beim Design vermeiden. Wenn die Größe einzelner
Pipes reduziert und Abhängigkeiten zwischenPipes verringert werden, lassen sich die Nachteile dieses Musters weitgehend vermeiden. - Nach vier Generationen von Pipeline-Design und Optimierung ist VSeed bereits die fünfte Version. Die typischen Stolpersteine wurden bereits durchlaufen.
Was ist Pipeline?
Pipeline ist eine mächtige Abstraktion und Engineering-Praxis. Sie zerlegt eine komplexe Aufgabe in eine Reihe kleinerer, miteinander verbundener Schritte, die nacheinander ausgeführt werden. Ihre Designidee und Implementierung sind stark von den Kernideen der funktionalen Programmierung (FP) geprägt.
Vorteile von Pipeline:
- Modularität: atomare Implementierung; durch Kombination von Atomen entstehen Module.
- Automatisierung: Sobald die Eingabe bestimmt ist, wird die Ausgabe automatisch erzeugt, ohne dass interne Details beachtet werden müssen.
- Reine Funktion: Eine bestimmte Eingabe liefert immer die erwartete Ausgabe; das ist ein Merkmal reiner Funktionen.
- Parallelität: unterstützt von Natur aus Nebenläufigkeit.
- Wiederverwendbarkeit: Jedes Modul kann wiederverwendet werden.
- Testbarkeit: Theoretisch ist jedes Modul unabhängig und kann separat getestet werden, um Qualität sicherzustellen.
- Nachverfolgbarkeit: Eingaben und Ausgaben jeder Phase sind klar, was Fehlersuche und Prozessüberwachung erleichtert.
- Cachebarkeit: Theoretisch kann die Ausgabe eines einzelnen
Pipeseparat gecacht werden, um wiederholte Berechnungen zu vermeiden und Effizienz zu erhöhen.
Nachteile von Pipeline:
- Reihenfolgeabhängigkeit: Wenn zwischen
Pipes eine Reihenfolgeabhängigkeit besteht, steigt der Verständnisaufwand, weil frühere Phasen verstanden werden müssen, bevor spätere Phasen verständlich sind. Zur schnellen Fehlersuche ist ein tieferes Verständnis des Gesamtprozesses nötig. - Debugging-Kosten: Da Pipeline sequenziell ausgeführt wird, führt der Fehler einer Phase zum Fehlschlag der gesamten Pipeline. Das erschwert Debugging, weil die fehlerhafte Phase gefunden und repariert werden muss.
- Performance-Probleme: Da Pipeline sequenziell ausgeführt wird, muss jede Phase auf die Ausgabe der vorherigen Phase warten. Das kann zu Performance-Problemen führen, besonders wenn eine Phase lange dauert.
- Funktionale Programmierung: Neue Konzepte müssen verstanden werden, was Lernaufwand bedeutet. Deshalb sollten Designprinzipien und Implementierungsdetails im Contribution Guide dokumentiert werden, damit andere Entwickler sie verstehen und nutzen können.
Wie sollte Pipeline in VSeed geschrieben werden?
Pipe-Kompositionsmuster
Mehrere funktionale Pipes können zu einem größeren funktionalen Pipe oder zu einer komplexeren Pipeline kombiniert werden.
In VSeed entspricht eine vollständige Pipeline der Implementierung eines Charttyps. Durch Beschreibung der Kompositionsbeziehungen zwischen Pipes lassen sich unterschiedliche Charttypen erstellen. In der Kompositionsphase der Pipeline muss man sich nicht um die konkrete Implementierung jedes pipe kümmern.
Kompositionsunterschiede
Ein Beispiel:
Liniendiagramme und Flächendiagramme können viele Funktionen wiederverwenden, etwa Labels, Legenden und Achsen. Ein Liniendiagramm hat jedoch keinen Flächen-Mark-Stil. Daher löst die pipeline diesen Unterschied durch Kombination funktionaler Pipes, ganz ohne if-Anweisung.
Pipe-Adaptermuster
Neben dem Kompositionsmuster hat der Aufbau eines Pipe häufig bestimmte Bedingungen. Um Pipe-Kombinationen unter unterschiedlichen Bedingungen zu erfüllen, verwendet VSeed viele Pipe-Adapter.
Kompositionsbedingungen
Ein Beispiel:
Liniendiagramme unterstützen Pivot. Ohne Pivot werden sie von VChart gerendert und geben eine VChart spec aus. Mit Pivot werden sie von VTable gerendert und geben eine VTable spec aus.
Pivot-Liniendiagramme müssen im Wesentlichen die Grundfunktionen von Liniendiagrammen wiederverwenden, etwa Labels, Legenden und Achsen. Deshalb wird das Adaptermuster benötigt, um Pipes eines Liniendiagramms in Pipes eines Pivot-Liniendiagramms umzuwandeln.
Zusammengefasst ist jeder Adapter ein if else. Bedingungen, die in einem pipe verborgen wären, können als Adapter abstrahiert werden; dadurch wird das if else auf die oberste Ebene verschoben. So entsteht eine Pipeline mit klareren Abhängigkeiten und geringeren Wartungskosten.
Die Grundeinheit von Pipeline: funktionaler Pipe
VSeed erwartet, dass alle Charttypen Funktionen als Grundeinheit verwenden, um ausreichende Wiederverwendbarkeit und Erweiterbarkeit bereitzustellen. Die Pipeline eines Charttyps wird von unten nach oben aufgebaut. Jeder funktionale Pipe sollte ein unabhängiges, testbares und wiederverwendbares Modul sein.
Entscheidend ist, funktionale Unterschiede als unterschiedliche Pipes zu abstrahieren, also weniger if else zu schreiben, statt einen großen allumfassenden Pipe zu schreiben.
Abgeflachter funktionaler Pipe
Ein Beispiel:
Balken-, Säulen-, Linien-, Flächen- und Streudiagramme haben alle X- und Y-Achsen. Sie sind ähnlich, aber unterscheiden sich leicht. Würde man einen großen allumfassenden axes pipe schreiben, könnte das so aussehen:
Die obige Logik wählt innerhalb eines funktionalen Pipe je nach Charttyp unterschiedliche untergeordnete pipes aus. Das führt zu zwei Problemen:
- Wie sollen die wiederholten Funktionen in
xy,yxundyywiederverwendet werden? Viele ähnliche, aber verschiedene Unterfunktionen müssen in unterschiedlichen untergeordnetenpipes wiederholt aufgerufen werden. Abhängigkeiten können leicht unübersichtlich werden und Wartungskosten erhöhen. - Beim Ändern von Funktionen für Linien- und Flächendiagramme kann man Balkendiagramme leicht übersehen, weil die Logik verzweigt ist. Beim Implementieren neuer Funktionen müssen daher Unterschiede berücksichtigt werden.
Wenn die gesamte spec pipeline auf mehrere hundert pipes anwächst, verursacht diese Schreibweise sehr hohe Wartungskosten. Daher brauchen wir einen einfacheren Weg, um je nach Charttyp unterschiedliche untergeordnete pipes auszuwählen.
Setzt man das obige Beispiel fort, werden die Unterschiede als verschiedene Pipes abstrahiert, auf feinerer funktionaler Granularität gekapselt und schließlich direkt in der pipeline kombiniert. So lassen sich die genannten Probleme vermeiden.
Im obigen Beispiel wird kein axes pipe implementiert. Stattdessen werden xBandAxis, yBandAxis, xLinearAxis und yLinearAxis direkt kombiniert. Dadurch wird vermieden, in einem axes pipe je nach Charttyp unterschiedliche untergeordnete pipes auszuwählen; charttypbasierte Verzweigungen und der Einsatz von if else werden reduziert.
Alle Verzweigungen aufgrund von Charttyp-Unterschieden sollten oberhalb der Pipeline liegen. Wenn es nicht unbedingt nötig ist, sollte Pipeline nicht je nach Charttyp unterschiedliche untergeordnete pipes auswählen.
Diese Kompositionsweise entspricht der Designphilosophie von VSeed: eine flachere Kombination funktionaler Pipes verwenden, statt mit if else einen großen allumfassenden funktionalen Pipe zu bauen.