Hallo zusammen. Vielen Dank fürs Ansehen dieser Präsentation. Ich werde über einige JMP-Klassen sprechen und darüber, wie man sie in JSL verwenden kann, um dynamische und erweiterbare Schnittstellenverhalten zu implementieren.
Mein Name ist Ben Barrett, ich arbeite für IDEXX Laboratories. Ich bin dort seit etwa zehn Jahren als Ingenieur tätig. Ich habe mit verschiedenen Anwendungen gearbeitet, darunter LabVIEW und MATLAB, und auch mit C# experimentiert. Generell habe ich mich bei all diesen Sprachen immer für den objektorientierten Programmierstil entschieden.
Aus irgendeinem Grund erscheint mir das intuitiv. Als ich jedoch mit Objekten in JSL zu arbeiten begann, merkte ich, dass mir diese Intuition nicht sehr weiterhalf. Ich musste einige Feinheiten überwinden, aber sobald ich ihren Umgang damit verstanden hatte, erwiesen sie sich als äußerst nützlich.
Ich beginne mit einem kurzen Überblick. Ich werde hier keine ausführliche Lektion zu JMP-Klassen halten. Ich möchte lediglich meinen Entdeckungsprozess beim Einstieg in die Arbeit damit schildern. Dabei zeige ich einige Schwierigkeiten, auf die ich gestoßen bin, und wie ich sie überwunden habe. Am Ende gibt es einige interessante Anwendungsbeispiele.
Hier ist eine grobe Agenda, mal sehen, ob ich mich daran halte. Ich möchte kurz auf den Gültigkeitsbereich eingehen, da dies bei der Implementierung einiger meiner Klassen ein wiederkehrendes Problem darstellt. Daraus führe ich einige meiner bewährten Vorgehensweisen. Außerdem möchte ich erläutern, inwiefern sich Klassen in JSL von denen anderer Sprachen wie C# unterscheiden. Anschließend zeige ich euch ein paar Beispiele für funktionalen Beispielcode.
Erwarten Sie hier keine tiefgehende, umfassende Einführung in Klassen in JSL oder gar in objektorientierte Designprinzipien im Allgemeinen. Hoffentlich hilft Ihnen dies aber, einige Schwierigkeiten zu vermeiden, falls Sie sich selbst mit Klassen auseinandersetzen möchten.
Hier ist die Agenda, ich habe das alles schon besprochen. Hier ist ein kurzer Überblick über die verschiedenen Scopes in JSL. Ich gebe nicht vor, alle Zusammenhänge und Anwendungsmöglichkeiten vollständig zu verstehen. Ich habe aber ein paar grundlegende Ideen dazu.
Es gibt vier allgemeine Gültigkeitsbereiche: einen lokalen, einen globalen und Ihre eigenen Namensräume. In diesem Dokument beschreibe ich, wie Sie mit den einzelnen Gültigkeitsbereichen interagieren können. Für weitere Informationen empfehle ich Ihnen, die Online-Dokumentation einzusehen. Generell hat sich jedoch gezeigt, dass die Arbeit mit Namensräumen beim Umgang mit Klassen am besten funktioniert.
Erstellen Sie eigene Namensräume, wenn Sie in Klassen darauf verweisen müssen. Das hat mir geholfen, viele Probleme mit Gültigkeitsbereichen und Konflikten zu vermeiden. Ich habe außerdem festgestellt, dass der Namespace `Here` in Bezug auf Klassen nicht besonders zuverlässig ist. Daher versuche ich, die Verwendung von `Here` als Standardwert zu vermeiden und diesen Wert nicht auf `1` zu setzen. Lokale Variablen können ebenfalls hilfreich sein, solange Sie den Befehl `local` von `Here` vermeiden. Dies erfordert jedoch etwas mehr Aufwand, da Sie explizit definieren müssen, welche Variablen lokal sein sollen. Einiges davon wird in den Demos deutlicher werden.
Ich habe gerade die Best Practices erläutert. Vermeiden Sie Standardnamen, verwenden Sie benutzerdefinierte Namensräume und Klassen. Klassen können Sie sich gewissermaßen als Namensräume vorstellen, die kopiert werden können, und Sie können gleichzeitig Duplikate im Speicher haben. Worin unterscheiden sich JSL-Klassen von Klassen, die Sie vielleicht kennen? Sicherlich gibt es weitere Funktionen, die manche von Klassen erwarten, aber ich denke, diese fünf stellen eine recht gängige Darstellung dessen dar, was man von Objekten erwartet.
Kapselung, Vererbung, Polymorphismus, Replikation und Abstraktion. Vererbung, Polymorphismus und Abstraktion hängen alle miteinander zusammen, und Klassen in JSL bieten eine recht gute Unterstützung für Kapselung, Vererbung und Replikation. Es gibt jedoch einige Feinheiten bei der Implementierung von polymorphem Verhalten und Abstraktion. Ich werde diese fünf Aspekte kurz erläutern.
Die Kapselung wird weitgehend unterstützt. Beim Definieren einer Klasse werden Eigenschaften und Methoden definiert, die dieser Klasse zugeordnet sind. Die einzige Besonderheit hierbei ist, dass keine dieser Annahmen zutrifft. Es gibt keine wirklich privaten Klassenmember. Alles ist öffentlich. Es verhält sich ähnlich wie mit einem Namensraum. Die Member und Eigenschaften einer Klasse sind global zugänglich.
Vererbung wird ebenfalls unterstützt. Wenn Sie eine von einer anderen Klasse abgeleitete Klasse erstellen, erbt diese Kindklasse die Elemente der Elternklasse. Es gibt einige Feinheiten bezüglich Polymorphismus, auf die wir später noch genauer eingehen werden. Um Polymorphismus vollständig zu implementieren, ist ein spezieller Workaround erforderlich, der es einer Elternklasse ermöglicht, Methoden ihrer Kindklassen innerhalb ihrer eigenen Methoden korrekt aufzurufen.
Replikation – das ist im Grunde der Hauptgrund, warum man Klassen verwenden sollte. Man kann mehrere Kopien eines Objekts instanziieren, und jede hat ihre eigenen, gekapselten Parameter und Methoden. Ich bin damit bisher auf keine Probleme gestoßen.
Abstraktion gibt es in JSL-Klassen nicht explizit, aber man kann eine Oberklasse mit Standardmethoden definieren, die beispielsweise einen Fehler zurückgeben oder ein bestimmtes grundlegendes Verhalten ausführen. Wenn Sie mit C# oder anderen Sprachen vertraut sind, ähnelt dies eher einer virtuellen als einer abstrakten Methode. Das ist ein kurzer Überblick darüber, wie sich Klassen in JSL von Klassen in anderen Sprachen unterscheiden. Nach diesem kurzen Überblick über Klassen in JSL werde ich nun zum nächsten Thema übergehen.
Wir haben unsere JMP-Instanz geöffnet. Ich habe ein paar recht einfache Beispiele. Nun ja, zuerst ein einfaches Beispiel, dann ein etwas komplizierteres und schließlich ein viel, viel komplizierteres. Das erste Beispiel, über das ich sprechen möchte, ist eine sehr einfache Fortschrittsanzeige.
Diese Demos dienen alle dem Zweck, die GUI-Entwicklung erweiterbarer und modularer zu gestalten, damit Sie die Möglichkeiten von Objekten voll ausschöpfen können. Ich werde diesen Fortschrittsbalken nur kurz ausführen, damit Sie seine Funktionsweise sehen können. Die grundlegende Implementierung habe ich online in einem Beitrag gefunden, in dem jemand danach gefragt hatte. Ich zeige Ihnen die Details der Erstellung des Fortschrittsbalkens, aber sie sind für dieses Thema nicht so wichtig.
Dieser Code definiert im Wesentlichen die Darstellung des Fortschrittsbalkens. Er erstellt ein Fenster. Hier werden die Objekte erstellt, deren Code wir gleich genauer betrachten werden. Anschließend werden die Elemente im Fenster angeordnet und verschiedene Elemente generiert, die die auszuführende Aufgabe repräsentieren. Danach wird die Verarbeitung iterativ durchgeführt und der Fortschrittsbalken mithilfe der Methoden aktualisiert, die Teil der Klassendefinition sind.
Hier gibt es keine Kurve, aber dieses Objekt kapselt das gesamte Verhalten, das diese Visualisierung erzeugt. Ich öffne mal das Fenster. Wir sind jetzt im Code. Ich gehe zur Definition der Fortschrittsbalkenklasse und minimiere diese Fenster kurz. Sie sehen, dass Sie eine Klasse mit dem Aufruf `define class` definieren, ihr einen Namen geben und sie so instanziieren. Variablen in einer Klasse zu definieren ist dann ganz einfach … Sie definieren sie genauso wie jede andere Variable.
Dieser Wert wird nun der Klasseninstanz zugewiesen. Sie greifen darauf zu, indem Sie die Klassenreferenz und anschließend den Klassennamen verwenden. Methoden in Klassen werden analog zu Funktionen definiert. Im Wesentlichen handelt es sich um Funktionen unter einem anderen Namen, die dem jeweiligen Klassenobjekt zugewiesen werden. Wie bereits erwähnt, sind Klassen wie ein Namensraum, den Sie bearbeiten können. Nachdem Sie eine Klasseninstanz erstellt haben, können Sie all diese Eigenschaften bearbeiten. Sie können Eigenschaften hinzufügen, die dem erstellten Objekt zugewiesen werden. Mit JMP-Klassen sind vielfältige dynamische Operationen möglich.
Ich werde hier nicht weiter ins Detail gehen. Ich möchte mich nur darauf konzentrieren, wie diese Fortschrittsanzeige erstellt wird. Die Initialisierungsfunktion wird ausgeführt. Ich zeige sie Ihnen mal in einer Seitenansicht. In diesem Beispiel wird die Initialisierungsfunktion beim Erstellen des Objekts ausgeführt. Der Aufruf von `new object` ruft `_init` automatisch auf.
Bei der Initialisierung ist hier eine Standardkonfiguration eingerichtet, Sie können aber auch eine beliebige andere Konfiguration angeben. Dadurch werden alle grundlegenden Einstellungen vorgenommen, und am Ende wird der Build-Befehl aufgerufen. Die Build-Methode ist hier unten definiert. In der Build-Methode erstellen wir alle Objekte, die für die Darstellung des Fortschrittsbalkens erforderlich sind. Wir haben unseren Container und einige Abstandshalter. Durch Vergrößern und Verkleinern der Abstandshalter wird der Fortschrittseffekt erzeugt. Diese Information stammt aus einem JMP-Community-Beitrag, dessen Quellcode ich leider nicht zur Hand habe.
Hier werden alle Details für das Aussehen des Fortschrittsbalkens festgelegt. Es gibt Hilfsmethoden, mit denen Sie Höhe und Breite anpassen können. Wie interagiert man mit dem Balken und ändert den Fortschritt? Dazu gibt es verschiedene Methoden mit unterschiedlichen Funktionen. Die Methode `set` legt den Prozentsatz explizit fest, `step` erhöht den Fortschritt um einen bestimmten Wert. Weitere Einstellungen können Sie nach Belieben festlegen.
Man könnte die genaue Anzahl der Schritte angeben und sie dann bei jeder Iteration erhöhen. Die Methoden ließen sich an die jeweilige Aufgabe anpassen. Wenn wir nun, da wir den Kontext kennen, auf das Beispiel zurückkommen, ist es recht einfach. Wir erstellen das Objekt, und dann … Wenn wir hier ein neues Fenster einrichten, müssen wir, um das Objekt nutzen zu können, das eigentliche Anzeigeelement daraus extrahieren können.
Im Fortschrittsbalken ist der übergeordnete Container diese H-Listenbox. Wenn wir das Objekt abrufen, geben wir lediglich dessen Wert oder den Container zurück. In der neuen Fensterdefinition können wir, um den Fortschrittsbalken an einer bestimmten Position einzufügen, einfach das Objekt oder das erstellte Objekt abrufen. Dies ließe sich beispielsweise mit „Panel abrufen“ verdeutlichen. Das wäre verständlicher, aber nun gut.
So würde man es einfügen. Hier referenzieren wir weiterhin das Objekt und durchlaufen es für den primären Balken. Für den sekundären Balken weisen wir ihm einen expliziten Wert zu. Wir aktualisieren den Text. Wir sehen uns das Ganze noch einmal an, um die Auswirkungen zu verdeutlichen. Wir verwenden hier wieder nur zufällige Werte und aktualisieren die Werte. Sie können sich vorstellen, wie Sie dies in eine andere Aufgabe einfügen könnten.
Eine weitere nützliche Sache, die ich erwähnen möchte: Sie können die Fortschrittsanzeige schließen. Wenn Sie dies tun, schlägt der nächste Durchlauf fehl, da keine Referenz mehr zum Fenster besteht. Die Anzeige ist geschlossen, und wenn Sie nun versuchen, das Fenster mit diesem Update zu aktualisieren, kann dies nicht geschehen. Daher wird ein Fehler zurückgegeben.
Da das alles in einem try-Block steht, schlägt dieser fehl und springt hierher zurück. Das ist also eine schnelle und einfache Möglichkeit, die Verarbeitung zu unterbrechen, falls nötig – was ich schon mehrmals gebraucht habe. Das ist die Fortschrittsanzeige. Eine recht einfache Implementierung. Man kann sie auf vielfältige Weise anpassen. Man kann Informationen daraus abrufen, was auch immer.
Als Nächstes möchte ich über etwas Komplizierteres sprechen, das wahrscheinlich ein wenig Vorbereitung erfordert. Ich möchte diese Tabellen hier einblenden. Ich habe die große Beispieltabelle verwendet und sie mit zufälligen Daten versehen, um die Funktion des virtuellen Joins zu demonstrieren. Für alle, die mit virtuellen Joins nicht vertraut sind: Es gibt den Standard-Join, bei dem wir … Wir haben diese Klasse mit den höchsten Punktzahlen. Sie enthält mehrere Einträge für jeden Schüler. Jeder Schüler hat zehn Versuche unternommen und eine Punktzahl erzielt. Die Klasse enthält nur den Namen, den Versuch und die Punktzahl, aber wir haben hier all diese anderen Informationen. Wir möchten diese Informationen hier einfügen. Dazu führen wir einen Join durch. Ich bin sicher, dass jeder, der JMP verwendet, das schon einmal gemacht hat.
Dadurch werden diese Werte in diese Tabelle eingefügt, aber es gibt noch eine andere Möglichkeit. Alle diese Namen sind eindeutig. Wir können sie als Verknüpfungs-ID festlegen und dann hier in dieser Tabelle darauf zugreifen. Dadurch haben wir einen virtuellen Join und können alle Informationen einsehen. Wenn wir hierher zurückkehren, kann ich die Informationen ein- oder ausblenden, und nun sind die Informationen aus dieser Spalte dieser Tabelle in dieser Tabelle verfügbar, ohne dass ein Join erforderlich ist. Dasselbe könnten wir auch mit dieser Tabelle tun.
Das bietet einige wirklich coole Vorteile. Wenn wir hier die Spalteninformationen und die Linkreferenz-Eigenschaft aufrufen, können wir festlegen, ob der Status „ausgewählt“ oder „ausgeschlossen“ akzeptiert wird. Wenn ich das anwende und dann hier diese Zeile auswähle, werden automatisch alle zugehörigen Zeilen ausgewählt. Dasselbe gilt für Zusammenfassungen, wenn man eine Verknüpfung zur Datentabelle oder Datenansicht herstellt – dort passiert im Prinzip dasselbe.
Das ist eine echt coole Funktion. Ich habe sie letztes Jahr irgendwann mal benutzt und wollte das Ganze schnell per JSL-Skript automatisieren. Im Grunde habe ich dann quasi einen Link-Manager geschrieben. Ich lösche die mal. Wir löschen die Link-ID. Wir löschen die Link-Referenz. Das sind jetzt ganz normale, unverknüpfte Tabellen.
Ich werde jetzt den Beispiel-Linkmanager hier öffnen. Ich führe einige Schritte aus. Wir werden alle Tabellen einrichten und den Linkmanager initialisieren. Den zugrundeliegenden Code erkläre ich Ihnen gleich.
So, da haben wir's. Ich weiß nicht, was der Fehler war, aber hier sehen wir das Standardfenster des Link-Managers. Die Benutzeroberfläche ist geöffnet, aber es wurden noch keine Links erstellt. Über die API können wir eine Linkquelle festlegen, was im Prinzip dem entspricht, was ich vorher hier gemacht habe, wo ich die Link-ID festgelegt habe. Wenn ich das ausführe, sehen wir, dass der Link-Manager aktualisiert wurde.
Hier erhalten wir diesen neuen GUI-Eintrag. Wir können nun eine der Tabellen verknüpfen. Wir erhalten einen neuen Eintrag. Wir können die zweite Tabelle verknüpfen. Wir erhalten einen zweiten Eintrag und haben nun die Möglichkeit, auf diese Verknüpfungskonfigurationen zuzugreifen und sie zu ändern.
Wenn ich diese eine Tabelle verwenden möchte, also die zweite Tabelle, kann ich die Auswahl- und Ausschlusskriterien so konfigurieren, dass sie alle gelten. Kurz gesagt: Wenn ich hier etwas auswähle, kann ich die Auswahl auch hierhin übertragen. Wir haben die gewünschten Elemente ausgewählt. Nun sollten alle diese Elemente ausgewählt sein, und zusätzlich sollte in der ursprünglichen Tabelle auch die Quelltabelle ausgewählt sein.
Normalerweise müsste man, um diese Einstellungen zu aktualisieren, mit der rechten Maustaste klicken, „Spalteninformationen“ auswählen, zu „Verknüpfungsreferenz“ gehen, „Versenden“ oder „Akzeptieren“ auswählen, die gewünschten Änderungen vornehmen und dann „Anwenden“ klicken. Anschließend müsste man die Änderungen an der anderen Stelle wiederholen. Ich wollte jedoch eine dynamischere Lösung, bei der man die Vorgänge direkt verfolgen kann. Man könnte eine weitere Tabelle verwenden, mehrere Datenquellen einrichten, um all dies zu realisieren, und alle Konfigurationen befinden sich direkt hier. So kann man Funktionen deaktivieren, Verknüpfungen entfernen usw.
Dieses Tool erwies sich als besonders nützlich bei der Arbeit mit großen Datensätzen, die sich über viele Tabellen erstrecken. Man hat beispielsweise eine große Übersichtstabelle, hat kontinuierliche Daten verarbeitet und möchte nun Verknüpfungen herstellen, ohne eine riesige Tabelle zu erstellen, da diese schnell unübersichtlich werden kann. Aus diesem Grund ist dieses Tool entstanden.
Wenn wir uns nun dem Code zuwenden, wird es etwas komplexer. Ich möchte gleich vorwegnehmen, dass nichts davon optimiert ist. Vieles ist noch… sozusagen der erste Entwurf. Ich habe es zum Laufen gebracht und seitdem nichts mehr überarbeitet. Bitte berücksichtigen Sie das bei der weiteren Erläuterung.
Hier sehen Sie die von mir aufgerufene Initialisierungsfunktion. Sie prüft, ob bereits ein Objekt existiert; andernfalls wird eines erstellt. Verbesserungswürdig ist möglicherweise Folgendes: Ursprünglich dachte ich, ich bräuchte mehrere Link-Manager gleichzeitig, falls ich mehrere Projekte betreibe. Das mag nützlich sein, muss aber nicht. Aktuell ist diese Klasse jedoch im Wesentlichen als Singleton implementiert.
Sie könnten bei Bedarf weitere Instanzen erstellen, aber diese Funktionalität wäre in einigen Details, auf die ich gleich eingehen werde, etwas unzuverlässig. Im Wesentlichen verwaltet der Link-Manager jedoch Arrays, die definieren, wie die Tabellen miteinander verknüpft sind, welche Tabelle mit welcher verknüpft ist und welchen Status die Verknüpfung aktuell hat. Er verfügt über Methoden zum Ändern der Verknüpfungen und für alle damit verbundenen Aufgaben.
Für die grafische Benutzeroberfläche haben wir zwei Klassen implementiert. Da ist zum einen die Link-Konfiguration. Wenn ich das Link-Manager-Fenster öffnen könnte… Wo ist es? Genau hier.
Im Link-Manager-Fenster befindet sich die Link-Konfiguration. Hier können Sie die Annahme- und Weiterleitungseinstellungen ändern sowie Ausschlüsse auswählen. Der Reiter „Linkquelle“ ist einer davon. Jede Quelle erhält einen eigenen Reiter, da Regeln dafür gelten, welche Spalten und welche Elemente verlinkt werden können. Beim Erstellen einer Linkquelle erzeugt der Link-Manager ein solches Objekt.
Es laufen noch viele andere Prozesse ab, um Dinge zu überprüfen und Ereignisse einzurichten, beispielsweise zum Schließen von Tabellen. Das können Sie aber ignorieren. Der Kern der Sache ist folgender: Es wird ein neues Linkquellen-Tab-Objekt erstellt und die Tabelle bereitgestellt.
Das war's im Prinzip. Es wird an die GUI angehängt, und ich glaube, das befindet sich hier unten. Hier ist die Quellenliste, und das hier wird daran angehängt. Das ist nur ein Container, und Sie sehen, wo wir diese Funktion haben, um den Tab zu erhalten, der das eigentliche Anzeigeobjekt ist. Das übernimmt im Grunde einen Großteil der Replikation. Jedes Mal, wenn wir eine neue Quelle erstellen, erstellen wir ein neues dieser Objekte und erhalten automatisch alle Funktionen.
Wenn wir zum Quellcode-Tab wechseln, finden wir dort ebenfalls eine Initialisierung. Dieser Bereich verfügt über ein eigenes Anzeigeobjekt (eine Seitenbox), in dem Elemente ausgerichtet werden, und enthält außerdem ein Listenobjekt. Dieses Listenobjekt enthält alle möglichen Linkkonfigurationen. Bedenken Sie, dass dies wahrscheinlich noch nicht optimal ist, aber Sie erkennen bereits die Vorteile… Es ist potenziell einfacher, dynamisches Verhalten in JMP-Oberflächen zu implementieren. Ich habe dies bereits an mehreren Stellen erfolgreich eingesetzt.
Einigen von euch ist vielleicht aufgefallen, dass ich hier oben diese Recall-Bibliothek habe. Darauf hatte ich vorhin angespielt, als ich von einer deutlich komplexeren Implementierung sprach. Ich glaube, ich habe nicht viel Zeit, mich da wirklich einzuarbeiten. Schade, denn das war tatsächlich mein allererstes Projekt mit JMP-Klassen. Leider befindet es sich, sozusagen, noch im Entwurfsstadium. Es gibt hier vieles, was sich leicht zusammenfassen ließe.
Ich hoffe, ich konnte Sie zum Nachdenken darüber anregen, wie man Klassen in JMP nutzen kann, um dynamischeres Verhalten zu erzielen und den Code etwas übersichtlicher zu gestalten. Denn ich weiß noch, wie kompliziert und dynamisch die Benutzeroberfläche sein kann, wenn man alles direkt in die Funktion zum Öffnen eines neuen Fensters einbaut.
Gleich im neuen Fenster hat man plötzlich 20 Tabs voller Funktionen und Schaltflächen, die dies und das tun, obwohl es viel elegantere und objektorientiertere Möglichkeiten gibt, dasselbe Verhalten zu implementieren. Das ist wartungsfreundlicher, skalierbarer und leichter lesbar. Ich denke, die Arbeit mit Klassen bietet viele Vorteile bei der Implementierung von Benutzeroberflächen.
Ich werde jetzt nicht näher auf die Recall-Funktionen eingehen. Ich möchte nur hinzufügen, dass ich den gesamten Beispielcode und die Recall-Bibliothek überarbeiten und in einem öffentlichen Git-Repository bereitstellen werde. Dort kann jeder Interessierte einen Blick darauf werfen, das Repository klonen, forken und es nach Belieben nutzen, um zu verstehen, wie man JMP-Klassen im eigenen Code effizienter einsetzen kann.
Ich denke, es bliebe nur noch kurz auf einige der Punkte zurückzukommen, die ich anfangs im Zusammenhang mit dem Gültigkeitsbereich angesprochen habe. Wir haben die Beispiele durchgesehen, Sie haben gesehen, wie man Strukturen für die Implementierung dieser dynamischen GUIs aufbauen kann. Jetzt geht es darum, die Schwierigkeiten zu verstehen und wie man sie umgehen kann. Ein gutes Beispiel wäre… Mal sehen, ob wir das hier umsetzen können.
Wenn ich mich für eine dieser Optionen entscheide, betrifft das bereits erwähnte Gültigkeitsbereichsproblem die Art und Weise, wie Methoden aufgerufen werden und welchen Gültigkeitsbereich sie erben. Ich habe mehrere Probleme beim Arbeiten mit dem Here-Namensraum beobachtet. Es kam zu Namenskonflikten zwischen dem globalen Namensraum und Methoden. Um es so einfach wie möglich zu machen: Die narrensichere Methode, die ich identifiziert habe, um alle Gültigkeitsbereichsprobleme einer Klasse zu umgehen (ich zeige sie hier nicht, aber dieses Beispiel verdeutlicht die Vorgehensweise), ist die Verwendung eines lokalen Modifikators. Ich formatiere meine gerne so.
Die einzige Variable, die hier explizit definiert werden muss, ist diese hier. Sie dient lediglich dazu, die Eigenschaft zu extrahieren und zu erstellen, die die Referenz erzeugt. Dieser Code ist irrelevant. Allein diese Variable könnte eine Variable in einem anderen Gültigkeitsbereich überschreiben, da sie nicht als lokal definiert wurde. Je nachdem, wo diese Methode in Ihrem Code aufgerufen wird, könnte diese Variable einen Konflikt verursachen.
Die sauberste Methode, die ich bisher gefunden habe, um Gültigkeitsbereichskonflikte mit Klassen zu vermeiden, ist die explizite Angabe lokaler Variablen. Ich gehe davon aus, dass ich dies für praktisch alle Methoden tun muss, da der Großteil des Codes geschrieben wurde, bevor ich die beste Vorgehensweise zur Umgehung dieser Probleme kannte. Die einzige weitere Möglichkeit, die Probleme zu umgehen, ist die explizite Angabe von Namensräumen.
Immer wenn ich eine Bibliothek schreibe, schreibe ich sie im Grunde in einem neuen, benutzerdefinierten Namensraum, und ich versuche, alle meine Variablen und Methoden bzw. Variablen und Funktionen ebenfalls als Teil dieses Namensraums zu definieren. Das hat mir geholfen, Konflikte zu vermeiden.
Dann gäbe es da noch etwas anderes zu besprechen – auch wenn das hier kein besonders gutes Beispiel dafür ist –, nämlich die Probleme mit Polymorphismus und Vererbung. Mal sehen. Ein guter Ort, um darüber zu sprechen, wäre die Recall-Bibliothek.
Hoffentlich ist es nicht zu kompliziert… Wir können einige Implementierungsdetails auslassen und uns auf die Probleme konzentrieren. Nach dem bisherigen Stand lässt sich grob sagen, dass dieses Recall-Panel im Wesentlichen ein Container ist, der so konfiguriert werden kann, dass er die Werte der Felder automatisch abruft. Es ist vorgesehen, von einer Basisklasse zu erben, sodass abgeleitete Klassen bestimmte Funktionen aufrufen müssen. Konkret geht es um diese Methode. Die abgeleitete Klasse müsste ihre eigene Version der Build-Methode implementieren.
Hier kommt die Nuance der Polymorphie ins Spiel. Wenn man diese Methode aus einer anderen Methode aufruft – wir befinden uns also in der Elternklasse und möchten `build` aufrufen, während eine Kindklasse `build` überschreibt –, wird `build` nicht korrekt an die Kindmethode übergeben. Ich kann nicht genau erklären, warum, aber ich vermute, dass es an der Implementierung der Klassen im Backend liegt. Es gibt jedoch eine Möglichkeit, dies zu umgehen. Am besten lässt sich das meiner Meinung nach anhand einer anderen Bibliothek demonstrieren, aber leider kann ich darauf nicht näher eingehen.
Ich denke, ich werde die Details hier nicht weiter ausführen und möchte nur sagen, dass Polymorphismus, also die Möglichkeit, eine Kindklasse zu implementieren, die das Verhalten überschreibt, möglicherweise nicht wie erwartet funktioniert, was etwas bedauerlich ist, da ich die Vererbung für einen der nützlichsten Aspekte von Klassen halte.
Zwar lassen sich Verhaltensweisen vererben, doch ist es etwas komplizierter, diese geerbten Verhaltensweisen in der Elternklasse aufzurufen. So werde ich es formulieren. Sobald ich diese Materialien online stelle, werde ich versuchen, konkretere Beispiele hinzuzufügen, die verdeutlichen, warum das nicht funktioniert. Mir geht es im Grunde nur darum, einen Überblick darüber zu geben, wie man Klassen in Junk JSL in seine UI-Designs integrieren kann.
Damit beende ich meine Ausführungen. Mein Name ist Ben Barrett, und ich ermutige Sie, sich bei Fragen oder Interesse an weiteren Beispielen dieser Implementierungen an mich zu wenden. Vielen Dank.