Da Aktionen die wichtigste Art der Verwendung vieler App-Store-Module sind, lohnt es sich, etwas Zeit und Gedanken in die Entwicklung guter Aktionen zu investieren. Hier sind elf Best Practices, die ich für jeden habe, der eine Java(Script)-Aktion schreibt, entweder für die Verwendung in einem Projekt oder als Teil eines wiederverwendbaren Moduls.
1. Verwenden Sie beschreibende Parameternamen und fügen Sie ihnen einen Unterstrich hinzu
Der Name eines Parameters sollte beschreibend und nicht redundant sein. Dies ist ein allgemeiner Ratschlag für die Namensgebung, der auch hier zutrifft. Vermeiden Sie also Namen wie Objekt, Parameter, Daten. Beachten Sie, dass Aktionsparameter nicht nur in Mendix verwendet werden, wenn die Aktion aufgerufen wird, sondern auch im Java(Script)-Code als Variablennamen. Dies verursacht ein kleines Problem, wenn der Parametername ein reserviertes Wort in der jeweiligen Sprache ist, z.B. Object. Um dies zu umgehen, hat Mendix den Variablennamen im Code mit Parameter1 usw. angehängt. Dies wurde in neueren Mendix-Versionen überarbeitet, was das Problem noch verschlimmert. Jetzt kann derselbe Aktionscode auf einer älteren Mendix-Version gut funktionieren und auf einer neueren Mx-Version nicht kompiliert werden, weil die Variablennamen auf unterschiedliche Weise generiert werden.
Durch die Verwendung eines Unterstrichs als Suffix können alle Konflikte mit reservierten Wörtern, die mir bekannt sind, beseitigt werden und sind sogar nicht einmal sichtbar, wenn die Aktion aufgerufen wird, wie im Screenshot unten zu sehen ist. Perfekt!
2. Fügen Sie eine neue Aktion hinzu, anstatt Änderungen an der Signatur vorzunehmen.
Das haben wir alle schon einmal erlebt. Selbst die am besten durchdachte Aktion muss geändert werden. Einige dieser Änderungen werden unweigerlich eine Änderung der Signatur nach sich ziehen, z. B. weil ein neuer Parameter hinzugefügt oder ein Rückgabetyp geändert wird. Dies ist sehr störend, da alle entsprechenden Aktionsaufrufe im gesamten Projekt aktualisiert werden müssen.
Die alte Aktion sollte intern die neue aufrufen und einen Standardwert für die neuen Parameter übergeben oder einen Rückgabewert ignorieren, falls ein solcher hinzugefügt wurde. Achten Sie darauf, beiden Aktionen einen logischen Namen zu geben, vielleicht sollte die alte Aktion irgendwo das Wort "deprecated" enthalten. Eine ergänzende Praxis zur Vermeidung zu vieler Signaturänderungen wird im Folgenden beschrieben.
3. Bündeln Sie Flags, Enums und Integer-Einstellungen in einem einzigen"Options"-Objekt.
Oft hat eine Aktion mehrere Flags oder verschiedene Einstellungen, die angepasst werden können und daher irgendwie offengelegt werden müssen. Es ist am besten, alle diese Parameter in einem einzigen Objekt zu bündeln, das oft Optionen, Einstellungen oder Konfiguration genannt wird.
Durch die Verwendung eines "Options"-Objekts können mehrere Probleme auf einmal angegangen werden: 1) das Hinzufügen einer neuen Einstellung ändert nicht die Signatur; 2) Standardwerte können im Objekt definiert werden und verschmutzen nicht den Konstantenbereich; 3) die Dokumentation für jedes Flag hat einen eindeutigen Platz, ohne den Aktionsdialog zu überfrachten; 4) die Liste der Aktionsparameter kann kürzer sein, wodurch der Aufruf der Aktion schneller und sauberer wird (denken Sie daran, dass jeder Parameter explizit gesetzt werden muss).
Ein Beispiel für ein "Options"-Objekt aus dem Parallel Execute App Store-Modul.
4. Verwenden Sie immer eine Fassade, anstatt Bibliotheken von Drittanbietern direkt aufzurufen.
Dies gilt für alle, die Abhängigkeiten von Drittanbietern in Form von jar-Dateien oder Javascript-Modulen verwenden. Ich habe einen separaten detaillierten Blogbeitrag mit den besten Praktiken dafür geschrieben. Durch die Verwendung einer Fassade vermeiden wir harte Kopplung und ermöglichen es, die Bibliothek ohne großen Aufwand auszutauschen. Außerdem ist es am besten, ein sprachspezifisches Abhängigkeitsmanagement- und Build-Tool wie maven oder gradle für Java und npm oder webpack für Javascript zu verwenden. Diese Tools bieten eine einfache Verwaltung von transitiven Abhängigkeiten, die Integration von Tests in den Build-Prozess und die Paketierung für die gewünschte Plattform.
5. Führen Sie den Benutzer beim Aufbau von Objekthierarchien mit einem Builder-Muster
In manchen Fällen sind die Argumente für eine Aktion nicht nur eine einfache Zahl oder ein Objekt mit wenigen Parametern. Stattdessen wird eine ganze Hierarchie von Objekten benötigt, die in einer bestimmten, von der Aktion erwarteten Weise assoziiert werden muss. Dies lässt sich zur Entwurfszeit nicht erzwingen, so dass eine geschickte Methode erforderlich ist, um den Benutzer bei der Erstellung der richtigen Hierarchie und der Festlegung der richtigen Verknüpfungen zu unterstützen.
Bei mittelgroßen Hierarchien, d. h. ein oder zwei Assoziationen, ziehe ich es vor, ein Builder-Muster zu verwenden, um dem Benutzer bei der Vorbereitung der Daten zu helfen. Mit dem Builder-Pattern lassen sich komplexe Hierarchien in einfachen Schritten aufbauen, Schritt für Schritt. Außerdem wird jeder Schritt durch Typen eingeschränkt, die oft nur von anderen Schritten erhältlich sind. In einer idealen Welt wäre es möglich, die manuelle Instanziierung einiger Builder-Objekte vollständig zu verhindern, so dass der Benutzer gezwungen wäre, sie mithilfe einer Aktion zu erstellen.
Beispiel für ein Builder-Muster, das in meinem Modul "Web-Push-Benachrichtigungen" verwendet wird. In Schritt 1 wird ein Benachrichtigungsobjekt mithilfe einer Java-Aktion erstellt. In Schritt 2 wird dann ein NotificationAction-Objekt erstellt und mit einer Benachrichtigung verknüpft, aber anstatt dies direkt zu tun, wird wieder eine Java-Action verwendet, die ein Benachrichtigungsobjekt als Parameter benötigt. Damit ist es unmöglich, die Reihenfolge der Schritte zu verwechseln und Schritt2 vor Schritt1 aufzurufen oder das Setzen der Zuordnung zu vergessen.
6. Verwenden Sie die spezifischsten und einschränkendsten Typen wie möglich.
Dies ist ein weiterer allgemeiner Ratschlag, der auch für Aktionen gilt und eng mit der vorherigen Best Practice verbunden ist. Verwenden Sie anstelle der Zeichenketten "Ja" oder "Nein" boolesche Werte. Verwenden Sie Enums und Datumsangaben anstelle von Strings, wo dies möglich ist. Vermeiden Sie das Überladen von Nullen oder anderen speziellen Werten, um Informationen zu übermitteln, verwenden Sie stattdessen spezifische Typen oder Enums. Wenn die Antwort zum Beispiel eine Fehlermeldung oder Null ist (eine schreckliche Idee, siehe 11), verwenden Sie stattdessen ein Objekt mit dem booleschen Wert "HasError" und einer Fehlermeldung.
7. Verwenden Sie nur Code für Dinge, die in Mendix nicht nativ gemacht werden können.
Wenn etwas in Mendix gemacht werden kann, dann machen Sie es in Mendix. Insbesondere für das Abrufen und/oder Ändern von Objekten. Es ist viel einfacher und sicherer, einen Flow in der Mitte der Aktion aufzurufen, um eine Abfrage nativ in Mendix durchzuführen, als den XPath und die Abfrage mit Code zu konstruieren.
Wenn Sie Objektattribute ändern, denken Sie daran, dass das Umbenennen des Attributs nicht automatisch die bestehenden Aufrufe von Getter/Setter-Methoden auf dem jeweiligen Proxy umbenennt. Auch hier gilt: Verwenden Sie nur dann Code, wenn es keine Alternative gibt, um das Gleiche nativ in Mendix zu tun.
8. Fügen Sie keine unnötige Kopplung zu Mendix-Laufzeit- und Core-Methoden hinzu.
Vermeiden Sie die Kopplung von Code mit Mendix-APIs, insbesondere bei Java-Aktionen. Mendix nimmt häufig Änderungen an den Laufzeit- und Kernmethoden vor, siehe die umfangreiche Liste in der Mendix 8 Version. Um Kopfschmerzen zu vermeiden, sollten Sie sich nicht auf die Laufzeitbibliotheken oder Core-Methoden von Mendix verlassen, wenn es nicht unbedingt notwendig ist. Es ist klar, dass ein gewisses Maß an Kopplung unvermeidbar ist, aber das Ziel ist es, dies auf ein absolutes Minimum zu beschränken.
Wenn Sie die anderen Best Practices befolgen, sollte dies eine Selbstverständlichkeit sein. Wenn Sie sich irgendwann dazu entschließen, diesen Teil aus Mendix auszulagern und auf einen separaten Server zu verlagern, dann wird es durch weniger Abhängigkeiten einfacher, diesen Teil der Funktionalität zu migrieren.
9. Validieren Sie die Anzahl und den Typ der Parameter für Microflow-Parameter.
Dies gilt nur, wenn Microflows als Parameter verwendet werden. Leider ist es in Mendix immer noch nicht möglich, die Anzahl und den Typ der Parameter einzuschränken, wenn sie in einer Aktion verwendet werden. Bis das implementiert ist, ist die nächstbeste Möglichkeit, die Eingabe- und Ausgabetypen zur Laufzeit zu überprüfen. Dies ist nicht trivial, bitte schauen Sie sich das Parallel Execute Modul für eine gute Implementierung an, die auch Vererbung abdeckt.
10. Verwenden Sie json als Vermittler für die Kommunikation mit Aktionen.
Das Builder-Pattern wurde bereits als eine Möglichkeit erwähnt, mit mittelgroßen Objekthierarchien umzugehen. Aber was ist mit großen Objekthierarchien? In diesem Fall ist es am besten, auf json zurückzugreifen. Dies gibt den Benutzern die völlige Freiheit, ihr eigenes Entitätsdiagramm mit Hilfe eines Export/Import-Mappings auf/von json abzubilden. Mappings werden in Mendix nativ unterstützt, was bedeutet, dass sie mit Typsicherheit ausgestattet sind und eine einfache Umbenennung von Entitäten, Assoziationen und Attributen ermöglichen. Dies steht im Gegensatz zu Code, bei dem eine solche Umbenennung einen Kompilierungsfehler verursachen würde.
Um es auf die nächste Stufe zu heben, schlage ich vor, ein JSON-Schema zu verwenden, um das eingehende/ausgehende JSON zu validieren. Ein kostenloser Bonus, den Sie durch die Verwendung von JSON erhalten, ist, dass es die Prüfung von Aktionen erleichtert. Man muss nicht für jeden Testfall eine Objekthierarchie erstellen, sondern kann einfach ein Test-Json einfügen.
11. Lassen Sie Ausnahmen sich ausbreiten, anstatt sie zu verschlucken.
Wenn eine Ausnahme innerhalb des Codes auftritt, ist es am besten, sie an Mendix weiterzugeben. Dann kann der Aufrufer der Aktion entscheiden, wie er sie am besten behandelt. Es gibt selten einen guten Grund, Exceptions zu schlucken.
Manchmal kann es sinnvoll sein, eine Ausnahme mit einer einfacheren Fehlermeldung erneut auszulösen und die eigentliche Meldung auf einer anderen Ebene als Fehler zu protokollieren. Zum Beispiel ist "NumberFormatException in Zeile 192*"* viel weniger informativ als "Amount is not a valid number".
Ich hoffe, dass Ihnen dieser Beitrag gefallen hat und dass er Ihnen hilft, bessere Mendix Code-Aktionen zu entwerfen!