Ciao a tutti. Grazie a tutti per aver guardato questa presentazione. Parlerò di alcune classi JMP e di come utilizzarle in JSL per implementare comportamenti di interfaccia dinamici ed estensibili.
Mi chiamo Ben Barrett e lavoro per IDEXX Laboratories. Sono un ingegnere lì da circa 10 anni. Ho lavorato con diverse applicazioni, LabVIEW, MATLAB, ho sperimentato con C#. In generale, in tutti questi linguaggi, ho sempre avuto una propensione per uno stile di programmazione orientato agli oggetti.
Per qualche ragione, mi sembra semplicemente intuitivo. Tuttavia, quando ho iniziato a lavorare con gli oggetti in JSL, ho scoperto che queste intuizioni non mi servivano molto. C'erano alcune sfumature che ho dovuto superare, ma una volta capito come usarle, le ho trovate molto utili.
Inizierò con una rapida panoramica. Non farò una lezione approfondita sulle classi JMP. Voglio solo ripercorrere il mio processo di scoperta man mano che ho iniziato a lavorarci. Alcune delle cose che ho trovato difficili, i grattacapi che ho incontrato e come li ho superati, e alla fine alcuni esempi interessanti di come le ho utilizzate.
Ecco un programma di massima, vedremo come lo seguirò. Voglio parlare un po' di scoping perché ho scoperto che è un problema persistente nel modo in cui ho implementato alcune classi, e questo mi porterà ad alcune delle mie best practice. Voglio anche parlare di come le classi in JSL siano leggermente diverse da quelle che ci si potrebbe aspettare da altri linguaggi come C#. Poi passerò ad un po' di codice demo funzionale.
Non aspettatevi una lezione approfondita e approfondita sulle classi in JSL, o sui principi di progettazione orientata agli oggetti in generale. Speriamo che questo vi eviti qualche grattacapo se decidete di approfondire le classi da soli.
Ecco l'ordine del giorno, ho esaminato tutto. Ecco una rapida panoramica dei diversi ambiti disponibili in JSL. Non pretenderò di capire appieno come interagiscono tra loro e come utilizzarli. Ho alcune idee di base al riguardo.
Esistono quattro ambiti generali. Qui c'è un ambito locale, uno globale e infine i propri namespace personalizzati. Ho descritto come interagire con ciascuno di questi ambiti in questo documento. Consiglio vivamente di consultare la documentazione online se si desiderano maggiori informazioni. Ma in generale, quello che ho scoperto è che quando si cerca di lavorare con le classi, il mio approccio migliore è quello di lavorare con i namespace.
Crea i tuoi namespace personalizzati se devi farvi riferimento nelle classi, e questo mi ha aiutato a evitare molti dei problemi di ambito e di conflitto che si sono presentati. Ho anche scoperto che il namespace Here non è particolarmente affidabile quando si tratta di classi, quindi cerco di evitare di usare nomi predefiniti come Here e di impostarlo a uno. Inoltre, anche l'utilizzo di variabili locali può essere utile, a patto di evitare il comando Here locale. Tuttavia, questo richiede un po' più di manutenzione perché è necessario definire esplicitamente quali variabili si desidera siano locali. Ma alcuni di questi aspetti saranno più chiari man mano che approfondiremo alcune delle demo.
Ho appena ripassato quali sono queste best practice. Evitate nomi predefiniti, usate namespace personalizzati e poi anche le classi. Potete pensare alle classi in un certo senso come a uno spazio dei nomi che può essere copiato e contemporaneamente potete avere duplicati in memoria. In che modo le classi JSL differiscono da una classe con cui potreste avere familiarità? Sono sicuro che ci siano più funzionalità che alcuni potrebbero aspettarsi di vedere nelle classi, ma penso che queste cinque rappresentino una rappresentazione abbastanza standard di ciò che ci si aspetta dagli oggetti.
Incapsulamento, ereditarietà, polimorfismo, replicazione e astrazione. Ereditarietà, polimorfismo e astrazione sono tutti correlati tra loro e le classi in JSL supportano in modo piuttosto efficace incapsulamento, ereditarietà e replicazione, ma ci sono alcune sfumature nell'implementazione del comportamento polimorfico e dell'astrazione. Parlerò brevemente di questi cinque aspetti.
L'incapsulamento è piuttosto ben supportato. Quando si definisce una classe, si definiscono proprietà e metodi associati a quella classe. L'unica sfumatura qui è che nessuna di queste è vera. Non è possibile definire un membro veramente privato della classe. Tutto è pubblico. È come uno spazio dei nomi. C'è accesso globale ai membri e alle proprietà di una classe.
Anche l'ereditarietà è supportata. Se si crea una classe derivata da un'altra classe, la classe figlia erediterà i membri della classe padre. Esistono alcune sfumature relative al polimorfismo, che approfondiremo più avanti. Per implementare realmente il polimorfismo, è necessaria una soluzione alternativa specifica che consenta a una classe padre di inoltrare correttamente i metodi figli dall'interno dei propri metodi.
Replicazione: questo è fondamentalmente il punto centrale per cui si vorrebbero usare le classi. È possibile istanziare più copie di un oggetto, ognuna delle quali avrà il proprio set incapsulato di parametri e metodi, e non ho mai riscontrato particolari problemi in questo senso.
Quindi, l'astrazione: non esiste una nozione esplicita di astrazione nelle classi JSL, ma è possibile definire una classe padre con alcuni metodi predefiniti che potrebbero restituire un errore o semplicemente eseguire un comportamento di base. Se si ha familiarità con C# o altri linguaggi, questo potrebbe essere più vicino a un metodo virtuale che a un metodo astratto. Questa è una panoramica di alto livello di come le classi in JSL potrebbero essere diverse dalle classi con cui si ha familiarità se si proviene da altri linguaggi. Dopo questa rapida e frettolosa panoramica delle classi in JSL, passerò all'argomento principale.
Abbiamo aperto la nostra istanza JMP. Ho un paio di esempi piuttosto semplici. Beh, uno semplice con cui voglio iniziare, poi uno leggermente più complicato, e poi uno molto, molto più complicato. Il primo di cui voglio parlare è una barra di avanzamento molto semplice.
Queste demo sono tutte incentrate sul rendere lo sviluppo di interfacce grafiche utente (GUI) più estensibile e modulare, permettendo di sfruttare appieno le potenzialità degli oggetti. In questa barra di avanzamento, la eseguirò velocemente, così potrete vedere cosa fa. L'implementazione di base di una barra di avanzamento l'ho trovata online da qualche parte in un post; qualcuno me l'ha chiesto. Vi mostrerò i dettagli di come è realizzata la barra di avanzamento, ma non sono gli aspetti più importanti ai fini di questo articolo.
Questo codice essenzialmente imposta alcune cose per definire l'aspetto della barra di avanzamento. Sta creando una finestra. Sta creando... Come puoi vedere, stiamo creando gli oggetti qui, e tra un minuto entreremo nel codice per gli oggetti. Poi sta semplicemente disponendo gli oggetti in una finestra, creando qualcosa di casuale che puoi... Per rappresentare l'attività che stai eseguendo. Quindi sta semplicemente iterando come se stesse eseguendo un'elaborazione e sta aggiornando le barre di avanzamento utilizzando questi metodi, che fanno parte della definizione della classe stessa.
Non c'è una svolta qui, ma questo oggetto incapsula tutto il comportamento che crea questa visualizzazione. Lasciatemi aprire quella finestra. Ora siamo nel codice. Andrò alla definizione della classe della barra di avanzamento e riduciamola rapidamente. Potete vedere che si definisce una classe usando la chiamata define class, le si assegna un nome, ed è così che la si istanzia, e poi definire le variabili in una classe è semplice come... Le si definisce nello stesso modo in cui si definirebbe un'altra variabile.
Ora questo valore è assegnato all'istanza della classe, quindi è possibile accedervi eseguendo il riferimento alla classe e poi quel nome. Ora, i metodi nelle classi sono definiti nello stesso modo in cui si definisce una funzione. Sono essenzialmente funzioni con un nome diverso e vengono assegnate a quell'oggetto di classe. Quello che ho detto prima riguardo alle classi come a uno spazio dei nomi, è che è possibile manipolare... Dopo aver creato un'istanza di una classe, è possibile manipolare tutte queste cose. È possibile aggiungere proprietà che verranno assegnate all'oggetto creato. Con le classi JMP è possibile fare ogni tipo di cosa molto dinamica.
Non entrerò troppo nei dettagli. Voglio solo concentrarmi su come viene creata questa barra di avanzamento. La funzione initialize verrà eseguita. Permettetemi di mostrarla in una vista laterale. In questo esempio, la funzione initialize verrà eseguita quando si crea l'oggetto. La chiamata al nuovo oggetto eseguirà automaticamente _init.
Quando inizializziamo, abbiamo una configurazione predefinita, ma puoi anche fornirla con qualsiasi configurazione desideri. Questo imposta solo tutte le cose di base e poi, alla fine, chiama il comando build. Il metodo build è definito qui sotto. Nel metodo build, è lì che creiamo tutti gli oggetti che contribuiscono a dare alla barra di avanzamento l'aspetto che ha. Abbiamo il nostro contenitore, poi ci sono alcuni box distanziatori, e i box distanziatori sono ciò che si ingrandisce e si restringe per creare l'effetto di avanzamento. Ancora una volta, questo proviene da... non ho il codice sorgente a portata di mano, ma c'era un post della community JMP a riguardo.
Questo imposta tutti i dettagli su come apparirà la barra di avanzamento. Poi puoi vedere che abbiamo alcuni metodi di supporto che cambieranno l'altezza o la larghezza, e puoi chiamarli tutti. Poi c'è anche il... beh, come si interagisce con essi e si modifica la quantità di avanzamento? Questi sono solo metodi. Hanno funzionalità diverse, quindi set fornirà... imposterà la percentuale esplicitamente a un valore. Step la farà avanzare di un certo importo, e poi puoi definire qualsiasi altra cosa tu voglia.
Potresti indicare esattamente quanti passaggi ci sono e poi semplicemente avanzare ogni volta che esegui un'iterazione. Potresti manipolare i metodi qui a tuo piacimento per adattarli al compito specifico. Di nuovo, se torniamo all'esempio ora che abbiamo il contesto, è piuttosto semplice. Stiamo solo creando l'oggetto, e poi... Quando impostiamo la nuova finestra qui, per sfruttare l'oggetto, dobbiamo essere in grado di estrarne l'elemento di visualizzazione vero e proprio.
Nella barra di avanzamento, il contenitore di alto livello è questa casella di riepilogo H. Ma se otteniamo l'oggetto, tutto ciò che facciamo è restituire quel valore o restituire quel contenitore. Nella definizione della nuova finestra, se vogliamo inserire la barra di avanzamento in una posizione particolare, possiamo semplicemente prendere l'oggetto o prendere l'oggetto che abbiamo creato e ottenere... Questo potrebbe essere chiarito come qualcosa come "get panel". Che avrebbe più senso, ma eccoci qui.
Comunque, questo è il modo in cui lo si inserisce, e poi qui, stiamo ancora facendo riferimento al riferimento all'oggetto, e lo stiamo esaminando passo passo per la barra primaria, e lo stiamo impostando a un valore esplicito per la barra secondaria. Stiamo aggiornando il testo qui. Ancora una volta, così possiamo vedere cosa sta facendo. Di nuovo, stiamo solo inserendo dei valori casuali, aggiornandolo, e puoi immaginare come potresti inserirlo in qualche altra attività.
Un'altra cosa che vorrei sottolineare e che ho trovato utile è che puoi chiudere la barra di avanzamento. Se lo fai, la prossima volta che proverà a scorrere, fallirà perché non avrà più il riferimento alle finestre. L'hai chiusa, e ora, quando esegui questo aggiornamento per provare ad aggiornare la finestra, non ci riesce. Quindi restituisce un errore.
Poiché tutto questo è racchiuso in un'istruzione try, che fallirà, verrà spostata qui, quindi questo è un modo rapido e semplice per interrompere l'elaborazione se è necessario, cosa che mi è servita in diverse occasioni. Questa è la barra di avanzamento. Un'implementazione piuttosto elementare. Ci sono molti modi per personalizzarla. Si possono estrarre informazioni, si può fare qualsiasi cosa.
La prossima cosa di cui voglio parlare è un po' più complicata e probabilmente richiede un po' di preparazione. Voglio visualizzare queste tabelle qui. Ho preso la tabella campione della classe grande e ho elaborato alcuni dati randomizzati perché voglio dimostrare la funzionalità di join virtuale. Per chi non ha familiarità con i join virtuali, c'è il join standard da cui potremmo prendere... Abbiamo questa classe di punteggi grandi. Ha più voci per ogni studente. Hanno tentato 10 volte di fare qualcosa e hanno ottenuto un punteggio. Contiene solo il loro nome, il tentativo e il punteggio, ma abbiamo tutte queste altre informazioni qui. Vogliamo inserirle qui. Eseguiamo un join. Sono sicuro che chiunque usi JMP l'abbia già fatto.
Questo porterà questi valori in questa tabella, ma c'è un altro modo per farlo. Tutti questi nomi sono univoci. Possiamo impostarli come ID collegamento, e poi qui in questa tabella, possiamo prenderlo e fare riferimento a questa tabella qui. Ora abbiamo un join virtuale, possiamo vedere tutte queste informazioni. Se torniamo qui, posso nascondere o mostrare, e ora abbiamo le informazioni di questa colonna di questa tabella accessibili in questa tabella senza dover effettuare un join. Potremmo fare la stessa cosa anche con questa tabella.
Ci sono alcuni vantaggi interessanti. Se andiamo nelle informazioni sulla colonna e nella proprietà di riferimento del collegamento, possiamo impostarla in modo che accetti lo stato "selezionato" o "escluso". Se applico questa impostazione e vado qui, quando seleziono questa riga, verrà selezionata una qualsiasi delle righe correlate. Lo stesso accade anche se si crea un riepilogo e si crea un collegamento alla tabella dati o alla vista dati: lì si verificano le stesse cose.
Questa è una funzionalità davvero interessante. Ho iniziato a usarla l'anno scorso e ho subito pensato di automatizzarla nello scripting JSL, finendo per scrivere un gestore di link in una certa misura. Lasciatemi cancellare queste informazioni. Cancelleremo l'ID del link. Cancelleremo il riferimento al link. Ora sono solo normali tabelle non linkate.
Quello che farò è aprire il gestore dei link di esempio qui. Eseguirò alcune operazioni. Imposteremo tutte le tabelle e inizializzeremo il gestore dei link. Tra un minuto spiegherò il codice alla base di tutto questo.
Ecco fatto. Non so quale fosse l'errore, ma ecco la finestra di base per il gestore dei link. Abbiamo l'interfaccia grafica, ma non è stato ancora creato alcun collegamento. L'API che possiamo usare è quella che ci permette di impostare una sorgente di collegamento, che in pratica equivale a quello che ho fatto prima qui, dove ho impostato l'ID del collegamento. Se lo eseguo, possiamo vedere che il gestore dei link è stato aggiornato.
Qui otteniamo questa nuova voce GUI. Ora possiamo collegare una delle tabelle. Otteniamo una nuova voce. Possiamo collegare la seconda tabella. Otteniamo una seconda voce e ora abbiamo i mezzi per accedere e modificare queste configurazioni di collegamento.
Se voglio avere questa distribuzione, quindi se voglio la seconda tabella, posso impostarla in modo che le selezioni e le esclusioni siano tutte... In pratica, se seleziono qualcosa qui, posso propagarla qui. Abbiamo selezionato qualsiasi cosa. Ora, tutti questi dovrebbero essere selezionati e, inoltre, nell'originale, dovrebbe essere selezionata anche la tabella sorgente.
Normalmente, se si volessero aggiornare queste cose, bisognerebbe entrare, cliccare con il tasto destro, selezionare "Informazioni colonna", andare al riferimento del collegamento, andare a "Distribuisci" o "Accetta", modificare quello che si desidera e poi "Applica". Ok, andare all'altro e procedere. Volevo qualcosa di un po' più dinamico, qualcosa in cui si potesse vedere cosa sta succedendo. Si potrebbe avere un'altra tabella, si potrebbero impostare più sorgenti per fare tutto questo, e tutte le configurazioni sono proprio qui, quindi si possono disattivare le cose, rimuovere il collegamento, tutto questo.
Ho trovato questo strumento particolarmente utile quando si hanno a che fare con set di dati più grandi distribuiti su più tabelle, come ad esempio un'unica grande tabella riassuntiva, e si è eseguita un'elaborazione dati su dati continui, e si desidera poter collegare i dati, ma non si vuole creare una tabella gigantesca perché potrebbe diventare un po' macchinosa. Questo è nato da tutto questo.
Ora, se vogliamo entrare nel codice, sarà un po' più complesso, e una cosa su cui voglio essere molto chiaro è che niente di tutto questo è ottimizzato. Molto di questo è ancora... Potremmo chiamarla la prima bozza di implementazione. L'ho fatta funzionare e non sono tornato indietro a sistemare le cose. Tenetelo a mente mentre sto esaminando alcune di queste cose.
Qui potete vedere la funzione initialize che ho chiamato, che controlla solo se c'è un oggetto, altrimenti ne crea uno. Uno degli aspetti che probabilmente potrebbero essere migliorati è che inizialmente ho pensato di voler attivare più gestori di link contemporaneamente se ho più progetti attivi e forse è utile, forse no, ma questa classe qui è essenzialmente implementata come singleton al momento.
Se lo si desidera, si potrebbero creare più istanze, ma questa funzionalità sarebbe un po' approssimativa per alcuni dettagli che spiegherò tra un minuto. In sostanza, il gestore dei link si limita a gestire alcuni array che definiscono come le tabelle sono collegate tra loro, a quale tabella è collegata e qual è lo stato attuale del collegamento. Dispone di metodi per modificare i collegamenti e fare tutte quelle cose divertenti.
Per l'interfaccia grafica, abbiamo implementato due classi. C'è questa configurazione dei link. Ecco, se riesco ad aprire... Lasciatemi aprire la finestra del gestore dei link. Dov'è? Proprio qui.
Se osserviamo la finestra del gestore dei link, la configurazione dei link è questa casella che consente di modificare l'opzione di accettazione, selezionare l'opzione di esclusione, che è la configurazione, e poi la scheda, la scheda della sorgente del link è una di queste. Una sorgente ha una scheda, perché ci sono alcune regole su quali colonne possono essere collegate, cosa può essere collegato ad essa, e varie cose del genere. Quando si crea una sorgente del link, il gestore dei link crea uno di questi oggetti.
Ci sono un sacco di altre cose che vengono eseguite per controllare le cose e impostare eventi per sapere quando le tabelle si chiudono e quant'altro, e puoi ignorare tutto questo. Questo è il nocciolo della questione. Crea un nuovo oggetto scheda sorgente link e fornisce la tabella.
Questo è più o meno tutto. Viene aggiunto alla GUI e credo che sia da qualche parte qui sotto. Qui c'è l'elenco delle sorgenti, e quindi questo viene aggiunto a quello, che è solo un contenitore, e potete vedere dove abbiamo questa funzione per ottenere la scheda che è l'oggetto di visualizzazione vero e proprio. In pratica, questo gestisce gran parte di quella replicazione. Ogni volta che creiamo una nuova sorgente, ne creiamo una nuova e otteniamo automaticamente tutte le funzionalità.
Se andiamo nella scheda sorgente, anche questa avrà un'inizializzazione. Ha un proprio oggetto di visualizzazione, è una casella di pagina, ci sono alcune operazioni di allineamento in corso e anche qui c'è un oggetto elenco. Questo è ciò che contiene tutte le diverse configurazioni di collegamento che potresti avere. Di nuovo, ricorda che probabilmente questo è un po' poco ottimizzato, ma puoi iniziare a vedere come è un po' più... È potenzialmente più facile sviluppare un comportamento dinamico per un'interfaccia in JMP. L'ho usato in molti altri posti.
Alcuni di voi avranno notato che ho qui questa libreria di richiamo, a cui ho accennato poco fa quando ho iniziato a parlare di un'implementazione molto più complessa. Non credo di avere molto tempo per approfondire l'argomento. Vorrei averlo avuto, perché questo è stato in realtà il primo progetto in cui ho iniziato a usare le classi JMP. Purtroppo, anche questo è ancora in una fase di prima bozza, si potrebbe dire. Ci sono molte cose che potrebbero essere consolidate molto facilmente.
A questo punto, spero solo di averti fatto riflettere su come le classi in JMP possano essere sfruttate per fornire un comportamento più dinamico e rendere il codice un po' più pulito. Perché so che quando provavo a... ho visto del codice di interfaccia utente complesso e dinamico in cui tutto si integra direttamente nella funzione "nuova finestra".
Proprio nella nuova finestra, ci si ritrova con 20 schede di tutte queste funzioni e pulsanti che fanno questo e quello, e questo e quello, quando ci sono modi molto più puliti e basati su oggetti per implementare lo stesso comportamento. È più gestibile, scalabile, più facile da leggere. Penso che lavorare con le classi offra molti vantaggi quando si tratta di implementare interfacce utente.
Non entrerò nei dettagli del recall. L'unica cosa che aggiungerò è che lavorerò per ripulire tutto questo codice di esempio e la libreria di recall e renderli disponibili in un repository Git pubblico, dove chiunque sia interessato potrà dare un'occhiata, clonare il repository, fare fork, farne ciò che vuole e cercare di capire come sfruttare le classi JMP nel proprio codice in modo da aumentarne l'efficienza.
Penso che l'unica cosa rimasta sarebbe quella di evidenziare rapidamente alcuni degli aspetti iniziali di cui ho parlato con lo scoping. Abbiamo esaminato gli esempi, avete visto come iniziare a costruire strutture per implementare queste GUI dinamiche, ma ora dobbiamo capire quali sono i problemi e come aggirarli. Penso che un buon esempio potrebbe essere... Vediamo se riusciamo a farlo qui.
Se scelgo una di queste, il problema di ambito di cui ho parlato prima riguarda il modo in cui si chiamano i metodi e l'ambito che ereditano. Ho riscontrato diversi problemi nel tentativo di lavorare nello spazio dei nomi Here. Ho notato collisioni tra lo spazio dei nomi globale e i metodi e, essenzialmente per semplificare il più possibile, il modo infallibile che ho identificato per aggirare qualsiasi problema di ambito che si potrebbe incontrare con una classe, e non lo sto dimostrando qui, ma questo è un buon esempio per mostrarvi come potreste volerlo fare, è semplicemente usare un modificatore locale. Io preferisco formattare il mio in questo modo.
L'unica variabile qui che deve essere definita esplicitamente è questa. In questo caso, si tratta semplicemente di una variabile che stiamo configurando per analizzare e creare la proprietà che creerà il riferimento. Quel codice è irrilevante. Questa variabile potrebbe nascondere una variabile in un altro ambito, semplicemente perché non è stata definita come locale, e a seconda di dove questo metodo viene chiamato nel codice, questa variabile potrebbe generare un conflitto.
Questo è davvero il modo più pulito che ho trovato per cercare di evitare conflitti di ambito con le classi: essere molto espliciti riguardo alle variabili locali. Presumo di dover ripetere questa procedura praticamente per tutti questi metodi, perché la maggior parte di questo articolo è stato scritto prima che capissi quale fosse il metodo migliore per aggirare alcuni di questi problemi. L'unica altra cosa che aiuta a aggirare i problemi è usare l'identificazione esplicita dello spazio dei nomi.
Ogni volta che scrivo una libreria, in pratica la scrivo in un nuovo namespace personalizzato e cerco di definire anche tutte le mie variabili e metodi o variabili e funzioni come parte di tale namespace, e questo mi ha aiutato a evitare conflitti.
Poi, credo che, in realtà, l'altro argomento di cui parlare, e questo non è un esempio molto calzante, sarebbero alcuni dei problemi con il polimorfismo e l'ereditarietà. Vediamo. Un buon posto per parlarne potrebbe essere la libreria di richiamo.
Speriamo che non sia troppo... Possiamo evitare alcuni dettagli dell'implementazione e concentrarci solo sui problemi. In base a quanto visto finora, la panoramica generale è che questo pannello di richiamo è essenzialmente un contenitore che è possibile impostare per richiamare automaticamente i valori dei campi. Ora, è destinato a essere ereditato, quindi è destinato ad avere classi figlie che devono chiamare alcuni dei comportamenti. È specificamente questo metodo qui. La classe figlia dovrebbe implementare la propria versione del metodo build.
Ora, è qui che entra in gioco la sfumatura del polimorfismo, perché se si prova a chiamare questo metodo da uno di questi altri metodi, quindi ci troviamo nella classe padre e si vuole chiamare build e c'è un metodo figlio che sovrascrive build, build non verrà correttamente inoltrato al metodo figlio. Non ho una spiegazione del perché penso che ci debba essere una certa sfumatura su come le classi vengono implementate nel back-end. Ma c'è un modo per aggirare il problema. Credo che sia meglio dimostrarlo in una libreria diversa, ma ahimè, non sono pronto ad approfondire l'argomento.
Penso che eviterò di entrare nei dettagli e cercherò solo di dire che dovresti concludere dicendo che il polimorfismo, ovvero il fatto che puoi implementare una classe figlia che sovrascriverà il comportamento, potrebbe non funzionare come previsto, il che è un po' spiacevole perché penso che l'ereditarietà sia uno degli aspetti più utili delle classi.
Sebbene sia possibile ereditare i comportamenti, è un po' più complicato chiamare i comportamenti ereditati nella classe padre. Credo che lo formulerò in questo modo. Quando pubblicherò questi materiali online, cercherò di includere esempi più specifici su come e perché questo non funziona. Ripeto, sto solo cercando di fornire una panoramica di come iniziare a incorporare le classi nei progetti di interfaccia utente in Junk JSL.
Detto questo, eviterò di dilungarmi troppo. Ripeto, mi chiamo Ben Barrett e vi invito a contattarmi se avete domande o volete vedere altre implementazioni di questo tipo. A parte questo, grazie mille.